8.8 KiB
SeaweedFS S3 Server (Docker Compose)
A small single-node SeaweedFS cluster with a hosted S3 API. It serves as the S3 backend (filestore and history).
All four services share one image (chrislusf/seaweedfs:latest); the root
compose.yaml merges them from the per-service files with extends:.
Layout
.
├── compose.yaml # root compose (merges the 4 services, depends_on order)
├── seaweedfs-master/compose.yaml # cluster metadata + data/ (persisted)
├── seaweedfs-volume/compose.yaml # data bricks + data/ (persisted)
├── seaweedfs-filer/compose.yaml # file namespace + data/ (persisted)
├── seaweedfs-s3/compose.yaml # S3 API gateway (no data dir)
├── .env / env.sample # S3 credentials (never commit .env)
├── 0_pull_other_containers.sh # setup: pull the shared image
├── 1_install_aws.sh # setup: awscli on the host
├── 2_create_s3_buckets.sh # setup: create the Overleaf buckets (idempotent)
├── 3_online_backup_s3.sh # backup: metadata snapshot + bucket sync
├── autoupdate_seaweedfs-*.sh # update: one service at a time, only if image is newer
├── up.sh / down.sh # whole stack up / down
└── up|down|logs_seaweedfs-*.sh # per-service shortcuts
Services, ports, network aliases
Start-up order is enforced with depends_on ... condition: service_healthy:
master → volume → filer → s3.
| Service | Network alias | Host ports (host → container) | Role | Healthcheck |
|---|---|---|---|---|
seaweedfs-master |
master |
9333 (master HTTP API), 19333 (master gRPC), 9327 (Prometheus metrics) |
cluster metadata | :9333/cluster/healthz |
seaweedfs-volume |
volume |
18080 → 8080 (volume gRPC; host 8080 is taken by another service) |
data bricks | :8080/status |
seaweedfs-filer |
filer |
8888 (filer HTTP), 18888 (filer gRPC) |
file namespace / directories | :8888/ |
seaweedfs-s3 |
s3 |
8333 (S3 API) |
S3 gateway for AWS-SDK clients | :8333/ answers HTTP |
Where the aliases resolve (common gotcha):
- The aliases (
s3,filer,master,volume) are registered on the compose network (default project network of this folder, i.e.s3_server_compose_default). - ✅ They resolve from any container attached to that network — that is
why
http://s3:8333works in the Overleaf config below. Check withdocker exec seaweedfs-filer ping -c1 s3(must reply from the stack IP). - ❌ They do not resolve from the host machine — Docker's embedded DNS
exists only inside the network. From the host, always use the published
ports, e.g.
http://localhost:8333(your2_/3_scripts already do this). - A container on a different network cannot reach the aliases at all; it
must either be attached to this network (
docker network connect s3_server_compose_default <container>) or usehttp://<host>:8333.
First-time setup
cp env.sample .env # then fill in keys (generation one-liners are in the file)
./0_pull_other_containers.sh
./1_install_aws.sh # installs awscli via apt on the host
./up.sh # = docker compose up -d (wait until all 4 are healthy)
./2_create_s3_buckets.sh # idempotent: creates the 4 Overleaf buckets if missing
Verify:
docker ps | grep seaweedfs # 4 × healthy
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8333/ # 403 = S3 API answers (expected)
The Overleaf buckets created by 2_create_s3_buckets.sh:
overleaf-user-files, overleaf-template-files, overleaf-project-blobs, overleaf-chunks
Overleaf integration
For the Overleaf container compose.yaml file (it is assume that the overleaf compose contains AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY):
#########################################
## Overleaf Filestore S3 Configuration ##
#########################################
OVERLEAF_FILESTORE_BACKEND: s3
OVERLEAF_FILESTORE_USER_FILES_BUCKET_NAME: overleaf-user-files
OVERLEAF_FILESTORE_TEMPLATE_FILES_BUCKET_NAME: overleaf-template-files
OVERLEAF_FILESTORE_S3_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
OVERLEAF_FILESTORE_S3_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
OVERLEAF_FILESTORE_S3_ENDPOINT: http://s3:8333
OVERLEAF_FILESTORE_S3_PATH_STYLE: "true"
OVERLEAF_FILESTORE_S3_REGION: "us-east-1"
#########################################
## Overleaf History S3 Configuration ##
#########################################
OVERLEAF_HISTORY_BACKEND: s3
OVERLEAF_HISTORY_PROJECT_BLOBS_BUCKET: overleaf-project-blobs
OVERLEAF_HISTORY_CHUNKS_BUCKET: overleaf-chunks
OVERLEAF_HISTORY_S3_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
OVERLEAF_HISTORY_S3_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
OVERLEAF_HISTORY_S3_ENDPOINT: http://s3:8333
OVERLEAF_HISTORY_S3_PATH_STYLE: "true"
OVERLEAF_HISTORY_S3_REGION: "us-east-1"
Notes:
http://s3:8333relies on thes3network alias — it only works while the Overleaf server container is attached to this stack's network (same compose network as theseaweedfs-*containers).- If the Overleaf server runs elsewhere (other host, other compose network),
replace the endpoint with
http://<reachable-host>:8333in bothOVERLEAF_FILESTORE_S3_ENDPOINTandOVERLEAF_HISTORY_S3_ENDPOINT. OVERLEAF_*_S3_PATH_STYLE: "true"is required (SeaweedFS is a virtual-host style-incompatible gateway), and the region value is cosmetic for a local gateway but must be set consistently.
Scripts
Setup (numbered)
| Script | What it does |
|---|---|
0_pull_other_containers.sh |
docker pull chrislusf/seaweedfs:latest — all four services share this one image, so a single pull covers the whole stack. |
1_install_aws.sh |
Installs awscli on the host via apt (needed by the bucket/backup scripts, which use aws s3 locally). |
2_create_s3_buckets.sh |
Loads .env, checks reachability of http://localhost:8333, then creates the four Overleaf buckets if they don't exist yet. Idempotent — safe to re-run. |
3_online_backup_s3.sh |
Online backup (no downtime): ① snapshots filer metadata into the running filer via weed shell (fs.meta.save, reached through the master:9333 alias), ② aws s3 syncs all four buckets into ./backup_seaweedfs_online/<TIMESTAMP>/<bucket>/. |
Updates
| Script | What it does |
|---|---|
autoupdate_seaweedfs-master.sh / …-volume.sh / …-filer.sh / …-s3.sh |
Pulls the latest image, compares the short image ID of the running container with the local one; if unchanged → no-op, if newer → docker compose down + up -d of that service only; if the container is missing → just starts it. Sources .env first. |
The autoupdate scripts
cd "$COMPOSE_DIRECTORY"before callingdocker compose, which now defaults to the current directory (${COMPOSE_DIRECTORY:-$(pwd)}). Just run them from this folder, or pointCOMPOSE_DIRECTORYsomewhere else to override.
To update the whole stack, run the four in this order: master → volume →
filer → s3.
Everyday
| Script | Equivalent |
|---|---|
up.sh / down.sh |
docker compose up -d / docker compose down |
up_seaweedfs-<svc>.sh |
docker compose up -d <svc> |
down_seaweedfs-<svc>.sh |
docker compose down <svc> |
logs_seaweedfs-<svc>.sh |
docker compose logs -f <svc> |
Data & operations
- Persistent data:
seaweedfs-master/data/,seaweedfs-volume/data/,seaweedfs-filer/data/(bind-mounted at/datain each container). The S3 gateway needs no data dir. Back up the volume data dir (the actual file bytes) — metadata + buckets are reconstructed/synced by the other pieces. - All services run
restart: unless-stopped; healthchecks per the table above gate the start-up order anddepends_on. - Metrics: Prometheus endpoint at
http://localhost:9327(master). - Security: the S3 gateway binds
0.0.0.0:8333— the keypair in.envis the only gate (plus a403on anonymous requests). Keep.envprivate (already git-ignored) and firewall8333if this host is reachable from the network.