From a3f998f15cd55e12af20e5d8f44fd8fbba1a0770 Mon Sep 17 00:00:00 2001 From: Ashesh Vashi Date: Sun, 7 Jun 2026 13:14:23 +0530 Subject: [PATCH] docs(container): warn about init-container tag mismatch on shared volumes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs pgadmin-org/pgadmin4#9940. Issue #9940 reported a SQLite "no such column: sharedserver.db_res" error on a Kubernetes deployment where the main pgAdmin container was pinned to 9.13 but an init container (running `setup.py load-servers` to import a servers.json) used the `pgadmin4:latest` image. The init container's migrations advanced the schema past 9.13's ORM expectations — specifically the `sharedserver_feature_parity_` migration (introduced in 9.15 via PR #9835) drops the `db_res` column from `sharedserver`. This is a real hazard of pgAdmin's forward-only-with-destructive- operations migration model when combined with the K8s init-container pattern, but it wasn't documented anywhere. Operators hitting this have no guidance on how to avoid it. Add a short section to docs/en_US/container_deployment.rst: - The single rule: init container's image tag must NOT be newer than the main container's, when they share a data volume. - A concrete Helm-values example showing the pinned tags side by side, with an explicit "Do NOT use :latest" note. - A reminder that during upgrades, every reference to the image tag must move together. Pure docs-only change — no code touched. Reviewable on its own. --- docs/en_US/container_deployment.rst | 75 +++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/docs/en_US/container_deployment.rst b/docs/en_US/container_deployment.rst index 2b7f4289c91..5e3c262615f 100644 --- a/docs/en_US/container_deployment.rst +++ b/docs/en_US/container_deployment.rst @@ -564,3 +564,78 @@ the container is typically launched per the example below: -e "SCRIPT_NAME=/pgadmin4" \ -l "traefik.frontend.pgadmin4.rule=Host(`host.example.com`) && PathPrefix(`/pgadmin4`)" \ -d dpage/pgadmin4 + +Sharing the data volume across containers (Kubernetes init containers) +---------------------------------------------------------------------- + +A common Kubernetes pattern is to bootstrap a pgAdmin deployment with one or +more init containers that share the ``/var/lib/pgadmin`` volume with the main +pgAdmin container — for example, an init container that runs +``setup.py load-servers`` to import a ``servers.json`` definition file at +each rollout. + +**The image tag of any such sidecar/init container must NOT be newer than +the main pgAdmin container's tag.** pgAdmin's configuration database +(``pgadmin4.db``) is migrated forward only — destructive operations such as +dropping a column are part of normal upgrades. If an init container running +``pgadmin4:latest`` (or any newer tag) executes ``setup.py`` against a +``pgadmin4.db`` that the main, older pgAdmin will then read, the init +container's migrations will advance the schema past what the main container's +ORM understands, and the main container will fail at runtime with errors +like ``no such column: .``. + +The safe configuration is to pin **both** the main container and every +sidecar/init container that touches the data volume to the **same** +explicit version tag, and upgrade them together: + +.. code-block:: yaml + + # In your Helm values / Deployment spec: + image: docker.io/dpage/pgadmin4:9.14 # main pgAdmin container + ... + extraInitContainers: + - name: import-servers-json + # Match the main container's tag exactly. Do NOT use :latest. + image: docker.io/dpage/pgadmin4:9.14 + # ... rest of the init container spec ... + +When upgrading pgAdmin in such a deployment, change every reference to the +image tag at the same time so that the main container and any sidecar/init +containers move forward together. + +Use ``strategy: Recreate``, not ``RollingUpdate`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Even when every container's image tag is pinned correctly, **a rolling +upgrade across pgAdmin versions is unsafe** when the old and new pods share +a data volume. Kubernetes' default ``RollingUpdate`` strategy starts the +new pod, waits for it to become healthy, and only *then* terminates the +old pod. During that overlap window: + +1. The new pod's startup runs ``db_upgrade()`` and applies any pending + migrations to the shared ``pgadmin4.db`` — including destructive ones + (column drops, table renames, type changes). +2. The new pod becomes healthy. The old pod is still alive and still has + ``pgadmin4.db`` open with its older ORM schema metadata. +3. Until Kubernetes terminates the old pod, requests routed to it + generate queries that reference the pre-migration schema. Those + queries now fail at runtime against the migrated database — for + example, ``no such column:
.`` when a column was + dropped in this release. + +Because pgAdmin's migrations are forward-only with single-step destructive +operations (a column dropped in release N+1 is gone immediately, not after +a deprecation cycle), there is no safe way for an N-version pod to keep +serving against a migrated N+1 schema. Set the deployment strategy to +``Recreate``, which terminates the old pod *before* starting the new one: + +.. code-block:: yaml + + spec: + strategy: + type: Recreate # not RollingUpdate + +The trade-off is a few seconds of downtime during each upgrade, in +exchange for not serving traffic from a pod whose ORM is misaligned with +the database schema underneath it. This is the supported upgrade +strategy for pgAdmin K8s deployments today.