Six compose files for the separate-container setup, a build and a prod file for each, modelled on DriverVault's Docker folder: - pocketbase.s3: media in an existing bucket elsewhere; the endpoint and key pair are required, host.docker.internal is mapped, and the homeserver waits up to 30 seconds for the endpoint before starting. - pocketbase.seaweedfs: SeaweedFS as one `weed server -s3` container whose only identity is the homeserver's key pair, plus a one-shot init that creates the bucket. - pocketbase.seaweedfs.split: master, volume, filer, S3 gateway and admin UI as separate containers; the init seeds the bucket and the homeserver's identity into the filer's IAM store, and the gateway waits for it. Only the S3 gateway and admin UI are published, on loopback. Both SeaweedFS files share one project and data layout, so switching needs no migration. .env.example gains the SEAWEED_* settings and leaves WARP_PB_S3_FORCE_PATH_STYLE empty so the S3 file's default applies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
240 lines
9.0 KiB
YAML
240 lines
9.0 KiB
YAML
# Warp on PocketBase with media in SeaweedFS split into its roles: the
|
|
# homeserver, the admin panel and the web client built from this checkout,
|
|
# plus SeaweedFS's master, volume, filer, S3 gateway and admin UI as
|
|
# separate containers.
|
|
#
|
|
# cd Docker
|
|
# cp .env.example .env # then edit it; set WARP_PB_S3_ACCESS_KEY,
|
|
# # WARP_PB_S3_SECRET and SEAWEED_ADMIN_PASSWORD
|
|
# docker compose -f docker-compose.pocketbase.seaweedfs.split.yml up -d --build
|
|
#
|
|
# The database stays in /data/pb_data (warp-data); media goes to the bucket
|
|
# WARP_PB_S3_BUCKET (default warp) in SeaweedFS (seaweed-data). Over
|
|
# docker-compose.pocketbase.seaweedfs.yml this buys SeaweedFS's admin UI,
|
|
# where S3 identities are created and revoked under Object Store → Users,
|
|
# per-role restarts, upgrades and metrics, and room for a second volume
|
|
# server. The S3 behaviour is the same. Media stored before S3 was on is not
|
|
# moved into the bucket.
|
|
#
|
|
# Homeserver http://localhost:8008, admin panel http://localhost:8009, web
|
|
# client http://localhost:8080. The S3 gateway (8333) and the SeaweedFS admin
|
|
# UI (23646) are published on loopback; SEAWEED_S3_BIND and
|
|
# SEAWEED_ADMIN_BIND=0.0.0.0 expose them, the admin UI only behind a proxy.
|
|
# Master, volume and filer are not published: the volume server serves file
|
|
# content by id with no authentication at all.
|
|
#
|
|
# Master, volume and filer share seaweed-data in the layout `weed server
|
|
# -dir` writes, and this file shares its project with
|
|
# docker-compose.pocketbase.seaweedfs.yml, so switching between the two
|
|
# needs no migration.
|
|
#
|
|
# Create the first administrator once the server is up:
|
|
#
|
|
# docker compose -f docker-compose.pocketbase.seaweedfs.split.yml exec warp warp create-account --username admin --admin
|
|
|
|
name: warp-pocketbase-seaweedfs
|
|
|
|
services:
|
|
# Volume and topology metadata; hands out file ids. -ip is the name the
|
|
# other roles reach it by.
|
|
seaweedfs-master:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: unless-stopped
|
|
command: >
|
|
master -ip=seaweedfs-master -ip.bind=0.0.0.0 -mdir=/data
|
|
-volumeSizeLimitMB=1024 -metricsPort=9324
|
|
volumes:
|
|
- seaweed-data:/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9333/cluster/status >/dev/null || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
# Where the bytes land. -max=0 sizes itself from free disk instead of the
|
|
# default cap of 8 volumes.
|
|
seaweedfs-volume:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: unless-stopped
|
|
command: >
|
|
volume -master=seaweedfs-master:9333 -ip=seaweedfs-volume -ip.bind=0.0.0.0
|
|
-port=8080 -dir=/data -max=0 -metricsPort=9325
|
|
depends_on:
|
|
seaweedfs-master:
|
|
condition: service_healthy
|
|
volumes:
|
|
- seaweed-data:/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8080/healthz >/dev/null || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
# The directory tree over the volume store (buckets, object keys) and the
|
|
# IAM store the S3 identities live in, kept on the data volume.
|
|
seaweedfs-filer:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: unless-stopped
|
|
command: >
|
|
filer -master=seaweedfs-master:9333 -ip=seaweedfs-filer -ip.bind=0.0.0.0
|
|
-port=8888 -defaultStoreDir=/data -metricsPort=9326
|
|
depends_on:
|
|
seaweedfs-volume:
|
|
condition: service_healthy
|
|
volumes:
|
|
- seaweed-data:/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8888/healthz >/dev/null || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
# Runs once per start and exits: creates the bucket (the homeserver never
|
|
# does) and writes the homeserver's identity into the filer's IAM store,
|
|
# both in place, so a rotated WARP_PB_S3_SECRET lands on the next start.
|
|
# That store is the one the admin UI writes, so the gateway gets no AWS_*
|
|
# variables or -config: those lower-priority sources would be displaced by
|
|
# the first identity added in the admin UI. The closing grep fails the
|
|
# start if the identity is missing, rather than letting the gateway come
|
|
# up open to anyone.
|
|
seaweedfs-init:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: "no"
|
|
depends_on:
|
|
seaweedfs-filer:
|
|
condition: service_healthy
|
|
environment:
|
|
# Expanded by the shell inside the container, so the secret stays off
|
|
# the command line.
|
|
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp}
|
|
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env}
|
|
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env}
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- |
|
|
set -e
|
|
printf '%s\n' \
|
|
"s3.bucket.create -name $$WARP_PB_S3_BUCKET" \
|
|
"s3.configure -user warp -access_key $$WARP_PB_S3_ACCESS_KEY -secret_key $$WARP_PB_S3_SECRET -actions Admin -apply" \
|
|
| weed shell -master=seaweedfs-master:9333 -filer=seaweedfs-filer:8888 >/dev/null
|
|
echo "s3.configure" \
|
|
| weed shell -master=seaweedfs-master:9333 -filer=seaweedfs-filer:8888 \
|
|
| grep -q "$$WARP_PB_S3_ACCESS_KEY"
|
|
|
|
# The homeserver's endpoint. Credentials come from the filer's IAM store.
|
|
seaweedfs-s3:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: unless-stopped
|
|
command: s3 -filer=seaweedfs-filer:8888 -ip.bind=0.0.0.0 -port=8333 -metricsPort=9327
|
|
depends_on:
|
|
seaweedfs-filer:
|
|
condition: service_healthy
|
|
seaweedfs-init:
|
|
condition: service_completed_successfully
|
|
ports:
|
|
# For aws-cli and the like; the homeserver uses the compose network.
|
|
- "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8333/healthz >/dev/null || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
# Cluster view, buckets, maintenance, and Object Store → Users. It can mint
|
|
# credentials for the bucket, and weed serves it with no authentication
|
|
# when the password is empty, so the password is required. Read from
|
|
# WEED_ADMIN_* to keep it off the command line.
|
|
seaweedfs-admin:
|
|
image: ${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}
|
|
restart: unless-stopped
|
|
command: admin -port=23646 -master=seaweedfs-master:9333 -dataDir=/data -metricsPort=9328
|
|
depends_on:
|
|
seaweedfs-master:
|
|
condition: service_healthy
|
|
environment:
|
|
WEED_ADMIN_USER: ${SEAWEED_ADMIN_USER:-admin}
|
|
WEED_ADMIN_PASSWORD: ${SEAWEED_ADMIN_PASSWORD:?set SEAWEED_ADMIN_PASSWORD in .env}
|
|
WEED_ADMIN_READONLY_USER: ${SEAWEED_ADMIN_READONLY_USER:-}
|
|
WEED_ADMIN_READONLY_PASSWORD: ${SEAWEED_ADMIN_READONLY_PASSWORD:-}
|
|
volumes:
|
|
- seaweed-admin:/data
|
|
ports:
|
|
- "${SEAWEED_ADMIN_BIND:-127.0.0.1}:${SEAWEED_ADMIN_PORT:-23646}:23646"
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:23646/health >/dev/null || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 15s
|
|
|
|
warp:
|
|
build:
|
|
context: ..
|
|
dockerfile: Dockerfile
|
|
image: warp:latest
|
|
restart: unless-stopped
|
|
depends_on:
|
|
seaweedfs-s3:
|
|
condition: service_healthy
|
|
# The signing key is created on first start and kept in the volume:
|
|
# losing it means other servers no longer recognise this one.
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
command:
|
|
- test -f /data/signing.key || warp generate-keys --output /data/signing.key; exec warp serve
|
|
env_file: .env
|
|
environment:
|
|
WARP_DB_DRIVER: pocketbase
|
|
WARP_DB_CONNECTION_STRING: /data/pb_data
|
|
# The gateway on the compose network; these override whatever .env
|
|
# says about S3.
|
|
WARP_PB_S3_ENABLED: "true"
|
|
WARP_PB_S3_ENDPOINT: http://seaweedfs-s3:8333
|
|
WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp}
|
|
WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-us-east-1}
|
|
WARP_PB_S3_FORCE_PATH_STYLE: "true"
|
|
WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env}
|
|
WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env}
|
|
volumes:
|
|
- warp-data:/data
|
|
ports:
|
|
- "${WARP_PORT:-8008}:8008"
|
|
|
|
admin:
|
|
build:
|
|
context: ..
|
|
dockerfile: apps/admin/Dockerfile
|
|
image: warp-admin:latest
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- warp
|
|
env_file: .env
|
|
environment:
|
|
WARP_ADMIN_HOMESERVER: http://warp:8008
|
|
ports:
|
|
- "${WARP_ADMIN_PORT:-8009}:8009"
|
|
|
|
web:
|
|
build:
|
|
context: ..
|
|
dockerfile: apps/web/Dockerfile
|
|
image: warp-web:latest
|
|
restart: unless-stopped
|
|
depends_on:
|
|
- warp
|
|
env_file: .env
|
|
environment:
|
|
WARP_WEB_HOMESERVER: http://warp:8008
|
|
ports:
|
|
- "${WARP_WEB_PORT:-8080}:8080"
|
|
|
|
volumes:
|
|
warp-data:
|
|
# Master, volume and filer share it, as `weed server -dir` lays it out.
|
|
seaweed-data:
|
|
# The admin UI's session key and maintenance state; no object data.
|
|
seaweed-admin:
|