Skip to content

feat(infra): a UAT bring-up that pulls its images instead of building them on the host - #638

Merged
beyondnetPeru merged 2 commits into
mainfrom
feat/uat-compose-vps
Aug 22, 2026
Merged

feat(infra): a UAT bring-up that pulls its images instead of building them on the host#638
beyondnetPeru merged 2 commits into
mainfrom
feat/uat-compose-vps

Conversation

@beyondnetPeru

Copy link
Copy Markdown
Contributor

What

Adds product/infra/docker-compose.uat.yml — the nine-service stack for the UAT environment on the VPS, pulling published images rather than building them.

Why

docker-compose.fullstack.yml cannot deploy to the VPS, for two independent reasons:

  1. It builds the Tracker from a sibling checkout (../../../evolith_tracker/src). Coolify clones one repository; that path does not exist there.
  2. It builds at all. The host has 2 vCPU, so building means compiling and serving on the same two cores — the environment degrades for the very people it exists for, precisely while it is being updated.

Both properties are correct for a developer machine. Neither survives the move to a host a client reaches.

What changes for the deployed environment

fullstack (laptop) uat (VPS)
Images built locally, cross-repo context pulled from GHCR
ASPNETCORE_ENVIRONMENT Development (DevBypass auth) Staging
CoreApi__MockFallback false false, and stated as load-bearing
Credentials development values inline ${VAR:?} — missing value fails the bring-up
Memory unbounded mem_limit + --max-old-space-size per service

The last row is not tidiness. Node sizes its heap from the host's RAM unless told otherwise, so four Node services on a 7.8 GB box each plan for memory they cannot have; mem_limit alone caps the container without telling the runtime, which turns a GC into an OOM kill.

Verified

  • docker compose config validates.
  • Omitting a secret fails with required variable EVOLITH_API_KEY is missing a value, rather than starting.
  • ghcr.io/beyondnetcode/evolith-tracker-web:latest pulls anonymously; the three Core images are private and need a registry credential on the host (out of scope here, it is server configuration).

Not in this PR

Creating the Coolify resource, the DNS record for the Tracker web surface, and the registry login on the host — all server-side steps that no repository change can carry.

🤖 Generated with Claude Code

… them on the host

`docker-compose.fullstack.yml` cannot deploy. Two properties make it correct on a
developer machine and unusable on the VPS: it builds the Tracker from a sibling
checkout (`../../../evolith_tracker/src`), a path that does not exist where
Coolify clones one repository; and it builds at all, on a 2 vCPU host that would
then be compiling and serving on the same two cores — degrading the environment
for the very people it exists for, precisely while it is updated.

This file builds nothing. Every service names an image CI already published to
GHCR (`ci-cd.yml` here, `images.yml` in the Tracker, both on merge to main), so
what a client exercises is the artifact CI verified rather than a rebuild of the
same source, and a rollback is a pull rather than a rebuild.

Three settings differ from the fullstack file because a host a client can reach
is not a laptop:

- `ASPNETCORE_ENVIRONMENT` is `Staging`, not `Development`, which is what enables
  the Tracker's DevBypass auth scheme.
- `CoreApi__MockFallback` is off. Left on, an unreachable core-api is answered
  with fabricated evaluations — in UAT, a client believing they saw the product
  work.
- `TRACKER_API_UPSTREAM` overrides the image default, which names a Kubernetes
  service that does not resolve on this network.

Credentials use `${VAR:?}` rather than defaults: a missing value fails the
bring-up instead of quietly shipping a development password.

Memory limits are declared per service and are not decoration. Node sizes its
heap from the HOST's RAM unless told otherwise, so four Node services on a 7.8 GB
box each plan for memory they cannot have. `mem_limit` caps the container and
`--max-old-space-size` tells the runtime about the cap; without the second the
process meets the OOM killer instead of a GC.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner August 21, 2026 23:27
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

Copy link
Copy Markdown

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 0
  • New EN files needing ES translation: 0

Repository Coverage

Metric Value
Total EN files 525
Total ES files 495
Paired files 0
Coverage 0%

Good: All EN changes have ES counterparts.


Generated by GitHub Actions

…oting

Measured on the target VPS, not reasoned about. With `start_period: 40s`,
`core-api` did not answer in time, compose aborted every service that depends on
it — agent-runtime, tracker-api and through it the whole Tracker — and the
bring-up ended with four of nine services up and

    dependency failed to start: container evolith-uat-core-api-1 is unhealthy

`core-api` then reported healthy on its own and was still healthy fourteen
minutes later. The boot was never the defect; the deadline was. On 2 vCPU this
service starts NestJS and loads the compiled policy bundle before it serves
/health, and the Tracker API runs behind EF migrations on the same two cores.

Both now allow 180s with a 15s interval and 5 retries. Re-run with the images
already pulled: nine of nine up, every healthcheck green, and the full chain
tracker-web -> tracker-api -> core-api answering 200.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru merged commit 21f3059 into main Aug 22, 2026
35 checks passed
@beyondnetPeru
beyondnetPeru deleted the feat/uat-compose-vps branch August 22, 2026 02:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant