| Service | Version | Address |
|---|---|---|
| PHP | 8.5 | — |
| PostgreSQL | 18 | 127.0.0.1:5432 |
| MySQL | 8.4 (LTS) | 127.0.0.1:3306 |
| Redis | 8 (alpine) | 127.0.0.1:6379 |
| Mailpit | latest | SMTP 127.0.0.1:1025 · Web UI localhost:8025 |
| Caddy | latest (via mise) | serves the app on localhost:8000 |
All services share the app container's network namespace
(network_mode: "service:app" in .devcontainer/compose.yml), so they are reachable
at 127.0.0.1 on their default ports — no hostname resolution needed.
Upgrading from PostgreSQL 17? The
postgres-datavolume must be recreated — PostgreSQL major versions cannot read older data directories:docker volume rm symfony-poly-stack_postgres-data(PostgreSQL 18+ images mount the volume at/var/lib/postgresql, not/var/lib/postgresql/data; data lives in a major-version subdirectory.)
The Dockerfile only provides PHP-FPM and its extensions. Everything else comes from
mise (.mise.toml): Bun, gh, Caddy, Dagger, Claude Code, Codex, and Composer (via the
github:composer/composer backend). .devcontainer/setup.sh activates mise in your
shell and runs mise install.
Caddy (Caddyfile) serves public/ on :8000 and forwards PHP requests to PHP-FPM
over a Unix socket (/tmp/php-fpm.sock, configured in
.devcontainer/config/php-fpm/www.conf). FPM health endpoints are proxied at
/fpm-ping and /fpm-status.
No script rewrites .env. Instead:
.mise.toml[env]templates computeDEFAULT_URI(https://$CODESPACE_NAME-8000.$GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN) and setTRUSTED_PROXIES=*whenCODESPACE_NAMEis present, falling back to the values in.env(orhttp://localhost:8000) locally.framework.trusted_proxiesconsumesTRUSTED_PROXIES;router.default_uriconsumesDEFAULT_URI(used for URL generation in CLI/scheduler contexts).- The Caddyfile forces
X-Forwarded-Proto: httpsbehind the Codespaces TLS-terminating proxy (@is_codespacematcher).
This works because php-fpm runs with clear_env = no, and real environment variables
always beat .env files in Symfony's Dotenv.