Spec 10 — Deployment version dashboard
Purpose
“What version of pdf-service is running on vps-i1 right now?” currently requires SSH + docker inspect. After this spec, it’s a Grafana panel.
The mechanism is standard: every image we build labels itself with the git SHA. cAdvisor exposes those labels as Prometheus series. A simple panel surfaces them.
Rulebook
- Every custom-built image carries OCI labels:
org.opencontainers.image.revision(SHA),org.opencontainers.image.version(semver if applicable),org.opencontainers.image.created(ISO timestamp). - CI sets the labels. Add as build args in
.github/workflows/build-*.yml. - No PRs to bump versions in compose files — Renovate (spec 08) handles upstream; for our own images we use
:latesthere intentionally and rely on the dashboard to show what’s actually running.
Implementation plan
- Update
infra-src/gotenberg/pdf-service/Dockerfileandinfra-src/claude-proxy/Dockerfile(andmonitoring/exporters/queue-exporter/Dockerfile) withARG GIT_SHAandLABEL org.opencontainers.image.revision=$GIT_SHA. - Update build workflows to pass
--build-arg GIT_SHA=${{ github.sha }}. - Add Grafana panel “Container versions” in a new dashboard
monitoring/grafana/provisioning/dashboards/versions.jsonusing cAdvisor’scontainer_label_org_opencontainers_image_revisionmetric.
Acceptance criteria
-
docker inspect pdf-service | grep revisionshows the current commit SHA - Grafana “Container versions” dashboard lists every container with its SHA + image creation date
- After a rebuild + redeploy, the dashboard reflects the new SHA within 1 min
Cost impact
0 €.
Back-out plan
Revert label additions. Dashboard panel can stay (shows empty if labels missing).
Risks / open questions
None. Standard practice.
Bootstrap
There are currently no CI workflows that build our custom images — they’re built manually on the VPS (via docker compose build or one-off docker build). To populate the Container versions dashboard, each image must be rebuilt once with the new build-args after this PR merges.
Images we build & where they run
| Image (compose service) | Dockerfile | Built on |
|---|---|---|
monitoring-pdf-service-1 | infra-src/gotenberg/pdf-service/Dockerfile | vps-i1 |
monitoring-queue-exporter-1 | monitoring/exporters/queue-exporter/Dockerfile | vps-i1 |
report-scheduler (oneshot) | infra-src/report-scheduler/Dockerfile | vps-i1 |
claude-proxyon vps-h1 runs as a Node script under PM2 / systemd — there is no Dockerfile. No labels apply.
Rebuild commands
Run on each affected VPS after this PR merges. The args derive the SHA + ISO timestamp from the local checkout of /opt/p24-infra, so make sure git pull was run there first.
# On vps-i1 (IONOS — 217.154.82.162)
cd /opt/p24-infra && git pull
export GIT_SHA=$(git rev-parse HEAD)
export BUILD_DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
export IMAGE_VERSION=main
cd /opt/p24-infra/monitoring
docker compose build \
--build-arg GIT_SHA=$GIT_SHA \
--build-arg BUILD_DATE=$BUILD_DATE \
--build-arg IMAGE_VERSION=$IMAGE_VERSION \
pdf-service queue-exporter
docker compose up -d pdf-service queue-exporter
# report-scheduler (oneshot — built into its own image)
cd /opt/p24-infra/infra-src/report-scheduler
docker build \
--build-arg GIT_SHA=$GIT_SHA \
--build-arg BUILD_DATE=$BUILD_DATE \
--build-arg IMAGE_VERSION=$IMAGE_VERSION \
-t report-scheduler:latest .Verify
ssh root@217.154.82.162 'docker inspect monitoring-pdf-service-1 | grep revision'→ expect the git SHA from the rebuild.- Grafana → “Container versions” dashboard → table populates within ~60 s (cAdvisor scrape interval).
- Repeat verification after each future rebuild: same commands, new SHA.
Adding a new image later
When a future spec introduces another image we build:
- Add the
ARG GIT_SHA / BUILD_DATE / IMAGE_VERSION+LABEL org.opencontainers.image.*block right after the finalFROMin the new Dockerfile. - Pass the three
--build-argflags wherever it’s built (manual on VPS, or in a future GitHub Actions workflow — pass${{ github.sha }},${{ github.event.repository.updated_at }},${{ github.ref_name }}). - No dashboard change needed — cAdvisor + the existing PromQL surface it automatically.