Files
DriverVault/Docker-AIO/docker-compose.prod.seaweedfs.yml
T
tajniak81andClaude Opus 5 c9a95131a5 The single-process object store follows its split sibling into the image
Dockerfile.seaweedfs bakes `weed server -s3` into the all-in-one as a
fourth supervisord program, sharing every build stage and the nginx
config with Dockerfile.seaweedfs.split. The gateway's single identity is
mapped from the PB_S3_* pair PocketBase already authenticates with, and
an empty pair refuses to start rather than serving allow-anyone. The
seaweedfs-init container becomes a step in PocketBase's program — wait
for the gateway, create the bucket, then serve — so the bucket exists
before the API Server's bootstrap checks it, in the order compose kept
with depends_on. The volume server moves to 8081 because 8080 is the API
Server; weed server has one bind flag for all four roles, so it stays
open and the comment says never to publish it.

docker-compose.prod.seaweedfs.yml goes from three services to one, with
build: and image: both set under a registry name of its own. The env
example and README follow.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 14:59:16 +02:00

141 lines
8.0 KiB
YAML

name: drivervault-aio
# Production all-in-one with SeaweedFS — and, unlike docker-compose.prod.yml,
# built into ONE image here: Dockerfile.seaweedfs bakes `weed server -s3`
# (master, volume, filer and S3 gateway in one process) into the same container
# as PocketBase, the API Server and the Web App. One service, one container,
# two volumes. Self-contained: one file, no overlays. Everything an operator
# needs to set lives in .env.
#
# 1. cp .env.prod.seaweedfs.example .env (then edit it)
# 2. docker compose -f docker-compose.prod.seaweedfs.yml build
# 3. docker compose -f docker-compose.prod.seaweedfs.yml up -d
#
# The build context is the repo root, so this has to run on a host that has the
# checkout. To deploy elsewhere, `push` the built image to the registry named in
# AIO_IMAGE and `pull` it there — `image:` and `build:` are both set, so the
# same file does either.
#
# This is docker-compose.prod.yml plus an S3 object store: PocketBase keeps its
# record files — document scans, service and refill receipts, workshop invoices,
# part photos — in a SeaweedFS bucket instead of on the pb_data volume next to
# the database. The database and PocketBase's own backups stay on PB_DATA.
# Clients cannot tell the difference: an attachment has always been fetched
# through the API Server, never from a storage URL.
#
# What baking it in costs against the two-container shape it replaces: a new
# SeaweedFS is a rebuild (SEAWEED_VERSION below), not a tag change. For
# per-role restart, metrics and the SeaweedFS admin UI, see
# docker-compose.prod.seaweedfs.split.yml — the S3 behaviour is identical, and
# the data volume is interchangeable (both write the `weed server -dir` layout).
#
# Before turning this on for a stack that already has uploads: PocketBase does
# NOT copy existing files into the bucket. See README.md.
#
# On first boot PocketBase's program creates the bucket and upserts the
# superuser from PB_ADMIN_*, and the API Server creates any missing
# collections, the DriverVault super-admin from DRIVERVAULT_SUPERADMIN_*, and
# points PocketBase's file storage at the bucket. Every step is idempotent.
services:
drivervault:
build:
# Project root (one level up from this compose file), so the Dockerfile
# can reach both "API Server/" and "Web App/".
context: ..
dockerfile: Docker-AIO/Dockerfile.seaweedfs
args:
# Empty -> bundle uses same-origin "/api", proxied internally by nginx.
- VITE_API_BASE=${VITE_API_BASE:-}
# Bare names = pass through only when set in the environment, so an
# unset PB_VERSION / SEAWEED_VERSION leaves the Dockerfile pin in place
# instead of overriding it with an empty string (which for PB_VERSION
# would resolve "latest" at build time, and for SEAWEED_VERSION would
# not build at all).
- PB_VERSION
- SEAWEED_VERSION
# Tagged for the registry so `docker compose push` lands it where `pull`
# on the deploy host expects it.
image: "${AIO_IMAGE:-10.2.1.10:5500/admin/drivervault-aio-seaweedfs:latest}"
container_name: drivervault-aio
restart: unless-stopped
environment:
# Superuser (also used by the API Server to authenticate to PocketBase).
PB_ADMIN_EMAIL: "${PB_ADMIN_EMAIL:?set PB_ADMIN_EMAIL in .env}"
PB_ADMIN_PASSWORD: "${PB_ADMIN_PASSWORD:?set PB_ADMIN_PASSWORD in .env}"
# Match CORS to the web origin (only used if a browser calls the API directly).
CORS_ALLOW_ORIGINS: "${CORS_ALLOW_ORIGINS:-http://localhost:8090}"
# Probed by the panel status page. nginx serves the Web App on port 80
# inside this container, so plain localhost:8090 would never answer.
# Override WEBAPP_URL in .env to make a change from the panel's Web App
# screen permanent; the panel alone only holds it for the container's life.
WEBAPP_URL: "${WEBAPP_URL:-http://127.0.0.1:80}"
# Schema + super-admin bootstrap (idempotent). Leave this ON: a release can
# add collections or fields the server needs, and a stack that skips the
# bootstrap never gets them. The API Server self-heals exactly one thing —
# app_settings, the collection holding the plugin settings, which it
# creates on demand because it cannot serve the plugin panel without it.
# Every other schema change still depends on this flag. Turn it off only
# for a database you know already matches the release.
PB_BOOTSTRAP: "${PB_BOOTSTRAP:-true}"
DRIVERVAULT_SUPERADMIN_EMAIL: "${DRIVERVAULT_SUPERADMIN_EMAIL:-}"
DRIVERVAULT_SUPERADMIN_PASSWORD: "${DRIVERVAULT_SUPERADMIN_PASSWORD:-}"
DRIVERVAULT_SUPERADMIN_NAME: "${DRIVERVAULT_SUPERADMIN_NAME:-Administrator}"
# OCPP charger control (Anker Solix). This image serves plain HTTP, so a
# charger can only connect when TLS is terminated in front of it (set
# OCPP_PUBLIC_URL to the public wss:// base) — or, on a trusted network,
# with OCPP_REQUIRE_TLS=false.
OCPP_REQUIRE_TLS: "${OCPP_REQUIRE_TLS:-true}"
OCPP_PUBLIC_URL: "${OCPP_PUBLIC_URL:-}"
# --- File storage --------------------------------------------------
# The image fixes the rest (PB_S3_ENABLED, the loopback endpoint, path
# style, the region): the gateway is inside, at an address that cannot
# change. supervisord passes these through to the API Server, whose
# bootstrap writes them into PocketBase's settings on every boot,
# idempotently. Only record files move — scans, receipts, invoices, part
# photos. The database and PocketBase's own backups stay on PB_DATA.
#
# The credentials do double duty: they configure the gateway's single
# identity — which is what takes it out of its default allow-anyone mode
# — *and* they are what PocketBase authenticates with. No safe defaults;
# the gateway refuses to start without them, and with it the whole
# container.
PB_S3_BUCKET: "${PB_S3_BUCKET:-drivervault}"
PB_S3_ACCESS_KEY: "${PB_S3_ACCESS_KEY:?set PB_S3_ACCESS_KEY in .env}"
PB_S3_SECRET: "${PB_S3_SECRET:?set PB_S3_SECRET in .env}"
ports:
- "${WEB_PORT:-8090}:80" # Web App
- "${PB_PORT:-8070}:8070" # PocketBase admin UI / API
- "${API_PORT:-8080}:8080" # API Server + panel (root /) + /ocpp/{serial}
# The S3 gateway, loopback only: PocketBase reaches it inside the
# container, so this is here for `aws s3 ls --endpoint-url
# http://127.0.0.1:8333` and nothing else. Set SEAWEED_S3_BIND=0.0.0.0 to
# expose it, and mean it. Nothing else of SeaweedFS is published: the
# volume server (8081 inside) serves file content by id with no
# authentication at all.
- "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333"
volumes:
# Named volumes by default; set either to a host path in .env for a bind
# mount. The API Server keeps no state on disk, so everything it owns
# (plugin settings included) is in PB_DATA. Back up PB_DATA and
# SEAWEED_DATA together — from here on the attachments live in the
# second, not the first.
- "${PB_DATA:-pb_data}:/pb/pb_data"
- "${SEAWEED_DATA:-seaweed_data}:/seaweed/data"
healthcheck:
# Every process must answer — SeaweedFS's master and gateway and the three
# app processes. Declared here as well as in the image so the check is
# visible, and works against an older pulled image. start_period covers
# SeaweedFS coming up plus the first-boot schema bootstrap.
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9333/cluster/status >/dev/null && wget -qO- http://127.0.0.1:8333/healthz >/dev/null && wget -qO- http://127.0.0.1:8070/api/health >/dev/null && wget -qO- http://127.0.0.1:8080/healthz >/dev/null && wget -qO- http://127.0.0.1:80/healthz >/dev/null || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 90s
volumes:
pb_data:
# The `weed server -dir` layout, so this file and its .split. twin can swap
# places on it.
seaweed_data: