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>
158 lines
7.8 KiB
YAML
158 lines
7.8 KiB
YAML
name: drivervault-aio
|
|
|
|
# Single all-in-one container, with SeaweedFS: PocketBase + API Server + Web App
|
|
# (nginx) in one image, plus an S3 object store beside it. Self-contained — one
|
|
# file, nothing to layer.
|
|
#
|
|
# cp .env.seaweedfs.example .env (then edit it)
|
|
# docker compose -f docker-compose.seaweedfs.yml up -d --build
|
|
#
|
|
# The build context is the project root so the Dockerfile can reach both
|
|
# "API Server/" and "Web App/".
|
|
#
|
|
# This is docker-compose.yml plus storage: 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.
|
|
#
|
|
# SeaweedFS runs as a second container beside the all-in-one, not as a fourth
|
|
# process inside it: keeping the object store in that image, on the volume the
|
|
# files are being moved off, would defeat the point and would mean rebuilding.
|
|
#
|
|
# Before turning this on for a stack that already has uploads: PocketBase does
|
|
# NOT copy existing files into the bucket. See README.md.
|
|
|
|
services:
|
|
seaweedfs:
|
|
image: "${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}"
|
|
container_name: drivervault-aio-seaweedfs
|
|
restart: unless-stopped
|
|
# One process, four roles: master, volume, filer and the S3 gateway. -dir is
|
|
# the only state it keeps.
|
|
command: server -dir=/data -s3 -master.volumeSizeLimitMB=1024
|
|
environment:
|
|
# SeaweedFS falls back to these when started without an -s3.config file,
|
|
# and configuring one identity is what takes the S3 gateway out of its
|
|
# default allow-anyone mode. The same credentials PocketBase authenticates
|
|
# with below — one pair to set, in .env.
|
|
AWS_ACCESS_KEY_ID: "${PB_S3_ACCESS_KEY:?set PB_S3_ACCESS_KEY in .env}"
|
|
AWS_SECRET_ACCESS_KEY: "${PB_S3_SECRET:?set PB_S3_SECRET in .env}"
|
|
volumes:
|
|
# From here on the attachments live here, not on pb_data.
|
|
- seaweed_data:/data
|
|
# No published port: the stack reaches the gateway over the compose network.
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:9333/cluster/status || exit 1"]
|
|
interval: 10s
|
|
timeout: 3s
|
|
retries: 12
|
|
start_period: 20s
|
|
|
|
seaweedfs-init:
|
|
image: "${SEAWEED_IMAGE:-chrislusf/seaweedfs:4.45}"
|
|
container_name: drivervault-aio-seaweedfs-init
|
|
# Runs once and exits. PocketBase never issues a CreateBucket of its own and
|
|
# SeaweedFS will not conjure one on first upload, so something has to.
|
|
# Creating a bucket that already exists is a no-op, so every later boot
|
|
# passes straight through.
|
|
restart: "no"
|
|
depends_on:
|
|
seaweedfs:
|
|
condition: service_healthy
|
|
entrypoint: ["/bin/sh", "-c"]
|
|
# `|| true` so a restart is never blocked by the shell's exit status: this
|
|
# step is best-effort, and a gateway that is genuinely unreachable is
|
|
# reported by the API Server's own S3 check at boot, with the reason.
|
|
command:
|
|
- 'echo "s3.bucket.create -name ${PB_S3_BUCKET:-drivervault}" | weed shell -master=seaweedfs:9333 || true'
|
|
|
|
drivervault:
|
|
build:
|
|
# Project root (one level up from this compose file).
|
|
context: ..
|
|
dockerfile: Docker-AIO/Dockerfile
|
|
args:
|
|
# Empty -> bundle uses same-origin "/api", proxied internally by nginx.
|
|
- VITE_API_BASE=${VITE_API_BASE:-}
|
|
# Bare name = pass through only when set in the environment, so an unset
|
|
# PB_VERSION leaves the Dockerfile pin in place instead of overriding it
|
|
# with an empty string (which would resolve "latest" at build time).
|
|
- PB_VERSION
|
|
image: drivervault-aio
|
|
container_name: drivervault-aio
|
|
restart: unless-stopped
|
|
depends_on:
|
|
# PocketBase — inside this container — is the process that reads and
|
|
# writes the objects, so the gateway has to be serving first, and the
|
|
# bucket has to exist before the bootstrap points PocketBase at it.
|
|
seaweedfs:
|
|
condition: service_healthy
|
|
seaweedfs-init:
|
|
condition: service_completed_successfully
|
|
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 --------------------------------------------------
|
|
# 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.
|
|
PB_S3_ENABLED: "true"
|
|
PB_S3_BUCKET: "${PB_S3_BUCKET:-drivervault}"
|
|
# The service name: a server-to-server call inside the compose network.
|
|
PB_S3_ENDPOINT: "http://seaweedfs:8333"
|
|
# SeaweedFS ignores the region; PocketBase insists on having one.
|
|
PB_S3_REGION: "${PB_S3_REGION:-us-east-1}"
|
|
PB_S3_ACCESS_KEY: "${PB_S3_ACCESS_KEY}"
|
|
PB_S3_SECRET: "${PB_S3_SECRET}"
|
|
# Path style, because a self-hosted gateway has no per-bucket DNS.
|
|
PB_S3_FORCE_PATH_STYLE: "true"
|
|
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}
|
|
volumes:
|
|
# The only volume: the API Server keeps no state on disk, so everything
|
|
# it owns — plugin settings included — lives in the database.
|
|
- pb_data:/pb/pb_data
|
|
healthcheck:
|
|
# All three processes must answer. Declared here as well as in the image so
|
|
# the check is visible, and works against an older pulled image.
|
|
test: ["CMD-SHELL", "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: 5s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
volumes:
|
|
pb_data:
|
|
seaweed_data:
|