Skip to content

chore(infra): Docker, CI/CD, and server-start optimization (Phase 9.7) - #18

Merged
withrvr merged 2 commits into
masterfrom
phase-9.7-infra-optimization
Sep 14, 2026
Merged

withrvr merged 2 commits into
masterfrom
phase-9.7-infra-optimization

Conversation

@withrvr

@withrvr withrvr commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Summary

Infrastructure-only pass per the Phase 9.7 spec: Docker image build, Docker Compose, CI/CD workflows, server startup, and test execution. No application logic, API/websocket behavior, or test content changed.

  • Docker image: python:3.14 -> python:3.14-slim (measured 2.13GB -> 1.14GB, -46%), non-root app user, configurable WEB_CONCURRENCY worker count (shell-expansion default, not a persistent ENV, so it can't leak into the dev fastapi dev command). Fixed the healthcheck regression the slim base caused (no curl -> python3/urllib.request). Hardened .dockerignore (root, backend/).
  • Compose: compose.deploy.yml's redis service was missing restart: always -- added. Named volumes, condition: service_healthy healthchecks, and Compose Watch were already current best practice, confirmed and left alone.
  • CI: added concurrency groups to 4 workflows that lacked them, dorny/paths-filter gating to test-backend.yml/test-docker-compose.yml (mirroring playwright.yml's existing pattern), and GHA-backed Docker layer caching (cache_from/cache_to: type=gha + COMPOSE_BAKE=1) targeting Playwright's 4x-redundant per-shard Docker build. Bumped stale bun-version pins to match local dev. Confirmed via real CI-log inspection that uv's dependency cache was already working correctly -- left alone.
  • Server startup: readiness and graceful shutdown were already correct, confirmed via direct code reading, no changes. Documented the Gunicorn-vs-bare-Uvicorn question as a deliberate deferral (no real deploy target yet).
  • Test execution: checked pytest-xdist (real cross-worker collision risk given the session-scoped shared-DB fixture, not applied) and prestart.sh's wait-for-DB logic (already covered by Compose/CI's own health-gated orchestration, not applied).

Full reasoning, including every area that was already fine and deliberately left alone, is in docs/development/ci-cd-and-docker.md.

Test plan

  • Backend suite (753 tests) green
  • ruff check / ruff format --check / mypy app / ty check app clean
  • prek run --all-files clean (incl. zizmor on every workflow edit)
  • Rebuilt slim backend image locally, verified docker compose up -d --wait backend adminer reports healthy
  • Verified COMPOSE_BAKE=1 docker compose build backend/playwright succeed locally with the new cache_from/cache_to fields
  • Verified merged compose.yml + compose.deploy.yml config shows redis: restart: always
  • Confirm on the next CI run that the GHA Docker layer cache actually produces faster shard-2-4 builds (documented as unverifiable locally, per the spec's "measured, not assumed" instruction)

https://claude.ai/code/session_01WACAxnAd7w5uk12Y58EHqz

Slim backend image by 46% (2.13GB -> 1.14GB), run it as a non-root user,
and make its worker count configurable via WEB_CONCURRENCY. Add GHA-backed
Docker layer caching, concurrency groups, and path-filter gating to CI
workflows that lacked them, and fix compose.deploy.yml's missing
restart:always on redis. Every researched area (Docker build, Compose,
CI caching, server startup, test execution) gets a documented verdict in
docs/development/ci-cd-and-docker.md, including the ones left unchanged
because they already matched current best practice.

Claude-Session: https://claude.ai/code/session_01WACAxnAd7w5uk12Y58EHqz
- Stop chowning the whole /app tree at the end of the Dockerfile: it
  forced overlay2 to copy up every earlier layer, giving back ~430MB of
  the slim-base size win (1.14GB -> 707MB once removed). Chown each
  layer's own output at creation time instead (COPY --chown, chown
  inside the same RUN as uv sync).
- Quote the ${WEB_CONCURRENCY:-4} expansion in the Dockerfile CMD.
- Add docker/setup-buildx-action (driver: docker-container) before the
  docker compose build steps that rely on cache_from/cache_to: type=gha
  -- the default docker driver silently ignores that cache backend.
- Add pyproject.toml/uv.lock/bun.lock to the changes-job path filters in
  test-backend.yml, test-docker-compose.yml, and playwright.yml, so a
  dependency-only bump can no longer skip its own test job.

Claude-Session: https://claude.ai/code/session_01WACAxnAd7w5uk12Y58EHqz
@withrvr
withrvr merged commit 35d845b into master Sep 14, 2026
18 checks passed
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