feedc0de 49f1692e44
Validate and deploy phpPgAdmin / Validate manifests (push) Successful in 22s
Validate and deploy phpPgAdmin / Deploy to Kubernetes (push) Successful in 40s
Deploy phpPgAdmin with maintained public image
2026-08-08 23:02:42 +02:00
2025-10-09 21:31:30 +02:00

phpPgAdmin on Kubernetes

Production deployment for the brunner.ninja phpPgAdmin service. The repository uses a maintained public container image, a single plain Kubernetes manifest, Traefik ingress, health probes, zero-unavailable rolling updates, and restricted Gitea Actions deployment credentials.

Architecture

  • tozd/phppgadmin supplies phpPgAdmin, Nginx, PHP, and PostgreSQL client tools.
  • The image is pinned to an immutable multi-platform manifest digest rather than a mutable tag.
  • default/phppgadmin-config mounts the complete multi-server configuration at /etc/phppgadmin/config.inc.php.
  • A ClusterIP Service exposes TCP port 80 to Traefik.
  • https://phppgadmin.brunner.ninja redirects to the image's native /phppgadmin/ path.
  • One replica uses maxUnavailable: 0, maxSurge: 1, and five seconds of minimum readiness. The old pod remains available until the replacement has passed its HTTP probe continuously for five seconds.
  • CI places the Git commit in a pod-template annotation. This forces a safe rollout when a ConfigMap-only change would otherwise leave a subPath mount unchanged in the running container.
  • Startup, readiness, and liveness probes request /phppgadmin/ over HTTP.

The deployment is stateless. phpPgAdmin does not store database passwords or sessions on a PVC; users authenticate directly to the selected PostgreSQL server. The ConfigMap lists server addresses but contains no credentials.

Why there is no Dockerfile

This repository previously built phpPgAdmin from the Arch User Repository on top of a private base image and pushed the result to the private Quay registry. That duplicated public image storage and made every repository commit rebuild an otherwise unchanged application.

The current deployment instead uses the actively maintained public tozd/phppgadmin image. It supports the same configuration-file mount used here. The pinned image contains phpPgAdmin 7.14.7, which is also the current AUR package version at the time of this repository conversion.

The committed image is:

tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489

It was inspected and smoke-tested before deployment. Pinning prevents an upstream rebuild from silently changing the running software. To upgrade, inspect and test the new upstream digest, replace the image line in phppgadmin.yaml, and merge the change through the normal deployment workflow.

Repository contents

  • phppgadmin.yaml: ConfigMap, Deployment, Service, Ingress, and Traefik root redirect Middleware.
  • install.sh: server-side dry-run, apply, and rollout verification.
  • test.sh: local script and Kubernetes schema validation.
  • ci-deployer.yaml: restricted Gitea Actions ServiceAccount, Role, binding, and revocable token.
  • create-ci-kubeconfig.sh: one-time CI identity bootstrap.
  • .gitea/workflows/deploy.yml: validation on every push and pull request; deployment after successful validation of main.

There is no Helm chart, Kustomize overlay, Argo CD application, private image, or container build pipeline.

Prerequisites

  • Kubernetes and kubectl.
  • An amd64 worker node. The pinned upstream image currently publishes amd64.
  • Traefik with the traefik.io/v1alpha1 Middleware CRD and ingress class traefik.
  • The existing Traefik websecure entrypoint and default *.brunner.ninja TLS certificate.
  • Network connectivity from the pod to every PostgreSQL hostname listed in the ConfigMap.

Another cluster operator can reuse the manifest by changing the namespace, database server list, ingress hostname, ingress class, and redirect Middleware.

Install manually

With an administrator kubeconfig active, run:

./install.sh

The script performs a server-side dry-run, applies all resources, and waits up to ten minutes for the Deployment. Existing resource names are preserved, so the Service keeps its ClusterIP and the change rolls out in place.

Verify both application paths:

curl -I https://phppgadmin.brunner.ninja/
curl -I https://phppgadmin.brunner.ninja/phppgadmin/

The first should redirect permanently and the second should return the phpPgAdmin application.

Configure PostgreSQL servers

Edit config.inc.php inside the ConfigMap in phppgadmin.yaml. Each server entry contains a display name, service DNS name, port, SSL mode, and default database. Do not put PostgreSQL usernames or passwords into the ConfigMap or Git repository.

The file is mounted using subPath, so Kubernetes does not update it inside an already-running pod. The CI workflow replaces the manifest's manual revision annotation with the Git commit SHA before applying it, which safely rolls the pod for every merged configuration change. A manual ./install.sh changes the annotation back to manual and likewise triggers a rollout after a CI deploy.

Validate locally

./test.sh

The test checks both shell scripts and validates the Kubernetes resources with kubeconform. The Traefik Middleware is reported as skipped because kubeconform's standard schema catalog does not contain cluster-specific CRDs; the Kubernetes API validates it during the server-side dry-run.

Automatic deployment with Gitea Actions

The workflow validates every push and pull request. A successful push to main applies the complete phppgadmin.yaml, including ConfigMap, Deployment, Service, Ingress, and Middleware, then polls the Deployment for up to ten minutes.

No image is built or pushed. The workflow requires only one Gitea Actions secret:

  • KUBE_CONFIG_BASE64: the restricted phpPgAdmin deployment kubeconfig, base64-encoded on one line.

One-time CI setup

  1. Run ./install.sh with an administrator identity so every application resource already exists.

  2. Run:

    ./create-ci-kubeconfig.sh
    
  3. Copy only the final base64 line into the Gitea repository Actions secret named KUBE_CONFIG_BASE64.

  4. Protect main, require successful validation before merging, and review manifest and workflow changes.

  5. Push a harmless README change and confirm the deployment job succeeds.

The workflow matches the existing brunner.ninja runner: Kubernetes API URL https://host.containers.internal:6443, with TLS server name 192.168.0.2. Other clusters must change those values.

CI can get, patch, and update only these existing named objects:

  • ConfigMap phppgadmin-config;
  • Deployment and Service phppgadmin;
  • Ingress phppgadmin;
  • Middleware phppgadmin-root-redirect.

It cannot create or delete resources and cannot call the Kubernetes Secrets API. Revoke it by deleting default/phppgadmin-deployer-token and the Gitea secret. Rerunning the wrapper creates a replacement token.

Kubernetes RBAC cannot constrain individual fields inside an allowed Deployment patch. A malicious change merged into main could alter the pod specification. Branch protection and review therefore remain part of the security boundary.

Internet exposure

This instance is reachable from the public internet and presents direct PostgreSQL username/password authentication. Use long, unique database passwords, restrictive PostgreSQL roles and pg_hba.conf rules, TLS for remote database connections, and external rate limiting or access control where appropriate. phpPgAdmin's extra_login_security remains enabled in the committed configuration.

S
Description
Simple docker image to run latest phppgadmin (provided by arch) in my kubernetes cluster. Includes kubernetes config files too
Readme GPL-3.0
62 KiB
Languages
Shell 100%