Skip to content

Add production Docker images - #442

Open
lyrixx wants to merge 2 commits into
mainfrom
prod-images
Open

lyrixx wants to merge 2 commits into
mainfrom
prod-images

Conversation

@lyrixx

@lyrixx lyrixx commented Sep 25, 2026

Copy link
Copy Markdown
Member

Same approach as in jolicode/monologue: the application can now ship as two self-contained, production-ready images.

What's inside

  • php image: php-fpm on the unix socket /var/run/php/php-fpm.sock, code and vendors baked in, APP_ENV=prod, non-root user (uid 1000). It is also the CLI image (migrations, crons, workers).
  • nginx image: official nginx:1.28-alpine, the public/ directory and the site configuration.
  • Built from the new "Production stages" of infrastructure/docker/services/php/Dockerfile, with application/ as the app build context (application/.dockerignore). Each build step (composer install + dump-env, yarn/npm build, AssetMapper compile, cache warmup) only runs if the application needs it, so the bare starter builds too.
  • Shared configuration: php-fpm and nginx configuration now lives in services/php/php/ and services/php/nginx/, used by both the dev frontend container and the production images. Production-only PHP settings are in app-prod.ini.
  • prod castor context: dedicated compose stack (docker-compose.prod.yml: postgres + the two images, no bind mounts, no router) on http://127.0.0.1:8000, e.g. castor start -c prod, castor builder -c prod, castor destroy -c prod.
  • castor docker:push --tag=... also pushes the images, not only their build cache.
  • .github/workflows/build-push.yml: pushes the images to GHCR on main and on tags. Commented out in docker-starter (nothing to deploy), castor init uncomments it.
  • CI: new prod-images job that starts the production stack and checks the response, removed with the rest of .github/ by castor init.

Breaking changes for existing projects

  • The dev frontend container now listens on port 8080 (was 80), and php-fpm on a unix socket instead of 127.0.0.1:9000. Traefik picks the new port automatically, unless a project hardcodes loadbalancer.server.port=80.
  • The nginx configuration moved from frontend/etc/nginx/nginx.conf to nginx/conf.d/default.conf (README cookbooks updated). The static-assets no-cache block was dropped.

Tested locally

  • castor start -c prod on the bare starter: stack healthy, "Environment: prod", /index.php returns 404, /php-fpm-status returns 403, var/ not baked in
  • A throwaway Symfony skeleton + AssetMapper: composer, dump-env, asset-map:compile and cache warmup run; both images healthy and serving
  • castor docker:push -c prod --tag=... --dry-run generates the expected bake file
  • Dev stack unchanged (HTTPS through Traefik, builder, test context)
  • castor init output: only build-push.yml is kept, uncommented, and passes actionlint
  • hadolint, PHPStan, PHP-CS-Fixer

* "php" (php-fpm on a unix socket, code and vendors baked in, APP_ENV=prod,
  also the CLI image) and "nginx" images, built from the new production
  stages of the Dockerfile, running as non-root users. Build steps (composer,
  yarn/npm, AssetMapper, cache warmup) only run when the application needs them
* php-fpm and nginx configuration shared between the dev frontend container
  and the production images (the frontend now listens on port 8080)
* "prod" castor context to build, run and push the images on a dedicated
  compose stack
* castor docker:push --tag also pushes the images
* "Build and push production images" workflow, commented in docker-starter
  and enabled by castor init
* CI job testing the production images
The username was a bare "$": GHCR accepts any username with a
GITHUB_TOKEN, so it worked by accident.
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