Docker Runtime VM

docker-runtime-vm — the standalone private runtime host for developer apps that ship through the GitLab → Jenkins → Trivy → Nexus chain but don't target OpenShift.

docker-runtime-vm is the standalone private runtime host for developer apps. It is the runtime endpoint of the current app-dev path: GitLab → Jenkins → Trivy → Nexus → docker-runtime. Per the 2026-05-09 user direction (project_app_dev_direction.md), OpenShift application delivery is paused; new apps target this VM until that scope reopens.

What it is

PropertyValue
VMdocker-runtime-vm
Private FQDNdocker-runtime-vm.sub.comptech-lab.com
Private aliasdocker-runtime.sub.comptech-lab.com
Networkprivate lab br30 /24
Operator SSH userze
Deploy SSH userdocker-deploy (sudo-limited to the deploy wrapper)
Runtime root/opt/docker-runtime
Public exposureNone by default (per-app decision)

The VM intentionally has no public HAProxy route. Each application’s exposure is a separate, explicit decision; the default is “private only.”

Installed runtime tools

ToolVersion
Docker Engine29.1.3
Docker Compose2.40.3
Docker Buildx0.30.1
Podman4.9.3
Buildah1.33.7
Skopeo1.13.3
Ansible core2.16.3
Terraform1.15.2

Both Docker Engine and Podman are present. App runtime uses Docker Compose; Podman/Buildah/Skopeo support build-time work and registry interaction for ad-hoc operations.

Why a standalone Docker VM

The OpenShift app delivery path has been paused (project_app_dev_direction.md). The Docker runtime VM is the current production-capable target for app delivery, because:

  • The GitLab → Jenkins → Trivy → Nexus pipeline already produces immutable, scanned, digest-pinned images.
  • Pulling those images and running them with Docker Compose is straightforward and well-understood.
  • It decouples app development from OpenShift cluster readiness gates (ESO not yet wired, namespace baseline undecided, cross-cluster pull-secret wiring open).
  • When the OpenShift path reopens, the same immutable images can be deployed to a cluster instead — the supply-chain side doesn’t change.

Image source

Application images come from app-registry.apps.sub.comptech-lab.com, by immutable tag or digest. Pulls from mirror-registry.* are explicitly forbidden — that registry is platform install content only.

# Correct
app-registry.apps.sub.comptech-lab.com/smoke/readiness-probe:build-8

# Wrong (platform install content)
mirror-registry.apps.sub.comptech-lab.com/smoke/readiness-probe:build-8

See the app-registry endpoint page for the contract.

docker-deploy wrapper

Routine deploys do not use the ze SSH account. They use a least-privilege docker-deploy system user whose sudo is restricted to a single wrapper:

/usr/local/sbin/docker-runtime-deploy *

Wrapper interface:

sudo /usr/local/sbin/docker-runtime-deploy deploy <app>
sudo /usr/local/sbin/docker-runtime-deploy status <app>
sudo /usr/local/sbin/docker-runtime-deploy logs <app>
sudo /usr/local/sbin/docker-runtime-deploy health <app>
sudo /usr/local/sbin/docker-runtime-deploy rollback <app> <release>
sudo /usr/local/sbin/docker-runtime-deploy validate <app>

The wrapper is what Jenkins (when triggered) and operators use. The shape of a routine deploy:

ssh -i "$DEPLOY_KEY" \
  docker-deploy@docker-runtime-vm.sub.comptech-lab.com \
  'sudo /usr/local/sbin/docker-runtime-deploy status readiness-probe'

Rollback was validated 2026-05-09 with:

sudo /usr/local/sbin/docker-runtime-deploy rollback readiness-probe 20260509T133018Z

Runtime layout

PathUse
/opt/docker-runtime/appsCompose files or service definitions, one directory per app
/opt/docker-runtime/envSecret-bearing runtime env files (approved custody only)
/opt/docker-runtime/releasesRelease snapshots for rollback (timestamped)
/opt/docker-runtime/logsRuntime log handoff location for host-retained logs
/opt/docker-runtime/healthOne health URL file per app
/opt/docker-runtime/templates/appStarter Compose template
/etc/docker-runtime/bootstrap-readyMarker created by cloud-init after baseline install

Runtime env files are not committed to Git. They are the env-file bridge per project_app_dev_direction.md — used until Vault/ESO is reopened.

Current app port allocations

AppHost portNotes
demo-smoke18080Developer smoke/demo allocation
readiness-probe19080Starter app health path

New allocations must be recorded in app release evidence and in this connection-details runbook. Per the current handoff: do not create public hostnames or HAProxy frontends without explicit reopening of app exposure by the platform owner.

Starter app

The validated starter is readiness-probe:

Image: app-registry.apps.sub.comptech-lab.com/smoke/readiness-probe:build-8
Health: http://localhost:19080/health   (private to the runtime VM)
Deploy: sudo /usr/local/sbin/docker-runtime-deploy deploy readiness-probe

Validation

# DNS
dig @<lab-dns> docker-runtime-vm.sub.comptech-lab.com A +short

# Tool versions
ssh ze@docker-runtime-vm.sub.comptech-lab.com 'docker info --format "{{.ServerVersion}} {{.Driver}}"'
ssh ze@docker-runtime-vm.sub.comptech-lab.com 'docker compose version'
ssh ze@docker-runtime-vm.sub.comptech-lab.com 'ansible --version | head -n1'
ssh ze@docker-runtime-vm.sub.comptech-lab.com 'terraform version | head -n1'

A scripted validation lives at opp-full-plat/scripts/rebuild/docker-runtime/validate-docker-runtime-vm.sh.

Operational guidance

  • No public exposure by default. Per-app decision; require explicit reopening before adding HAProxy routes or public DNS.
  • Always use the wrapper for deploys. Don’t SSH as ze and docker compose up directly; the wrapper enforces the release snapshot + rollback discipline.
  • Pin app images by immutable tag or digest. Don’t reference :latest in Compose files.
  • Don’t pull from mirror-registry.*. That’s platform install content; not for app runtime.

Failure modes

Symptom: deploy fails with “registry not authorized”

Root cause. /opt/docker-runtime/env/<app>.env is missing or has stale Nexus credentials.

Fix. Refresh the env file with current nexus-jenkinsbot-equivalent runtime creds. Verify with docker login from the VM.

Prevention. Track credential rotation; update env files as part of rotation.

Symptom: rollback target release is not present

Root cause. Release directory was cleaned up (manual or by a too-aggressive retention script).

Fix. Rebuild the previous image tag and redeploy. Future rollbacks require keeping the release snapshot.

Prevention. Don’t delete release snapshots without a tracked decision. Retention is a deliberate policy, not a routine cleanup.

Symptom: app port conflict

Root cause. Two apps allocated to the same host port.

Fix. Pick a different host port; update the app’s Compose file; record in the allocation table.

Prevention. Maintain the port allocation table as a single source of truth.

References

  • opp-full-plat/connection-details/docker-runtime-vm.md — runbook for the live service.
  • opp-full-plat/plans/disconnected-rebuild/environments/dc-lab/docker-runtime-vm-plan.md — VM plan.
  • app-registry endpoint — image source contract.

Last reviewed: 2026-05-11