Skip to main content

Run SeaweedFS for S3-compatible object storage

Inspect 1.36

SeaweedFS runs an S3-compatible object store inside your own cluster. You get buckets and an S3 endpoint on storage you operate, so your uploads, your Velero backups, and the data behind JuiceFS all sit on disks you control rather than on someone else's service. On Syself Autopilot that matters twice over: the data stays on your Hetzner hardware, and backing it with block volumes keeps it off the nodes, where node replacement never touches it.

SeaweedFS has a few moving parts, and how you set replication decides how much failure the data survives.

When self-hosted S3 is the right call

Run your own object store for sovereignty, cost, or egress. The data stays on hardware you own, which answers data-residency questions directly. There are no per-request or per-gigabyte charges from an outside provider, only the cost of the disks. And traffic between your workloads and your buckets stays inside the cluster network, so you do not pay to move data out and back.

SeaweedFS is yours to run: it is not part of the Cluster Stack, so its health, upgrades, and data are your responsibility rather than part of the managed platform. If you only need a bucket and do not want to operate the store, point your workloads at any S3-compatible endpoint instead. See . If you would rather not operate SeaweedFS, the Syself Consulting team can deploy and maintain it for you, or advise on sizing and replication.

Deploy SeaweedFS

Install the master, volume servers, filer, and S3 gateway from the SeaweedFS Helm chart. The volume servers need real disks, so the same command gives them a StorageClass. Two choices fit Syself Autopilot:

  • Hetzner Cloud block volumes through the standard class. The data detaches and reattaches with the volume server, so it survives node replacement.
  • Local disks through on a bare metal server. Faster, but the data is tied to that server.
Note

Chart value keys change between versions. Confirm them against the SeaweedFS chart before you install.

		$ helm repo add seaweedfs https://seaweedfs.github.io/seaweedfs/helm
$ helm repo update
$ helm install seaweedfs seaweedfs/seaweedfs \
    --namespace seaweedfs --create-namespace \
    --set master.replicas=1 \
    --set volume.replicas=1 \
    --set filer.replicas=1 \
    --set s3.enabled=true \
    --set s3.enableAuth=true \
    --set master.data.type=persistentVolumeClaim \
    --set master.data.storageClass=standard \
    --set master.data.size=10Gi \
    --set master.logs.type="" \
    --set filer.data.type=persistentVolumeClaim \
    --set filer.data.storageClass=standard \
    --set filer.data.size=10Gi \
    --set filer.logs.type="" \
    --set s3.logs.type="" \
    --set 'volume.dataDirs[0].name=data' \
    --set 'volume.dataDirs[0].type=persistentVolumeClaim' \
    --set 'volume.dataDirs[0].storageClass=standard' \
    --set 'volume.dataDirs[0].size=10Gi' \
    --set 'volume.dataDirs[0].maxVolumes=0'
	

Set each storageClass to the class you chose, and size the volumes for the data you expect. These values run one of each component and keep a single copy of the data, so adjust the replica counts and replication to your own availability requirements.

The empty logs.type values send logs to stdout, where kubectl logs and any log collector can read them. The chart's default writes log files to the node, which the read-only Syself node images do not allow.

Warning

Without s3.enableAuth=true the gateway accepts unauthenticated requests, so any pod in the cluster can read and write every bucket.

Create buckets and keys

Create a bucket with weed shell, SeaweedFS's own admin shell, which runs inside the master pod:

		$ echo "s3.bucket.create -name backups" | kubectl exec -i -n seaweedfs sts/seaweedfs-master -- weed shell
	

The master also serves a status page showing cluster status, volumes, and topology. Port-forward it to open the page in a browser.

		$ kubectl -n seaweedfs port-forward svc/seaweedfs-master 9333:9333
	

Then open http://localhost:9333.

The install created a Secret named seaweedfs-s3-secret in the seaweedfs namespace, holding two identities: an admin key that reads and writes, and a read-only key. Read the admin key ID and secret with:

		$ kubectl get secret seaweedfs-s3-secret -n seaweedfs \
    -o jsonpath='{.data.admin_access_key_id}' | base64 -d; echo
$ kubectl get secret seaweedfs-s3-secret -n seaweedfs \
    -o jsonpath='{.data.admin_secret_access_key}' | base64 -d; echo
	

Give each workload the narrowest key it needs. A workload that only reads backups should get the read-only key, not the admin one. Least-privilege keys limit the blast radius if one leaks.

Secrets are namespaced, so copy the key a workload needs into a Secret in that workload's namespace, along with the endpoint:

s3-credentials.yamlyaml
		apiVersion: v1
kind: Secret
metadata:
  name: s3-credentials
  namespace: default
type: Opaque
stringData:
  AWS_ACCESS_KEY_ID: <access-key-id>
  AWS_SECRET_ACCESS_KEY: <secret-access-key>
  S3_ENDPOINT: http://seaweedfs-s3.seaweedfs.svc.cluster.local:8333
	

The gateway uses path-style addressing, so the bucket name goes in the path (http://<endpoint>/<bucket>), not in the hostname. Set forcePathStyle (or s3ForcePathStyle) to true in your client.