What this prompt does
This prompt outputs two files in one shot — docker-compose.yml and docker-compose.prod.yml — bridging the gap between a working local stack and a hardened production deployment. The split-file design is deliberate: the base file holds service definitions, named volumes, and network declarations; the prod overlay tightens resource limits, enables log drivers, and wires in SSL termination without duplicating every key.
What makes the template structurally useful is that it forces you to declare [network_zones] before generating anything. That single decision drives correct network isolation — your database tier never shares a network segment with your public-facing reverse proxy, and the generated configs enforce that boundary explicitly. Health checks are required for all services, which matters because depends_on without condition: service_healthy is effectively decorative in production; containers report as ready before their internal processes are accepting connections.
The [persistent_data] variable ensures volume declarations are intentional, not an afterthought. You name exactly what needs to survive container restarts — database files, uploaded assets, TLS certificates — and the prompt scopes those named volumes correctly across both compose files.
When to use it
- Standing up a Laravel or Django app with separate web, worker, and database containers for the first time in a production environment.
- Migrating a monolith to a containerized stack and needing a reference architecture with real network isolation, not a tutorial toy with everything on the default bridge network.
- Adding a reverse proxy (Nginx, Traefik, or Caddy) in front of existing services and needing SSL termination configured from the start rather than bolted on later.
- Replacing ad-hoc
docker runcommands with a reproducible, version-controlled Compose setup for staging or production. - Setting up structured logging via
[log_driver]—json-file,loki,fluentd— before wiring into a log aggregation stack, so the logging config is part of the service definition rather than a retrofit.
Example output
For app_type: Laravel API, services: nginx, php-fpm, mysql, redis, network_zones: frontend / backend, persistent_data: mysql-data, redis-data, storage, ssl_setup: Nginx with Let's Encrypt, log_driver: json-file:
# docker-compose.yml (excerpt)
services:
nginx:
image: nginx:1.25-alpine
networks: [frontend]
depends_on:
php-fpm:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost/health || exit 1"]
interval: 30s
retries: 3
php-fpm:
build: .
networks: [frontend, backend]
healthcheck:
test: ["CMD-SHELL", "SCRIPT_NAME=/ping SCRIPT_FILENAME=/ping REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 || exit 1"]
interval: 15s
retries: 3
mysql:
image: mysql:8.0
networks: [backend]
volumes:
- mysql-data:/var/lib/mysql
env_file: .env
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u$$MYSQL_USER", "-p$$MYSQL_PASSWORD"]
interval: 20s
retries: 5
redis:
image: redis:7-alpine
networks: [backend]
volumes:
- redis-data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
retries: 3
volumes:
mysql-data:
redis-data:
storage:
networks:
frontend:
backend:
# docker-compose.prod.yml (excerpt)
services:
php-fpm:
deploy:
resources:
limits:
cpus: "1.0"
memory: 512M
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
mysql:
deploy:
resources:
limits:
cpus: "2.0"
memory: 1G
logging:
driver: json-file
options:
max-size: "10m"
max-file: "5"
Pro tips
- Use
wgetorcurlonly if the image ships it. Alpine-based images (nginx:alpine,redis:alpine) do not includecurlby default. Usewget -qO-for nginx healthchecks or installcurlexplicitly in your Dockerfile. UsingCMD curlon a vanilla Alpine image produces a silent always-failing healthcheck with no error indocker inspect. - php-fpm has no built-in ping binary. The standard approach is either
cgi-fcgi(available in thefcgipackage) to hit thepm.status_pathendpoint, or a small shell script mounted into the container. Do not referencephp-fpm-healthcheckwithout installing the renatomefi script — it does not exist in upstream php images. - Use
service_healthyindepends_on, not justservice_started. The prompt generates health checks for all services — but they only gate startup ordering if you explicitly setcondition: service_healthy. The defaultcondition: service_startedfires as soon as the container process launches, before the service inside it is ready. - Pair
docker-compose.prod.ymlwith aMakefiletarget. The two-file output is most useful when you standardize invocation:docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d. Encode that in a Makefile so the wrong file can never accidentally be run in production by a new team member. - Specify your actual log driver upfront, not
json-fileas a placeholder. If you are shipping to Grafana Loki, includelokiand yourloki-urlin the[log_driver]variable. The generated logging block will include correct plugin options rather than generic settings you have to rewrite after the fact.