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

  1. 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).
  2. CI sets the labels. Add as build args in .github/workflows/build-*.yml.
  3. No PRs to bump versions in compose files — Renovate (spec 08) handles upstream; for our own images we use :latest here intentionally and rely on the dashboard to show what’s actually running.

Implementation plan

  1. Update infra-src/gotenberg/pdf-service/Dockerfile and infra-src/claude-proxy/Dockerfile (and monitoring/exporters/queue-exporter/Dockerfile) with ARG GIT_SHA and LABEL org.opencontainers.image.revision=$GIT_SHA.
  2. Update build workflows to pass --build-arg GIT_SHA=${{ github.sha }}.
  3. Add Grafana panel “Container versions” in a new dashboard monitoring/grafana/provisioning/dashboards/versions.json using cAdvisor’s container_label_org_opencontainers_image_revision metric.

Acceptance criteria

  • docker inspect pdf-service | grep revision shows 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)DockerfileBuilt on
monitoring-pdf-service-1infra-src/gotenberg/pdf-service/Dockerfilevps-i1
monitoring-queue-exporter-1monitoring/exporters/queue-exporter/Dockerfilevps-i1
report-scheduler (oneshot)infra-src/report-scheduler/Dockerfilevps-i1

claude-proxy on 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

  1. ssh root@217.154.82.162 'docker inspect monitoring-pdf-service-1 | grep revision' → expect the git SHA from the rebuild.
  2. Grafana → “Container versions” dashboard → table populates within ~60 s (cAdvisor scrape interval).
  3. Repeat verification after each future rebuild: same commands, new SHA.

Adding a new image later

When a future spec introduces another image we build:

  1. Add the ARG GIT_SHA / BUILD_DATE / IMAGE_VERSION + LABEL org.opencontainers.image.* block right after the final FROM in the new Dockerfile.
  2. Pass the three --build-arg flags 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 }}).
  3. No dashboard change needed — cAdvisor + the existing PromQL surface it automatically.