Skip to content

Technical guide · Overview

Page status: Active

NutsNews VPS Runtime Environment Isolation

NutsNews has separate production and staging runtime identities. Production

Visual overview

Primary diagram

System map

NutsNews has separate production and staging runtime identities. Production

Render the repository-owned system map when you need it.

Diagram is not rendered yet.

View as text
NutsNews VPS Runtime Environment Isolation

NutsNews has separate production and staging runtime identities. Production

flowchart TB
  accTitle: NutsNews VPS Runtime Environment Isolation
  accDescr {
    NutsNews has separate production and staging runtime identities. Production
  }
  A["NutsNews VPS Runtime Environment Isolation"] --> B["NUTSNEWS_VPS_RUNTIME_ENVIRONMENT_ISOLATION.md"]
  B --> C["Auto-generated placeholder diagram"]

NutsNews VPS Runtime Environment Isolation

Fullscreen diagram view.

NutsNews VPS Runtime Environment Isolation

Section titled “NutsNews VPS Runtime Environment Isolation”

NutsNews has separate production and staging runtime identities. Production retains its existing route and reviewed image. The staging runtime can be deployed by its fixed candidate workflow, while its hostname/access boundary remains opt-in and not live until the separately approved #120 applies.

Use this guide when reviewing the NutsNews Compose/Ansible app layer and the fixed staging deployment path for nutsnews-infra #117. It does not itself authorize a live staging apply.

Production and staging use different Docker projects, files, networks, containers, and writable cache volumes. Production keeps its current identity, so the staging deployment path cannot move the live route or swap its image digest.

Staging has a fixed-purpose, approval-gated deployment path. Its reviewed hostname and access layer are documented separately and remain inactive before approval. The #118 capacity contract defines its hard CPU, memory, PID, log, disk-preflight, and test-traffic budget. See Immutable Staging Deployment for the candidate trust boundary and separate live-apply evidence.

Any configured application image must be a reviewed immutable reference:

ghcr.io/ramideltoro/nutsnews@sha256:<64 lowercase hexadecimal characters>

Mutable tags, including latest, fail before Compose materializes a runtime.

Runtime itemProductionStaging
Compose projectnutsnews-appnutsnews-staging
Containernutsnews-appnutsnews-app-staging
Docker networknutsnews-edge-v6nutsnews-edge-staging
Caddy accessExisting route/network unchangedAdditive attachment and protected hostname only when #120 is enabled
App directory/opt/nutsnews/apps/nutsnews/opt/nutsnews/apps/nutsnews-staging
Env file/etc/nutsnews/nutsnews-app.env/etc/nutsnews/nutsnews-staging-app.env
Release manifest/opt/nutsnews/ops/apps/production/release.json/opt/nutsnews/ops/apps/staging/release.json
Apply marker/opt/nutsnews/ops/last-app-apply.json/opt/nutsnews/ops/apps/staging/last-apply.json
Last-known-good record/opt/nutsnews/ops/apps/production/last-known-good.json/opt/nutsnews/ops/apps/staging/last-known-good.json
Writable cache volumenutsnews-app-cachenutsnews-app-staging-cache
StateExisting protected production behaviorFixed-purpose staging-vps; hostname uses separate approved #120 apply

The existing protected workflow continues to consume reviewed production release state and production runtime secrets from NUTSNEWS_APP_ENVS_JSON. The separate staging workflow consumes only the staging-vps Environment after its no-secret preflight succeeds. It cannot configure a staging route, TLS, public access boundary, or production promotion.

Ansible allows exactly two environment keys: production and staging. The default selected environment is production. Each selected environment gets its own Compose file, root-only env file, release manifest, apply marker, last-known-good record, project name, Docker network name and alias, and named writable cache volume.

The per-environment Compose command passes --project-name explicitly. Its --remove-orphans scope is therefore that project only; the task has no down, stop, or remove command against another environment. Regression coverage renders both Compose configurations, proves a staging-only Ansible check does not target production application paths or containers, and verifies that a staging-only input change leaves production runtime renders byte-for-byte unchanged.

Caddy has a narrower trust boundary than the runtime model: it joins only nutsnews-edge-v6, the production network. Its app route templates read only the production configuration. The staging network is not attached to Caddy, so a staging service-name collision cannot become the production upstream.

On a successful enabled deployment, the environment release manifest becomes that environment’s last-known-good record. Release manifests and markers record the environment, project, image/review metadata, container, network, cache volume, and route state—never application secret values.

flowchart LR
caddy["Caddy\nproduction network only"] --> prodNet["nutsnews-edge-v6"]
prodNet --> prodApp["nutsnews-app\nproject: nutsnews-app"]
stageNet["nutsnews-edge-staging\nnot attached to Caddy"] --> stageApp["nutsnews-app-staging\nproject: nutsnews-staging\nGitOps-only"]
prodApp --> prodState["production env, manifest, marker,\nlast-known-good, cache volume"]
stageApp --> stageState["staging env, manifest, marker,\nlast-known-good, cache volume"]
budget["#118 fixed resource budget\nseparate approval required"] -. constrains .-> stageApp
  1. Use only the immutable staging workflow. Do not add a manual deploy, hostname, TLS, access boundary, data access, Caddy route, or production promotion behavior outside its reviewed GitOps contract.
  2. Never use a mutable image tag. Review the exact GHCR digest through normal GitOps review before the environment can materialize.
  3. Use protected check mode before any approved production apply. Do not SSH to mutate runtime files or invoke Compose directly on the VPS.
  4. Treat manifests and last-known-good records as deployment evidence, not as a substitute for the later qualification and promotion gates.
  5. Treat staging containers with no active qualification metadata as inactive staging, not production health. The GitOps-managed staging auto-idle service is responsible for stopping the staging app/access projects after the grace window; operators should not run ad hoc docker compose down over SSH.

From ramideltoro/nutsnews-infra:

Terminal window
python3 ansible/tests/validate_nutsnews_environment_isolation.py
cd ansible && ansible-playbook playbooks/bootstrap.yml --syntax-check
cd ansible && ansible-lint .

The isolation regression renders both Compose configurations, checks all identity/path separation, rejects mutable inputs for both environment names, and checks that staging-only rendering cannot change production artifacts.

Issue #118 records a same-host go decision with enforceable application CPU, memory, PID, and log ceilings. The fixed-purpose deployment workflow consumes that contract, but no live staging apply has occurred merely by merging its infrastructure PR. A separately approved dispatch and the completion evidence in Immutable Staging Deployment remain required. The reviewed TLS/access design is in Staging Access And Credential Boundary, but protected provider/VPS applies and live verification remain pending.