PocketBase reaches the gateway over the compose network (or inside the AIO container), so publishing 8333 on the host only made a second SeaweedFS stack fail with "port is already allocated". Drop the binding and SEAWEED_S3_BIND/SEAWEED_S3_PORT. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
167 lines
9.9 KiB
YAML
167 lines
9.9 KiB
YAML
name: drivervault-aio
|
|
|
|
# Production all-in-one with SeaweedFS split into its roles — and, unlike the
|
|
# other prod files, built into ONE image here: Dockerfile.seaweedfs.split bakes
|
|
# master, volume, filer, S3 gateway and the admin UI into the same container as
|
|
# PocketBase, the API Server and the Web App. One service, one container, three
|
|
# volumes. Self-contained: one file, no overlays. Everything an operator needs
|
|
# to set lives in .env.
|
|
#
|
|
# 1. cp .env.prod.seaweedfs.split.example .env (then edit it)
|
|
# 2. docker compose -f docker-compose.prod.seaweedfs.split.yml build
|
|
# 3. docker compose -f docker-compose.prod.seaweedfs.split.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.
|
|
#
|
|
# What the split buys inside one image: per-role restart under supervisord,
|
|
# per-role metrics ports, and the SeaweedFS admin UI (weed admin) — a cluster
|
|
# view, and Object Store → Users, where S3 identities are created and revoked
|
|
# without touching a file. What it costs against the six-container shape it
|
|
# replaces: a new SeaweedFS is a rebuild (SEAWEED_VERSION below), not a tag
|
|
# change, and the roles cannot be spread over hosts. If the object store should
|
|
# stay outside the app image, use docker-compose.prod.seaweedfs.yml — the S3
|
|
# behaviour is identical.
|
|
#
|
|
# The on-disk layout is the same as every other SeaweedFS shape here: master,
|
|
# volume and filer share one data mount, exactly as `weed server -dir` lays it
|
|
# out (master raft state, volume .dat/.idx, the filer's filerldb2/ — no filename
|
|
# overlap). So a SEAWEED_DATA volume from docker-compose.prod.seaweedfs.yml
|
|
# mounts here unchanged, and vice versa, with no migration either way.
|
|
#
|
|
# Inside the container every SeaweedFS role listens on loopback except the S3
|
|
# gateway and the admin UI. Only the admin UI is published, on loopback by
|
|
# default; PocketBase reaches the gateway inside. The volume server serves file
|
|
# content by id with no authentication at all, so it is never reachable from
|
|
# outside; the admin UI shows what its port would.
|
|
#
|
|
# 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 the S3 gateway's program creates the bucket and seeds
|
|
# PocketBase's identity into the filer's IAM store, PocketBase 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.split
|
|
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-split: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: the gateway's program seeds them into
|
|
# the filer's IAM store as the identity named "drivervault" (updating it
|
|
# in place on every boot — that is how a rotated secret lands) *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}"
|
|
# --- SeaweedFS admin UI --------------------------------------------
|
|
# An unauthenticated panel that can mint credentials for the bucket *is*
|
|
# the bucket, so the password is required rather than defaulted — weed
|
|
# leaves auth off entirely when it is empty, and the image refuses to
|
|
# start the panel at all in that case. Read from WEED_ADMIN_* rather
|
|
# than a flag, which keeps it off the process command line.
|
|
WEED_ADMIN_USER: "${SEAWEED_ADMIN_USER:-admin}"
|
|
WEED_ADMIN_PASSWORD: "${SEAWEED_ADMIN_PASSWORD:?set SEAWEED_ADMIN_PASSWORD in .env}"
|
|
# Optional view-only login. weed ignores it unless the admin password
|
|
# above is set, which it is.
|
|
WEED_ADMIN_READONLY_USER: "${SEAWEED_ADMIN_READONLY_USER:-}"
|
|
WEED_ADMIN_READONLY_PASSWORD: "${SEAWEED_ADMIN_READONLY_PASSWORD:-}"
|
|
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 is not published: PocketBase reaches it inside the
|
|
# container. The admin UI is, on loopback: storage plumbing, not one of
|
|
# the app's own panels. On a remote host that means unreachable, so set
|
|
# SEAWEED_ADMIN_BIND=0.0.0.0 and put it behind a reverse proxy.
|
|
- "${SEAWEED_ADMIN_BIND:-127.0.0.1}:${SEAWEED_ADMIN_PORT:-23646}:23646"
|
|
volumes:
|
|
# Named volumes by default; set any of them 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"
|
|
# The admin UI's own small volume: session key and maintenance state, no
|
|
# object data.
|
|
- "${SEAWEED_ADMIN_DATA:-seaweed_admin}:/seaweed/admin"
|
|
healthcheck:
|
|
# Every process must answer — the five SeaweedFS roles 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
|
|
# the SeaweedFS chain 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:8081/healthz >/dev/null && wget -qO- http://127.0.0.1:8888/healthz >/dev/null && wget -qO- http://127.0.0.1:8333/healthz >/dev/null && wget -qO- http://127.0.0.1:23646/health >/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:
|
|
# Master, volume and filer share it — the same layout `weed server -dir`
|
|
# writes, so this file and docker-compose.prod.seaweedfs.yml can swap places
|
|
# on it.
|
|
seaweed_data:
|
|
# The admin UI's own session key and maintenance-task state. Small, and no
|
|
# part of the object store.
|
|
seaweed_admin:
|