Deploy phpPgAdmin with maintained public image
Validate and deploy phpPgAdmin / Validate manifests (push) Successful in 22s
Validate and deploy phpPgAdmin / Deploy to Kubernetes (push) Successful in 40s

This commit is contained in:
2026-08-08 23:02:42 +02:00
parent 35fda2ac59
commit 49f1692e44
9 changed files with 814 additions and 24 deletions
+105
View File
@@ -0,0 +1,105 @@
name: Validate and deploy phpPgAdmin
on:
push:
pull_request:
env:
KUBECONFORM_VERSION: v0.7.0
KUBECTL_VERSION: v1.36.3
KUBERNETES_API: https://host.containers.internal:6443
KUBERNETES_TLS_SERVER_NAME: 192.168.0.2
NAMESPACE: default
jobs:
validate:
name: Validate manifests
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Validate scripts and Kubernetes resources
run: |
sh -n install.sh
bash -n create-ci-kubeconfig.sh
bash -n test.sh
docker run --rm --interactive \
"ghcr.io/yannh/kubeconform:${KUBECONFORM_VERSION}@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c" \
-strict -ignore-missing-schemas -summary < phppgadmin.yaml
docker run --rm --interactive \
"ghcr.io/yannh/kubeconform:${KUBECONFORM_VERSION}@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c" \
-strict -summary < ci-deployer.yaml
deploy:
name: Deploy to Kubernetes
if: gitea.ref == 'refs/heads/main'
needs: validate
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4
- name: Install kubectl
run: |
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl"
curl --fail --silent --show-error --location \
--output "${RUNNER_TEMP}/kubectl.sha256" \
"https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl.sha256"
printf '%s %s\n' "$(cat "${RUNNER_TEMP}/kubectl.sha256")" "${RUNNER_TEMP}/kubectl" | sha256sum --check
chmod 0700 "${RUNNER_TEMP}/kubectl"
- name: Configure Kubernetes access
env:
KUBE_CONFIG_BASE64: ${{ secrets.KUBE_CONFIG_BASE64 }}
run: |
test -n "${KUBE_CONFIG_BASE64}"
printf '%s' "${KUBE_CONFIG_BASE64}" | base64 --decode > "${RUNNER_TEMP}/kubeconfig"
chmod 0600 "${RUNNER_TEMP}/kubeconfig"
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
"${RUNNER_TEMP}/kubectl" config set-cluster cluster \
--server="${KUBERNETES_API}" \
--tls-server-name="${KUBERNETES_TLS_SERVER_NAME}"
- name: Apply and verify
env:
COMMIT_SHA: ${{ gitea.sha }}
run: |
export KUBECONFIG="${RUNNER_TEMP}/kubeconfig"
sed \
"s|deployment.brunner.ninja/revision: manual|deployment.brunner.ninja/revision: ${COMMIT_SHA}|" \
phppgadmin.yaml > "${RUNNER_TEMP}/phppgadmin.yaml"
grep -Fq "deployment.brunner.ninja/revision: ${COMMIT_SHA}" \
"${RUNNER_TEMP}/phppgadmin.yaml"
"${RUNNER_TEMP}/kubectl" apply \
--dry-run=server \
--validate=false \
--filename "${RUNNER_TEMP}/phppgadmin.yaml"
"${RUNNER_TEMP}/kubectl" apply \
--validate=false \
--filename "${RUNNER_TEMP}/phppgadmin.yaml"
for attempt in {1..120}; do
IFS='|' read -r generation observed desired updated ready available unavailable image <<< "$(
"${RUNNER_TEMP}/kubectl" --namespace "${NAMESPACE}" \
get deployment/phppgadmin \
--output=jsonpath='{.metadata.generation}|{.status.observedGeneration}|{.spec.replicas}|{.status.updatedReplicas}|{.status.readyReplicas}|{.status.availableReplicas}|{.status.unavailableReplicas}|{.spec.template.spec.containers[0].image}'
)"
echo "Rollout ${attempt}/120: generation ${observed}/${generation}, replicas ${updated}/${desired} updated, ${ready}/${desired} ready, ${available}/${desired} available, image ${image}"
if [[ "${observed}" == "${generation}" \
&& "${updated}" == "${desired}" \
&& "${ready}" == "${desired}" \
&& "${available}" == "${desired}" \
&& ( -z "${unavailable}" || "${unavailable}" == "0" ) ]]; then
echo "phpPgAdmin deployment successfully rolled out"
exit 0
fi
sleep 5
done
echo "phpPgAdmin rollout did not complete within ten minutes" >&2
exit 1
+7
View File
@@ -0,0 +1,7 @@
# Never commit generated cluster or registry credentials.
kubeconfig
*.kubeconfig
.env
# Local build output.
*.log
-22
View File
@@ -1,22 +0,0 @@
FROM registry.brunner.ninja/feedc0de/php-apache-postgres
RUN pacman -Syu --noconfirm git sudo base-devel \
&& cd /tmp \
&& sudo -u http git clone https://aur.archlinux.org/phppgadmin.git \
&& cd phppgadmin \
&& sudo -u http makepkg -s \
&& pacman -U --noconfirm *.pkg.* \
&& ls \
&& cd .. \
&& rm -Rfv phppgadmin \
&& pacman -Rcnsu --noconfirm git sudo base-devel \
&& pacman -Sc --noconfirm \
&& rm -v /var/log/pacman.log /var/lib/pacman/sync/*
RUN sed -i 's|^DocumentRoot.*|DocumentRoot "/usr/share/webapps/phppgadmin"|' /etc/httpd/conf/httpd.conf \
&& echo '<Directory "/usr/share/webapps/phppgadmin">' >> /etc/httpd/conf/httpd.conf \
&& echo ' DirectoryIndex index.php' >> /etc/httpd/conf/httpd.conf \
&& echo ' AllowOverride All' >> /etc/httpd/conf/httpd.conf \
&& echo ' Options FollowSymlinks' >> /etc/httpd/conf/httpd.conf \
&& echo ' Require all granted' >> /etc/httpd/conf/httpd.conf \
&& echo '</Directory>' >> /etc/httpd/conf/httpd.conf
+184 -2
View File
@@ -1,3 +1,185 @@
# phppgadmin-docker
# phpPgAdmin on Kubernetes
Simple docker image to run latest phppgadmin (provided by arch)
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`](https://gitlab.com/tozd/docker/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:
```text
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:
```sh
./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:
```sh
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
```sh
./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:
```sh
./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.
+88
View File
@@ -0,0 +1,88 @@
# Bootstrap this file once with an administrator identity. CI can update only
# the existing phpPgAdmin resources and cannot call the Kubernetes Secrets API.
apiVersion: v1
kind: ServiceAccount
metadata:
name: phppgadmin-deployer
namespace: default
automountServiceAccountToken: false
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: phppgadmin-deployer
namespace: default
rules:
- apiGroups:
- ""
resources:
- configmaps
resourceNames:
- phppgadmin-config
verbs:
- get
- patch
- update
- apiGroups:
- ""
resources:
- services
resourceNames:
- phppgadmin
verbs:
- get
- patch
- update
- apiGroups:
- apps
resources:
- deployments
resourceNames:
- phppgadmin
verbs:
- get
- patch
- update
- apiGroups:
- networking.k8s.io
resources:
- ingresses
resourceNames:
- phppgadmin
verbs:
- get
- patch
- update
- apiGroups:
- traefik.io
resources:
- middlewares
resourceNames:
- phppgadmin-root-redirect
verbs:
- get
- patch
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: phppgadmin-deployer
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: phppgadmin-deployer
subjects:
- kind: ServiceAccount
name: phppgadmin-deployer
namespace: default
---
apiVersion: v1
kind: Secret
metadata:
name: phppgadmin-deployer-token
namespace: default
annotations:
kubernetes.io/service-account.name: phppgadmin-deployer
type: kubernetes.io/service-account-token
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -euo pipefail
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
namespace=${NAMESPACE:-default}
service_account=${SERVICE_ACCOUNT:-phppgadmin-deployer}
secret=${TOKEN_SECRET:-phppgadmin-deployer-token}
kubectl apply --filename "${project_dir}/ci-deployer.yaml" >&2
for attempt in {1..30}; do
if kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.token}' 2>/dev/null | grep -q .; then
break
fi
if [[ "${attempt}" == 30 ]]; then
echo "Timed out waiting for the service-account token" >&2
exit 1
fi
sleep 1
done
workdir=$(mktemp --directory)
trap 'rm -rf "${workdir}"' EXIT
server=$(kubectl config view --minify --output=jsonpath='{.clusters[0].cluster.server}')
kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.ca\.crt}' | base64 --decode > "${workdir}/ca.crt"
token=$(kubectl --namespace "${namespace}" get secret "${secret}" \
--output=jsonpath='{.data.token}' | base64 --decode)
export KUBECONFIG="${workdir}/config"
kubectl config set-cluster cluster \
--server="${server}" \
--certificate-authority="${workdir}/ca.crt" \
--embed-certs=true >/dev/null
kubectl config set-credentials "${service_account}" --token="${token}" >/dev/null
kubectl config set-context phppgadmin \
--cluster=cluster \
--user="${service_account}" \
--namespace="${namespace}" >/dev/null
kubectl config use-context phppgadmin >/dev/null
echo "Store the following single line as the Gitea Actions secret KUBE_CONFIG_BASE64." >&2
echo "Treat it as a password; do not commit it." >&2
base64 --wrap=0 "${KUBECONFIG}"
printf '\n'
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
set -eu
namespace=default
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
if ! command -v kubectl >/dev/null 2>&1; then
echo "Required command not found: kubectl" >&2
exit 1
fi
kubectl apply --dry-run=server --filename "$script_dir/phppgadmin.yaml"
kubectl apply --filename "$script_dir/phppgadmin.yaml"
kubectl rollout status deployment/phppgadmin \
--namespace "$namespace" \
--timeout=10m
echo
echo "phpPgAdmin is available at https://phppgadmin.brunner.ninja."
+334
View File
@@ -0,0 +1,334 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: phppgadmin-config
namespace: default
labels:
app.kubernetes.io/name: phppgadmin
data:
config.inc.php: |
<?php
/**
* Central phpPgAdmin configuration. As a user you may modify the
* settings here for your particular configuration.
*
* $Id: config.inc.php-dist,v 1.55 2008/02/18 21:10:31 xzilla Exp $
*/
// An example server. Create as many of these as you wish,
// indexed from zero upwards.
// Display name for the server on the login screen
$conf['servers'][0]['desc'] = 'arschrock';
// Hostname or IP address for server. Use '' for UNIX domain socket.
// use 'localhost' for TCP/IP connection on this computer
$conf['servers'][0]['host'] = 'arschrock';
// Database port on server (5432 is the PostgreSQL default)
$conf['servers'][0]['port'] = 5432;
// Database SSL mode
// Possible options: disable, allow, prefer, require
// To require SSL on older servers use option: legacy
// To ignore the SSL mode, use option: unspecified
$conf['servers'][0]['sslmode'] = 'allow';
// Change the default database only if you cannot connect to template1.
// For a PostgreSQL 8.1+ server, you can set this to 'postgres'.
$conf['servers'][0]['defaultdb'] = 'template1';
// Specify the path to the database dump utilities for this server.
// You can set these to '' if no dumper is available.
$conf['servers'][0]['pg_dump_path'] = '/usr/bin/pg_dump';
$conf['servers'][0]['pg_dumpall_path'] = '/usr/bin/pg_dumpall';
$conf['servers'][1]['desc'] = 'nextcloud-postgresql.nextcloud.svc.cluster.local';
$conf['servers'][1]['host'] = 'nextcloud-postgresql.nextcloud.svc.cluster.local';
$conf['servers'][1]['port'] = 5432;
$conf['servers'][1]['sslmode'] = 'allow';
$conf['servers'][1]['defaultdb'] = 'template1';
$conf['servers'][2]['desc'] = 'postgresql.default.svc.cluster.local';
$conf['servers'][2]['host'] = 'postgresql.default.svc.cluster.local';
$conf['servers'][2]['port'] = 5432;
$conf['servers'][2]['sslmode'] = 'allow';
$conf['servers'][2]['defaultdb'] = 'template1';
$conf['servers'][3]['desc'] = 'postgresql.authentik.svc.cluster.local';
$conf['servers'][3]['host'] = 'postgresql.authentik.svc.cluster.local';
$conf['servers'][3]['port'] = 5432;
$conf['servers'][3]['sslmode'] = 'allow';
$conf['servers'][3]['defaultdb'] = 'template1';
$conf['servers'][4]['desc'] = 'postgresql.gramps.svc.cluster.local';
$conf['servers'][4]['host'] = 'postgresql.gramps.svc.cluster.local';
$conf['servers'][4]['port'] = 5432;
$conf['servers'][4]['sslmode'] = 'allow';
$conf['servers'][4]['defaultdb'] = 'template1';
$conf['servers'][5]['desc'] = 'postgresql.matrix.svc.cluster.local';
$conf['servers'][5]['host'] = 'postgresql.matrix.svc.cluster.local';
$conf['servers'][5]['port'] = 5432;
$conf['servers'][5]['sslmode'] = 'allow';
$conf['servers'][5]['defaultdb'] = 'template1';
/* Groups definition */
/* Groups allow administrators to logicaly group servers together under
* group nodes in the left browser tree
*
* The group '0' description
*/
//$conf['srv_groups'][0]['desc'] = 'group one';
/* Add here servers indexes belonging to the group '0' separated by comma */
//$conf['srv_groups'][0]['servers'] = '0,1,2';
/* A server can belong to multi groups. Here server 1 is referenced in both
* 'group one' and 'group two'*/
//$conf['srv_groups'][1]['desc'] = 'group two';
//$conf['srv_groups'][1]['servers'] = '3,1';
/* A group can be nested in one or more existing groups using the 'parents'
* parameter. Here the group 'group three' contains only one server and will
* appear as a subgroup in both 'group one' and 'group two':
*/
//$conf['srv_groups'][2]['desc'] = 'group three';
//$conf['srv_groups'][2]['servers'] = '4';
//$conf['srv_groups'][2]['parents'] = '0,1';
/* Warning: Only groups with no parents appears at the root of the tree. */
/* You can apply specific theme depending on servers, users and databases
* The priority order is :
* * the theme defined for a server
* * the theme defined for a database apply over the server one
* * the theme defined for a user apply over the database one
*/
/* Example for servers */
//$conf['servers'][0]['theme']['default'] = 'default';
/* Example for users */
//$conf['servers'][0]['theme']['user']['specific_user'] = 'default';
/* Example for databases */
//$conf['servers'][0]['theme']['db']['specific_db'] = 'default';
// Default language. E.g.: 'english', 'polish', etc. See lang/ directory
// for all possibilities. If you specify 'auto' (the default) it will use
// your browser preference.
$conf['default_lang'] = 'auto';
// If extra session security is true, then PHP's session cookies will have
// SameSite cookie flags set to prevent CSRF attacks. If you're using
// auto-start sessions, autostarted sessions will be destroyed and
// restarted with SameSite on. If this this solution is not acceptable for
// your situation, you will need to either turn off auot-start sessions, or
// turn off secure sessions. Versions of PHP below 7.3 do not have access
// to this feature and will be vulnerable to CSRF attacks.
$conf['extra_session_security'] = true;
// AutoComplete uses AJAX interaction to list foreign key values
// on insert fields. It currently only works on single column
// foreign keys. You can choose one of the following values:
// 'default on' enables AutoComplete and turns it on by default.
// 'default off' enables AutoComplete but turns it off by default.
// 'disable' disables AutoComplete.
$conf['autocomplete'] = 'default on';
// If extra login security is true, then logins via phpPgAdmin with no
// password or certain usernames (pgsql, postgres, root, administrator)
// will be denied. Only set this false once you have read the FAQ and
// understand how to change PostgreSQL's pg_hba.conf to enable
// passworded local connections.
$conf['extra_login_security'] = true;
// Only show owned databases?
// Note: This will simply hide other databases in the list - this does
// not in any way prevent your users from seeing other database by
// other means. (e.g. Run 'SELECT * FROM pg_database' in the SQL area.)
$conf['owned_only'] = false;
// Display comments on objects? Comments are a good way of documenting
// a database, but they do take up space in the interface.
$conf['show_comments'] = true;
// Display "advanced" objects? Setting this to true will show
// aggregates, types, operators, operator classes, conversions,
// languages and casts in phpPgAdmin. These objects are rarely
// administered and can clutter the interface.
$conf['show_advanced'] = false;
// Display "system" objects?
$conf['show_system'] = false;
// Minimum length users can set their password to.
$conf['min_password_length'] = 1;
// Width of the left frame in pixels (object browser)
$conf['left_width'] = 200;
// Which look & feel theme to use
$conf['theme'] = 'default';
// Show OIDs when browsing tables?
// Only supported in versions <=11
$conf['show_oids'] = false;
// Max rows to show on a page when browsing record sets
$conf['max_rows'] = 30;
// Max chars of each field to display by default in browse mode
$conf['max_chars'] = 50;
// Send XHTML strict headers?
$conf['use_xhtml_strict'] = false;
// Base URL for PostgreSQL documentation.
// '%s', if present, will be replaced with the PostgreSQL version
// (e.g. 8.4 )
$conf['help_base'] = 'http://www.postgresql.org/docs/%s/interactive/';
// Configuration for ajax scripts
// Time in seconds. If set to 0, refreshing data using ajax will be disabled (locks and activity pages)
$conf['ajax_refresh'] = 3;
/** Plugins management
* Add plugin names to the following array to activate them
* Example:
* $conf['plugins'] = array(
* 'Example',
* 'Slony'
* );
*/
$conf['plugins'] = array();
/*****************************************
* Don't modify anything below this line *
*****************************************/
$conf['version'] = 19;
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: phppgadmin
namespace: default
labels:
app: phppgadmin
app.kubernetes.io/name: phppgadmin
spec:
replicas: 1
revisionHistoryLimit: 3
minReadySeconds: 5
progressDeadlineSeconds: 600
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: phppgadmin
template:
metadata:
annotations:
deployment.brunner.ninja/revision: manual
labels:
app: phppgadmin
app.kubernetes.io/name: phppgadmin
spec:
automountServiceAccountToken: false
terminationGracePeriodSeconds: 30
containers:
- name: phppgadmin
# Public, maintained image pinned to the verified multi-platform
# manifest digest published on 2026-08-08.
image: tozd/phppgadmin@sha256:4ee690768d54b0c057dbc4ed5c1baa152c1a5ef0ebd6eb12f1e310e545eb4489
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
protocol: TCP
startupProbe:
httpGet:
path: /phppgadmin/
port: http
periodSeconds: 2
timeoutSeconds: 2
failureThreshold: 60
readinessProbe:
httpGet:
path: /phppgadmin/
port: http
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 2
livenessProbe:
httpGet:
path: /phppgadmin/
port: http
periodSeconds: 15
timeoutSeconds: 3
failureThreshold: 3
volumeMounts:
- name: phppgadmin-config
mountPath: /etc/phppgadmin/config.inc.php
subPath: config.inc.php
volumes:
- name: phppgadmin-config
configMap:
name: phppgadmin-config
---
apiVersion: v1
kind: Service
metadata:
name: phppgadmin
namespace: default
labels:
app: phppgadmin
app.kubernetes.io/name: phppgadmin
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app: phppgadmin
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: phppgadmin
namespace: default
labels:
app.kubernetes.io/name: phppgadmin
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.middlewares: default-phppgadmin-root-redirect@kubernetescrd
spec:
ingressClassName: traefik
rules:
- host: phppgadmin.brunner.ninja
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: phppgadmin
port:
name: http
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: phppgadmin-root-redirect
namespace: default
labels:
app.kubernetes.io/name: phppgadmin
spec:
redirectRegex:
regex: ^https://phppgadmin\.brunner\.ninja/?$
replacement: https://phppgadmin.brunner.ninja/phppgadmin/
permanent: true
Executable
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -euo pipefail
project_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
sh -n "${project_dir}/install.sh"
bash -n "${project_dir}/create-ci-kubeconfig.sh"
validate_file() {
local manifest=$1
if command -v kubeconform >/dev/null 2>&1; then
kubeconform -strict -ignore-missing-schemas -summary "${manifest}"
elif command -v docker >/dev/null 2>&1; then
docker run --rm --interactive \
ghcr.io/yannh/kubeconform:v0.7.0@sha256:85dbef6b4b312b99133decc9c6fc9495e9fc5f92293d4ff3b7e1b30f5611823c \
-strict -ignore-missing-schemas -summary < "${manifest}"
else
echo "Required command not found: install kubeconform or Docker" >&2
exit 1
fi
}
validate_file "${project_dir}/phppgadmin.yaml"
validate_file "${project_dir}/ci-deployer.yaml"
echo "phpPgAdmin scripts and Kubernetes resources are valid."