From 01a8fecf408201c51c5b44c1402a560d049b9931 Mon Sep 17 00:00:00 2001 From: tajniak81 <13187254+tajniak81@users.noreply.github.com> Date: Fri, 21 Aug 2026 17:02:14 +0200 Subject: [PATCH] Docker: stop telling operators to turn off the bootstrap that upgrades them Every deployment file advised setting PB_BOOTSTRAP=false "once the database is established". That was harmless while the schema was static. It stopped being harmless in 9bd5c52, which moved the plugin settings into a new app_settings collection: a stack upgraded with the bootstrap off never gets that collection, and a missing collection is deliberately read as "the database is not ready" rather than "no plugins configured" - so the plugin panel answers 503 indefinitely and the background retry spins forever. Fixing the advice rather than the reading: treating a missing collection as empty would let the first save write a fresh document over settings the server had simply failed to find, which is the failure this whole line of work exists to prevent. So all four compose files, all four .env examples, both stack READMEs and the AIO Dockerfile now say to leave the bootstrap on, including across upgrades, and name the symptom an operator would otherwise have to guess at. Turning it off is still supported, but framed as something to do only for a database known to match the running release. Compose files still parse as YAML; go build, go vet and go test ./... pass (untouched by this commit - it is comments and docs only). Note that this is guidance, not a guard: an operator who sets PB_BOOTSTRAP=false anyway still ends up in the same place, and the server would have to re-run the bootstrap when it finds the collection missing to make that impossible. Co-Authored-By: Claude Opus 5 --- API Server/README.md | 2 +- Docker-AIO/.env.example | 5 ++++- Docker-AIO/.env.prod.example | 5 ++++- Docker-AIO/Dockerfile | 5 ++++- Docker-AIO/README.md | 10 ++++++++-- Docker-AIO/docker-compose.prod.yml | 8 ++++++-- Docker-AIO/docker-compose.yml | 8 ++++++-- Docker/.env.example | 5 ++++- Docker/.env.prod.example | 5 ++++- Docker/README.md | 10 ++++++++-- Docker/docker-compose.prod.yml | 8 ++++++-- Docker/docker-compose.yml | 9 ++++++++- 12 files changed, 63 insertions(+), 17 deletions(-) diff --git a/API Server/README.md b/API Server/README.md index 03a0e5f..612a883 100644 --- a/API Server/README.md +++ b/API Server/README.md @@ -321,7 +321,7 @@ Copy `.env.example` to `.env` and fill in. Summary: | `PLUGINS_FILE` | `plugins.json` | legacy plugin store, imported once then renamed | | `OCPP_REQUIRE_TLS` | `true` | reject chargers that did not connect over TLS | | `OCPP_PUBLIC_URL` | — | canonical `ws(s)://` base to point chargers at | -| `PB_BOOTSTRAP` | `true` | run the on-boot schema create/reconcile | +| `PB_BOOTSTRAP` | `true` | run the on-boot schema create/reconcile (leave on across upgrades) | | `DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD` / `_NAME` | — / — / `Administrator` | first `superadmin`, created on boot when absent | `PB_URL`, `PB_ADMIN_EMAIL`, `PB_ADMIN_PASSWORD`, `PORT` and `CORS_ORIGINS` are diff --git a/Docker-AIO/.env.example b/Docker-AIO/.env.example index b12e418..814fe98 100644 --- a/Docker-AIO/.env.example +++ b/Docker-AIO/.env.example @@ -12,7 +12,10 @@ DRIVERVAULT_SUPERADMIN_EMAIL=owner@example.com DRIVERVAULT_SUPERADMIN_PASSWORD=change-me-long-password DRIVERVAULT_SUPERADMIN_NAME=Administrator -# Set to false to skip schema creation/reconcile once the database is set up. +# Schema creation/reconcile on boot. Leave this true: a release can add a +# collection the server needs (app_settings, which holds the plugin settings), +# and a stack that skipped the bootstrap never gets it — the plugin panel then +# answers 503 forever. Set false only for a database you know matches the release. PB_BOOTSTRAP=true # Allowed CORS origin(s) — match your web origin / WEB_PORT. diff --git a/Docker-AIO/.env.prod.example b/Docker-AIO/.env.prod.example index 4ac8b43..a3b637a 100644 --- a/Docker-AIO/.env.prod.example +++ b/Docker-AIO/.env.prod.example @@ -18,7 +18,10 @@ DRIVERVAULT_SUPERADMIN_EMAIL=owner@example.com DRIVERVAULT_SUPERADMIN_PASSWORD=change-me-long-password DRIVERVAULT_SUPERADMIN_NAME=Administrator -# Set to false to skip schema creation/reconcile once the database is set up. +# Schema creation/reconcile on boot. Leave this true: a release can add a +# collection the server needs (app_settings, which holds the plugin settings), +# and a stack that skipped the bootstrap never gets it — the plugin panel then +# answers 503 forever. Set false only for a database you know matches the release. PB_BOOTSTRAP=true # Allowed CORS origin(s) — match your public web URL / WEB_PORT. diff --git a/Docker-AIO/Dockerfile b/Docker-AIO/Dockerfile index 103d5e5..0e59897 100644 --- a/Docker-AIO/Dockerfile +++ b/Docker-AIO/Dockerfile @@ -219,7 +219,10 @@ ENV API_ADDR=:8080 \ # Required at runtime (no safe defaults): PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD. # Optional: DRIVERVAULT_SUPERADMIN_EMAIL / DRIVERVAULT_SUPERADMIN_PASSWORD create -# the first app super-admin on boot; PB_BOOTSTRAP=false skips schema setup. +# the first app super-admin on boot. PB_BOOTSTRAP=false skips schema setup — +# leave it on: a release can add a collection the server needs (app_settings, +# which holds the plugin settings), and a stack that skipped the bootstrap never +# gets it, leaving the plugin panel answering 503 indefinitely. # For Anker Solix charger control, OCPP_REQUIRE_TLS (default true) rejects # chargers that did not arrive over TLS — this image serves plain HTTP, so put a # TLS-terminating proxy in front and set OCPP_PUBLIC_URL to the public wss:// diff --git a/Docker-AIO/README.md b/Docker-AIO/README.md index 7e39384..83dfaae 100644 --- a/Docker-AIO/README.md +++ b/Docker-AIO/README.md @@ -62,8 +62,14 @@ Identical to the multi-container stack, and idempotent: 1. PocketBase upserts its superuser from `PB_ADMIN_EMAIL` / `PB_ADMIN_PASSWORD`. 2. The API Server waits for PocketBase to report healthy, then creates any missing collections, reconciles existing ones, and creates the first app - `superadmin` from `DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD`. Set - `PB_BOOTSTRAP=false` to skip once the database is established. + `superadmin` from `DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD`. + +> Leave `PB_BOOTSTRAP` at `true`, including across upgrades. A release can add a +> collection the server needs — `app_settings`, which holds the plugin settings, +> is one — and a stack that skipped the bootstrap never gets it. The plugin panel +> then answers `503` indefinitely, because a missing collection is read as "the +> database is not ready yet", never as "no plugins configured". Turn it off only +> for a database you know already matches the release you are running. ## Volumes diff --git a/Docker-AIO/docker-compose.prod.yml b/Docker-AIO/docker-compose.prod.yml index 8adc035..4f7ff72 100644 --- a/Docker-AIO/docker-compose.prod.yml +++ b/Docker-AIO/docker-compose.prod.yml @@ -26,8 +26,12 @@ services: # Probed by the panel status page. nginx serves the Web App on port 80 # inside this container, so the default (localhost:8090) would never answer. WEBAPP_URL: "http://127.0.0.1:80" - # Schema + super-admin bootstrap on boot (idempotent). Set PB_BOOTSTRAP=false - # to skip once the database is established. + # Schema + super-admin bootstrap (idempotent). Leave this ON. A release can + # add a collection the server needs — app_settings, holding the plugin + # settings, is one — and a stack that skipped the bootstrap never gets it: + # the plugin panel then answers 503 forever, because a missing collection + # is read as "database not ready", never as "no plugins configured". + # 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:-}" diff --git a/Docker-AIO/docker-compose.yml b/Docker-AIO/docker-compose.yml index af1bee9..87ab8e0 100644 --- a/Docker-AIO/docker-compose.yml +++ b/Docker-AIO/docker-compose.yml @@ -29,8 +29,12 @@ services: # Probed by the panel status page. nginx serves the Web App on port 80 # inside this container, so the default (localhost:8090) would never answer. WEBAPP_URL: "http://127.0.0.1:80" - # Schema + super-admin bootstrap on boot (idempotent). Set PB_BOOTSTRAP=false - # to skip once the database is established. + # Schema + super-admin bootstrap (idempotent). Leave this ON. A release can + # add a collection the server needs — app_settings, holding the plugin + # settings, is one — and a stack that skipped the bootstrap never gets it: + # the plugin panel then answers 503 forever, because a missing collection + # is read as "database not ready", never as "no plugins configured". + # 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:-}" diff --git a/Docker/.env.example b/Docker/.env.example index 4e9752e..87de3f7 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -12,7 +12,10 @@ DRIVERVAULT_SUPERADMIN_EMAIL=owner@example.com DRIVERVAULT_SUPERADMIN_PASSWORD=change-me-long-password DRIVERVAULT_SUPERADMIN_NAME=Administrator -# Set to false to skip schema creation/reconcile once the database is set up. +# Schema creation/reconcile on boot. Leave this true: a release can add a +# collection the server needs (app_settings, which holds the plugin settings), +# and a stack that skipped the bootstrap never gets it — the plugin panel then +# answers 503 forever. Set false only for a database you know matches the release. PB_BOOTSTRAP=true # --- API Server ------------------------------------------------------------- diff --git a/Docker/.env.prod.example b/Docker/.env.prod.example index 366e94e..b6b5b13 100644 --- a/Docker/.env.prod.example +++ b/Docker/.env.prod.example @@ -24,7 +24,10 @@ DRIVERVAULT_SUPERADMIN_EMAIL=owner@example.com DRIVERVAULT_SUPERADMIN_PASSWORD=change-me-long-password DRIVERVAULT_SUPERADMIN_NAME=Administrator -# Set to false to skip schema creation/reconcile once the database is set up. +# Schema creation/reconcile on boot. Leave this true: a release can add a +# collection the server needs (app_settings, which holds the plugin settings), +# and a stack that skipped the bootstrap never gets it — the plugin panel then +# answers 503 forever. Set false only for a database you know matches the release. PB_BOOTSTRAP=true # --- API Server -------------------------------------------------------------- diff --git a/Docker/README.md b/Docker/README.md index a8b4c08..0d4782e 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -47,8 +47,14 @@ Both steps are idempotent, so restarts and upgrades are safe: bootstrap it. The API Server then authenticates with the same credentials. 2. **The API Server** creates any missing collections and reconciles existing ones, then creates the first app `superadmin` from - `DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD` if no such user exists. Set - `PB_BOOTSTRAP=false` to skip once the database is established. + `DRIVERVAULT_SUPERADMIN_EMAIL` / `_PASSWORD` if no such user exists. + +> Leave `PB_BOOTSTRAP` at `true`, including across upgrades. A release can add a +> collection the server needs — `app_settings`, which holds the plugin settings, +> is one — and a stack that skipped the bootstrap never gets it. The plugin panel +> then answers `503` indefinitely, because a missing collection is read as "the +> database is not ready yet", never as "no plugins configured". Turn it off only +> for a database you know already matches the release you are running. No manual `setup-pocketbase.mjs` step is needed here — the server runs the same schema reconcile itself. diff --git a/Docker/docker-compose.prod.yml b/Docker/docker-compose.prod.yml index b37fa1e..ddf9a00 100644 --- a/Docker/docker-compose.prod.yml +++ b/Docker/docker-compose.prod.yml @@ -59,8 +59,12 @@ services: WEBAPP_URL: "http://web-app:8090" CORS_ALLOW_ORIGINS: "${CORS_ALLOW_ORIGINS:-http://localhost:8090}" AUTH_USERS_COLLECTION: "${AUTH_USERS_COLLECTION:-users}" - # Schema + super-admin bootstrap (idempotent). Set PB_BOOTSTRAP=false to - # skip it once the database is established. + # Schema + super-admin bootstrap (idempotent). Leave this ON. A release can + # add a collection the server needs — app_settings, holding the plugin + # settings, is one — and a stack that skipped the bootstrap never gets it: + # the plugin panel then answers 503 forever, because a missing collection + # is read as "database not ready", never as "no plugins configured". + # 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:-}" diff --git a/Docker/docker-compose.yml b/Docker/docker-compose.yml index 1087162..c837b78 100644 --- a/Docker/docker-compose.yml +++ b/Docker/docker-compose.yml @@ -52,7 +52,14 @@ services: AUTH_USERS_COLLECTION: "${AUTH_USERS_COLLECTION:-users}" # Schema + super-admin bootstrap (idempotent). Without the SUPERADMIN vars # the collections are still created but no app user is, leaving a stack - # you cannot log into. Set PB_BOOTSTRAP=false to skip once established. + # you cannot log into. + # + # Leave the bootstrap ON. A release can add a collection the server needs — + # app_settings, holding the plugin settings, is one — and a stack that + # skipped it never gets that collection: the plugin panel then answers 503 + # forever, because a missing collection is read as "database not ready", + # never as "no plugins configured". 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:-}"