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. SeaweedFS is made of a few parts that work together: - **The master** tracks where data lives and assigns storage to writes. - **Volume servers** hold the actual bytes on disk. - **The filer** provides a directory tree and file metadata on top of the volumes. - **The S3 gateway** exposes an S3 API in front of the filer, so clients talk plain S3. SeaweedFS decides durability by how many copies it keeps and where it puts them. Replication is set per write, encoded as three digits for copies across data centers, racks, and servers. A value of `001` keeps a second copy on a different server in the same rack; `000` keeps a single copy. More copies cost more disk and survive more failures. Set the default replication on the master and match it to how much loss the data can take. ## 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 [Use S3-compatible storage from workloads](/docs/hetzner/apalla/storage/object/use-s3-from-workloads). 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 [TopoLVM](/docs/hetzner/apalla/storage/local/local-nvme-with-topolvm) 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](https://github.com/seaweedfs/seaweedfs/tree/master/k8s/charts/seaweedfs) before you install. ```console $ 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: ```console $ 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. ```console $ kubectl -n seaweedfs port-forward svc/seaweedfs-master 9333:9333 ``` Then open [http://localhost:9333](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: ```console $ 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: ```yaml title="s3-credentials.yaml" apiVersion: v1 kind: Secret metadata: name: s3-credentials namespace: default type: Opaque stringData: AWS_ACCESS_KEY_ID: AWS_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:///`), not in the hostname. Set `forcePathStyle` (or `s3ForcePathStyle`) to `true` in your client. ## Related - [Use S3-compatible storage from workloads](/docs/hetzner/apalla/storage/object/use-s3-from-workloads) - [ReadWriteMany with JuiceFS](/docs/hetzner/apalla/storage/shared/readwritemany-with-juicefs) - [Back up with Velero](/docs/hetzner/apalla/storage/backup/back-up-with-velero)