# warp-aio-pocketbase-seaweedfs-split — the homeserver, the admin panel and # the web client on PocketBase, with SeaweedFS in the same image as the # media store, split into its roles: master, volume, filer, S3 gateway and # admin UI, one process each. Over Dockerfile.PocketBase.SeaweedFS that buys # per-role restart, per-role metrics ports and the admin UI, where S3 # identities are minted and revoked under Object Store → Users. # # The homeserver's identity lives in the filer's IAM store, seeded on every # boot, the same store the admin UI writes; so a key added there never # displaces the homeserver's. /seaweed/data has the layout `weed server -dir` # writes, so it moves between the two SeaweedFS images unchanged. # # Build from the repository root: # # docker build -t warp-aio-pocketbase-seaweedfs-split -f Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split . # docker run -p 8008:8008 -p 8009:8009 -p 8080:8080 \ # -p 127.0.0.1:8333:8333 -p 127.0.0.1:23646:23646 \ # -v warp-data:/data -v seaweed-data:/seaweed/data -v seaweed-admin:/seaweed/admin \ # -e WARP_SERVER_NAME=warp.example \ # -e WARP_PB_S3_ACCESS_KEY=warp -e WARP_PB_S3_SECRET=change-me \ # -e WEED_ADMIN_PASSWORD=change-me \ # warp-aio-pocketbase-seaweedfs-split # # WARP_PB_S3_ACCESS_KEY, WARP_PB_S3_SECRET and WEED_ADMIN_PASSWORD are # required; the container refuses to start an open gateway or admin UI. # # Ports inside the container (publish 8333 and 23646 on loopback at most; # master, volume and filer listen on loopback only): # # 8008 homeserver 8333 SeaweedFS S3 gateway # 8009 admin panel 8888 SeaweedFS filer # 8080 web client 9333 SeaweedFS master # 8081 SeaweedFS volume 23646 SeaweedFS admin UI # 9324-9328 SeaweedFS metrics: master, volume, filer, S3, admin # SeaweedFS release to bake in, pinned so a rebuild later brings up the same # one; --build-arg SEAWEED_VERSION=... to upgrade. Global, because a FROM uses # it. ARG SEAWEED_VERSION="4.45" FROM node:22-alpine AS web WORKDIR /src RUN corepack enable # Lockfile and manifests first, so the package store survives source changes. COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ COPY apps/ui/package.json ./apps/ui/ COPY apps/admin/package.json ./apps/admin/ COPY apps/web/package.json ./apps/web/ RUN pnpm install --frozen-lockfile --filter @warp/admin... --filter @warp/web... COPY apps/ui ./apps/ui COPY apps/admin ./apps/admin COPY apps/web ./apps/web RUN pnpm --filter @warp/admin build && pnpm --filter @warp/web build FROM golang:1.26-alpine AS build ARG VERSION=0.0.0-dev ARG COMMIT=unknown ARG BUILD_DATE=unknown ENV CGO_ENABLED=0 WORKDIR /src # The homeserver. Dependencies first, so the module cache survives source # changes. COPY server/go.mod server/go.sum ./server/ RUN cd server && go mod download COPY server ./server RUN cd server && go build -trimpath \ -ldflags "-s -w \ -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Version=${VERSION} \ -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Commit=${COMMIT} \ -X github.com/dariuszjaniak/warp/server/internal/buildinfo.BuildDate=${BUILD_DATE}" \ -o /out/warp ./cmd/warp # The admin panel and the web client, each with its frontend embedded. COPY apps/admin/go.mod ./apps/admin/ COPY apps/admin/cmd ./apps/admin/cmd COPY apps/admin/internal ./apps/admin/internal COPY --from=web /src/apps/admin/internal/panel/build/dist ./apps/admin/internal/panel/build/dist RUN cd apps/admin && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ -o /out/warp-admin ./cmd/warp-admin COPY apps/web/go.mod ./apps/web/ COPY apps/web/cmd ./apps/web/cmd COPY apps/web/host ./apps/web/host COPY apps/web/client/client.go ./apps/web/client/ COPY --from=web /src/apps/web/client/build/dist ./apps/web/client/build/dist RUN cd apps/web && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ -o /out/warp-web ./cmd/warp-web # The one static weed binary from the official image. FROM chrislusf/seaweedfs:${SEAWEED_VERSION} AS seaweed FROM alpine:3.21 # tini is PID 1 so signals reach the entrypoint and zombies are reaped. RUN apk add --no-cache ca-certificates tzdata curl tini && \ adduser -D -u 10001 -h /data warp COPY --from=build /out/warp /out/warp-admin /out/warp-web /usr/local/bin/ COPY --from=seaweed /usr/bin/weed /usr/local/bin/weed COPY warp.yaml.example /etc/warp/warp.yaml.example COPY --chmod=755 Docker-AIO/entrypoint.sh /usr/local/bin/warp-aio # /data: the database, signing key and anything stored before S3 was on. # /seaweed/data: master, volume and filer, sharing it. # /seaweed/admin: the admin UI's session key and maintenance state. # Mount a volume on each; a fresh named volume inherits the ownership. RUN mkdir -p /data /seaweed/data /seaweed/admin && chown -R warp:warp /data /seaweed WORKDIR /data USER warp # Media goes to the bucket on the gateway inside this image: a fixed # loopback address, path style because a self-hosted gateway has no # per-bucket DNS, and a region SeaweedFS ignores. Media stored on the # filesystem before S3 was on is not moved into the bucket. ENV WARP_LISTEN_ADDRESS=":8008" \ WARP_PRIVATE_KEY="/data/signing.key" \ WARP_DB_DRIVER="pocketbase" \ WARP_DB_CONNECTION_STRING="/data/pb_data" \ WARP_PB_S3_ENABLED="true" \ WARP_PB_S3_ENDPOINT="http://127.0.0.1:8333" \ WARP_PB_S3_BUCKET="warp" \ WARP_PB_S3_REGION="us-east-1" \ WARP_PB_S3_FORCE_PATH_STYLE="true" \ WARP_LOG_FORMAT="json" \ WARP_ADMIN_LISTEN=":8009" \ WARP_ADMIN_HOMESERVER="http://127.0.0.1:8008" \ WARP_ADMIN_LOG_FORMAT="json" \ WARP_WEB_LISTEN=":8080" \ WARP_WEB_HOMESERVER="http://127.0.0.1:8008" \ WARP_WEB_LOG_FORMAT="json" \ WARP_AIO_ADMIN="true" \ WARP_AIO_WEB="true" \ WARP_AIO_EMBEDDED_POSTGRES="false" \ WARP_AIO_SEAWEEDFS="split" \ WEED_ADMIN_USER="admin" EXPOSE 8008 8009 8080 8333 23646 # Every process must answer, so a wedged one shows in `docker ps`. HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ CMD curl -fsS -o /dev/null http://127.0.0.1:9333/cluster/status && \ curl -fsS -o /dev/null http://127.0.0.1:8081/healthz && \ curl -fsS -o /dev/null http://127.0.0.1:8888/healthz && \ curl -fsS -o /dev/null http://127.0.0.1:8333/healthz && \ curl -fsS -o /dev/null http://127.0.0.1:23646/health && \ curl -fsS -o /dev/null http://127.0.0.1:8008/healthz && \ { [ "$WARP_AIO_ADMIN" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8009/healthz; } && \ { [ "$WARP_AIO_WEB" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8080/healthz; } || exit 1 ENTRYPOINT ["/sbin/tini", "--", "warp-aio"]