Skip to content

feat(docker): two-stage production image on Debian bookworm + openapi.json#65

Open
t0kubetsu wants to merge 6 commits into
mainfrom
dev
Open

feat(docker): two-stage production image on Debian bookworm + openapi.json#65
t0kubetsu wants to merge 6 commits into
mainfrom
dev

Conversation

@t0kubetsu
Copy link
Copy Markdown
Contributor

@t0kubetsu t0kubetsu commented May 11, 2026

Summary

Closes #64

Delivers all four artifacts required by the issue:

  • Dockerfile — two-stage build (builderruntime) on Debian Bookworm (python:3.13-bookworm / python:3.13-slim-bookworm). Stage 1 installs the Python virtualenv at /opt/venv and Ansible collections at /usr/share/ansible/collections. Stage 2 copies both into a slim runtime image and runs uvicorn.
  • docker-compose.yml — production-oriented; no source bind-mounts; application code is baked into the image at build time. Image tag driven by IMAGE_NAME env var (default ghcr.io/range42/range42-backend-api:latest).
  • openapi.json — current API spec exported from the live FastAPI route table, committed at repo root for Kong gateway bootstrap.
  • README.md — new Docker: Build & Publish section with GHCR login, build, tag, push commands, and openapi.json regeneration snippet.

Why Debian Bookworm instead of Ubuntu Server LTS

Benjamin's original spec called for Ubuntu Server LTS. We diverged deliberately, using python:3.13-bookworm — the Docker Official Image, which uses upstream CPython compiled from source and does not ship the EXTERNALLY-MANAGED marker. Dependencies are installed into a virtualenv (/opt/venv), which bypasses PEP 668 on any distro.

Clarification: Debian 12 itself also enforces PEP 668 on its apt-installed Python, same as Ubuntu. The image works correctly because of the Docker Official Image base and the virtualenv, not because Debian avoids PEP 668. The preference for Debian is that the official Python Docker images are Debian-based, making it the more predictable and well-tested foundation.

Test plan

  • docker compose build completes without errors
  • docker compose up starts the container; curl http://localhost:8000/docs/openapi.json returns HTTP 200
  • Health check passes within 10 s start period
  • openapi.json at repo root matches /docs/openapi.json served by the running container
  • Image can be pushed: IMAGE_NAME=ghcr.io/range42/range42-backend-api:dev docker compose push

pparage and others added 6 commits March 20, 2026 08:49
When rc != 0, the JSON response now includes:
- error: the fatal/FAILED line from Ansible output
- log_multiline: last 10 lines for context

Previously returned only {rc: 2, result: []} with no explanation.
Now returns {rc: 2, result: [], error: "...", log_multiline: [...]}
The CORS regex env var gets mangled by systemd EnvironmentFile
backslash parsing. Adding the lab subnet to the hardcoded default
avoids that issue entirely.
…api.json

- Dockerfile: rewrite to two-stage build (builder → runtime) using
  python:3.13-bookworm / python:3.13-slim-bookworm; venv installed to
  /opt/venv in builder and copied to runtime stage; Ansible collections
  copied from /usr/share/ansible/collections; correct ANSIBLE_COLLECTIONS_PATH
  env var for ansible-core 2.19.1
- docker-compose.yml: production-oriented; no source bind-mounts; image tag
  driven by IMAGE_NAME env var (default ghcr.io/range42/range42-backend-api:latest)
- openapi.json: export current API spec for Kong gateway bootstrap
- README.md: add Docker Build & Publish section with GHCR login, build,
  tag and push commands; add openapi.json regeneration snippet
- .dockerignore: exclude openapi.json from build context

Closes #64
…ce, compose fixes

- Dockerfile: add ARG UID/GID (default 1000) and non-root user `range42`;
  chown /app to range42; set HOME=/home/range42; add USER directive before
  EXPOSE/HEALTHCHECK/CMD
- docker-compose.yml: pass UID/GID build args; update SSH key volume mount
  path to /home/range42/.ssh; restore API_BACKEND_PUBLIC_PLAYBOOKS_DIR
  default to /app/ (regression fix); add commented inventory bind-mount
  override; annotate VAULT_PASSWORD as dev-only
- README.md: document non-root user, UID/GID build args, SSH known_hosts
  requirement, and VAULT_PASSWORD_FILE as the production vault secret pattern
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.

Task: Package and publish deployer-backend-api as a production Docker image

2 participants