Bring the Docker setup in line with DriverVault's

The combined stack now includes PocketBase as a first-class service instead
of assuming an external one: a Docker/pocketbase image built from the release
binary that bundles the pb_migrations and upserts the superuser on boot, so a
fresh stack checks/creates its collections and super-admin the same way
DriverVault does. Add production compose variants (registry images) and
.env(.prod).example files for both Docker/ and Docker AIO/, a top-level compose
name, a PocketBase healthcheck with a service_healthy dependency, and
env-driven ports. Move the containerized PocketBase to :8070 (the live remote
PB stays 10.2.1.10:8026). Add a root .dockerignore to trim the repo-root build
context used by the pocketbase and AIO images.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tajniak81andClaude Opus 4.8 committed 2026-07-19 19:16:21 +02:00
1 parent 11fb1e060d
commit 2d20f6f29c
13 files changed
+401 -32

No files matched your search

+24
View File
@@ -0,0 +1,24 @@
# Copy to .env and fill in. Used by the combined docker-compose.yml (build & run).
# --- PocketBase superuser (also used by the API Server to authenticate) ------
# Created/updated on the PocketBase container's first boot. REQUIRED.
POCKETBASE_ADMIN_EMAIL=admin@dji.local
POCKETBASE_ADMIN_PASSWORD=change-me-long-password
# The app super-admin login (superadmin@pilotvault.local) is created by the
# bundled PocketBase migrations on first boot — no separate env var needed.
# --- API Server -------------------------------------------------------------
# Allowed CORS origin(s) for the web app (match WEB_PORT / your public URL).
# Native apps are not subject to CORS.
CORS_ALLOW_ORIGINS=http://localhost:8090
# --- Host port mappings (optional; defaults shown) --------------------------
PB_PORT=8070
API_PORT=8080
WEB_PORT=8090
# --- Storage ----------------------------------------------------------------
# Docker-managed named volume by default; set PB_DATA to an absolute host path
# for a bind mount, e.g. PB_DATA=/srv/pilotvault/pb_data
PB_DATA=pb_data
+38
View File
@@ -0,0 +1,38 @@
# PilotVault — production stack config.
# Copy to .env and fill in, then:
# docker compose -f docker-compose.prod.yml pull
# docker compose -f docker-compose.prod.yml up -d
# --- Registry images --------------------------------------------------------
# Defaults point at the internal registry; override to pin a tag or use a mirror.
PB_IMAGE=10.2.1.10:5500/admin/pilotvault-pocketbase:latest
API_IMAGE=10.2.1.10:5500/admin/pilotvault-api-server:latest
WEB_IMAGE=10.2.1.10:5500/admin/pilotvault-web-app:latest
# --- PocketBase superuser (required) ----------------------------------------
# Created/updated on the PocketBase container's first boot. The API Server uses
# these same credentials to manage the database.
POCKETBASE_ADMIN_EMAIL=admin@dji.local
POCKETBASE_ADMIN_PASSWORD=change-me-long-password
# The app super-admin login (superadmin@pilotvault.local) is created by the
# bundled PocketBase migrations on first boot — distinct from the superuser above.
# --- API Server -------------------------------------------------------------
# Allowed CORS origin(s) for the web app (match your public URL / WEB_PORT).
CORS_ALLOW_ORIGINS=http://localhost:8090
# --- Ports ------------------------------------------------------------------
# WEB_PORT is the public front door (bound on all interfaces).
WEB_PORT=8090
# PocketBase admin UI and the API panel are bound to localhost only by default.
# Set PB_BIND / API_BIND to 0.0.0.0 to expose them on the network.
PB_PORT=8070
PB_BIND=127.0.0.1
API_PORT=8080
API_BIND=127.0.0.1
# --- Storage ----------------------------------------------------------------
# Default is a Docker-managed named volume ("pb_data"). Set PB_DATA to an
# absolute host path for a bind mount, e.g. PB_DATA=/srv/pilotvault/pb_data
PB_DATA=pb_data
+70
View File
@@ -0,0 +1,70 @@
name: pilotvault
# Production PilotVault stack — pulls prebuilt images from the registry instead of
# building from source. Everything an operator needs to set lives in .env.
#
# 1. cp .env.prod.example .env (then edit — secrets/ports/volumes/images)
# 2. docker compose -f docker-compose.prod.yml pull
# 3. docker compose -f docker-compose.prod.yml up -d
#
# Traffic flow (browser): Web App BFF --/api--> API Server --> PocketBase.
#
# On first boot PocketBase upserts its superuser from POCKETBASE_ADMIN_* and runs
# the bundled migrations (create/reconcile collections + seed the baseline org and
# the superadmin@pilotvault.local login). Idempotent, so restarts/upgrades are safe.
services:
pocketbase:
image: "${PB_IMAGE:-10.2.1.10:5500/admin/pilotvault-pocketbase:latest}"
container_name: pilotvault-pocketbase
restart: unless-stopped
environment:
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL:?set POCKETBASE_ADMIN_EMAIL in .env}"
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD:?set POCKETBASE_ADMIN_PASSWORD in .env}"
volumes:
# Named volume by default; set PB_DATA to a host path in .env for a bind mount.
- "${PB_DATA:-pb_data}:/pb/pb_data"
ports:
# Bound to localhost by default — the admin UI (/_/) is reachable only on the
# host. Set PB_BIND=0.0.0.0 in .env to expose it on the network.
- "${PB_BIND:-127.0.0.1}:${PB_PORT:-8070}:8070"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8070/api/health || exit 1"]
interval: 10s
timeout: 3s
retries: 12
start_period: 10s
api-server:
image: "${API_IMAGE:-10.2.1.10:5500/admin/pilotvault-api-server:latest}"
container_name: pilotvault-api-server
restart: unless-stopped
depends_on:
pocketbase:
condition: service_healthy
environment:
API_ADDR: ":8080"
POCKETBASE_URL: "http://pocketbase:8070"
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL}"
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD}"
CORS_ALLOW_ORIGINS: "${CORS_ALLOW_ORIGINS:-http://localhost:8090}"
ports:
# Localhost-only by default (the Web App reaches it over the internal
# network). Set API_BIND=0.0.0.0 to expose the API panel on the network.
- "${API_BIND:-127.0.0.1}:${API_PORT:-8080}:8080"
web-app:
image: "${WEB_IMAGE:-10.2.1.10:5500/admin/pilotvault-web-app:latest}"
container_name: pilotvault-web-app
restart: unless-stopped
depends_on:
- api-server
environment:
ADDR: ":8090"
API_BASE: "http://api-server:8080"
ports:
# The public front door. Bound on all interfaces so browsers can reach it.
- "${WEB_PORT:-8090}:8090"
volumes:
pb_data:
+69 -16
View File
@@ -1,40 +1,93 @@
# Combined stack: API Server + Web App on a shared network.
# Run from this Docker/ folder: docker compose up --build
# Build contexts point back up to each service directory.
name: pilotvault
# Combined PilotVault stack: PocketBase (database) + API Server + Web App, built
# from source on a shared network. Run from this Docker/ folder:
#
# cp .env.example .env # then edit the secrets/ports
# docker compose up --build
#
# Traffic flow (browser): Web App BFF --/api--> API Server --> PocketBase.
#
# On first boot PocketBase upserts its superuser from POCKETBASE_ADMIN_* and runs
# the bundled JS migrations, which create/reconcile every collection and seed the
# baseline org + accounts (including the superadmin@pilotvault.local app login).
# Every step is idempotent, so restarts and upgrades are safe.
services:
pocketbase:
build:
# Repo root — the Dockerfile bundles the migrations from "API Server/".
context: ..
dockerfile: Docker/pocketbase/Dockerfile
image: pilotvault-pocketbase
container_name: pilotvault-pocketbase
restart: unless-stopped
environment:
# The superuser is created/updated on boot (the API Server authenticates
# with it). This is the only place the first superuser can be created — the
# REST API cannot bootstrap it.
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL:?set POCKETBASE_ADMIN_EMAIL in .env}"
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD:?set POCKETBASE_ADMIN_PASSWORD in .env}"
volumes:
# Named volume by default; set PB_DATA to a host path in .env for a bind mount.
- "${PB_DATA:-pb_data}:/pb/pb_data"
ports:
# Admin UI / API exposed on the host for management (http://host:8070/_/).
- "${PB_PORT:-8070}:8070"
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8070/api/health || exit 1"]
interval: 10s
timeout: 3s
retries: 12
start_period: 10s
networks:
- pilotvault
api-server:
build:
context: "../API Server"
image: pilotvault-api-server
container_name: pilotvault-api-server
# Config (POCKETBASE_URL, CORS_ALLOW_ORIGINS, POCKETBASE_ADMIN_*) from .env.
env_file:
- "../API Server/.env"
restart: unless-stopped
depends_on:
pocketbase:
condition: service_healthy
environment:
API_ADDR: ":8080"
# Reach PocketBase by its service name on the shared network.
POCKETBASE_URL: "http://pocketbase:8070"
POCKETBASE_ADMIN_EMAIL: "${POCKETBASE_ADMIN_EMAIL}"
POCKETBASE_ADMIN_PASSWORD: "${POCKETBASE_ADMIN_PASSWORD}"
# Same-origin requests go through the Web App BFF, so CORS is only needed if
# a browser ever calls the API Server directly. Default to the web origin.
CORS_ALLOW_ORIGINS: "${CORS_ALLOW_ORIGINS:-http://localhost:8090}"
ports:
- "8080:8080"
# Optional direct access to the API Server (and its panel at /); the Web App
# reaches it over the internal network, not this host port.
- "${API_PORT:-8080}:8080"
networks:
- pilotvault
restart: unless-stopped
web-app:
build:
context: "../Web App"
image: pilotvault-web-app
container_name: pilotvault-web-app
environment:
ADDR: ":8090"
# Reach the API Server by its service name on the shared network —
# no host.docker.internal needed here.
API_BASE: "http://api-server:8080"
ports:
- "8090:8090"
restart: unless-stopped
depends_on:
- api-server
environment:
ADDR: ":8090"
# The BFF reverse-proxies /api/* to the API Server over the shared network.
API_BASE: "http://api-server:8080"
ports:
- "${WEB_PORT:-8090}:8090"
networks:
- pilotvault
restart: unless-stopped
networks:
pilotvault:
driver: bridge
volumes:
pb_data:
+44
View File
@@ -0,0 +1,44 @@
# syntax=docker/dockerfile:1
#
# PocketBase for PilotVault, built from the official release binary on alpine.
#
# It bundles the JS migrations from "API Server/pocketbase/pb_migrations" so that
# on first serve PocketBase creates/reconciles every collection and seeds the
# baseline organization + accounts (including the superadmin@pilotvault.local app
# login). Together with the `superuser upsert` in the entrypoint, THIS is where
# PilotVault "checks" that the schema and the super-admin exist — all idempotent,
# so restarts and upgrades are safe.
#
# BUILD CONTEXT IS THE REPO ROOT (the migrations live under "API Server/"). It is
# built for you by Docker/docker-compose.yml; to build it by hand run from
# E:\VS Code Projects\PilotVault:
#
# docker build -f Docker/pocketbase/Dockerfile -t pilotvault-pocketbase .
FROM alpine:latest
# Pin the version to match the rest of PilotVault; override with --build-arg.
# PB_ARCH is "amd64" (default) or "arm64" (e.g. Apple Silicon).
ARG PB_VERSION=0.22.21
ARG PB_ARCH=amd64
RUN apk add --no-cache ca-certificates tzdata unzip wget
WORKDIR /pb
RUN set -eux; \
wget -q -O /tmp/pb.zip \
"https://github.com/pocketbase/pocketbase/releases/download/v${PB_VERSION}/pocketbase_${PB_VERSION}_linux_${PB_ARCH}.zip"; \
unzip /tmp/pb.zip -d /pb; \
rm /tmp/pb.zip
# Schema + seed migrations (single source of truth in the API Server tree).
COPY ["API Server/pocketbase/pb_migrations/", "/pb/pb_migrations/"]
COPY ["Docker/pocketbase/entrypoint.sh", "/entrypoint.sh"]
RUN chmod +x /entrypoint.sh
# pb_data holds the SQLite database and uploads — mount a volume here.
VOLUME ["/pb/pb_data"]
EXPOSE 8070
ENTRYPOINT ["/entrypoint.sh"]
+19
View File
@@ -0,0 +1,19 @@
#!/bin/sh
set -e
# Create/update the superuser from env vars so the API Server can authenticate on
# first boot. `superuser upsert` is idempotent (PocketBase v0.22+/v0.23). Accepts
# either the PilotVault names (POCKETBASE_ADMIN_*) or the short PB_ADMIN_* aliases.
ADMIN_EMAIL="${POCKETBASE_ADMIN_EMAIL:-$PB_ADMIN_EMAIL}"
ADMIN_PASSWORD="${POCKETBASE_ADMIN_PASSWORD:-$PB_ADMIN_PASSWORD}"
if [ -n "$ADMIN_EMAIL" ] && [ -n "$ADMIN_PASSWORD" ]; then
echo "Ensuring PocketBase superuser $ADMIN_EMAIL exists..."
/pb/pocketbase superuser upsert "$ADMIN_EMAIL" "$ADMIN_PASSWORD" \
|| echo "warning: superuser upsert failed; create an admin via the UI at /_/"
fi
# Serve. --migrationsDir applies the bundled JS migrations on boot, which
# create/reconcile every collection and seed the baseline org + accounts
# (including superadmin@pilotvault.local) — all idempotent.
exec /pb/pocketbase serve --http=0.0.0.0:8070 --migrationsDir=/pb/pb_migrations