init
This commit is contained in:
+162
@@ -0,0 +1,162 @@
|
||||
# Using AI Coding Agents with Dev Containers
|
||||
|
||||
This project ships with a [Dev Container](https://containers.dev/) configuration that enables
|
||||
AI coding agents to run autonomously inside a sandboxed environment with network-level restrictions.
|
||||
|
||||
[Claude Code](https://claude.ai/claude-code) is pre-installed and configured out of the box,
|
||||
but the setup also works with other agents such as [OpenAI Codex CLI](https://github.com/openai/codex)
|
||||
and [opencode](https://opencode.ai).
|
||||
|
||||
This setup is ideal for letting AI agents work on your Symfony project autonomously
|
||||
while ensuring they cannot reach arbitrary internet hosts.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- [Docker Desktop](https://www.docker.com/products/docker-desktop/) (or any Docker-compatible runtime)
|
||||
- [Visual Studio Code](https://code.visualstudio.com/) with the [Dev Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) extension
|
||||
- A valid subscription or API key for the agent you want to use
|
||||
|
||||
## Quick Start
|
||||
|
||||
1. Open the project in Visual Studio Code.
|
||||
2. When prompted "Reopen in Container", click **Reopen in Container**.
|
||||
Alternatively, open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`) and run
|
||||
**Dev Containers: Reopen in Container**.
|
||||
3. Wait for the container to build and start. On each container start, the
|
||||
`postStartCommand` configures the firewall automatically.
|
||||
4. Claude Code is pre-installed and configured in YOLO mode — open the Claude Code
|
||||
panel in Visual Studio Code or run `claude` in the integrated terminal to start using it.
|
||||
|
||||
That's it. Claude Code will run without permission prompts, and the firewall ensures
|
||||
network access is restricted to only the necessary services.
|
||||
|
||||
## What Is YOLO Mode?
|
||||
|
||||
YOLO mode (also known as "bypass permissions" mode) allows Claude Code to execute
|
||||
commands, edit files, and perform actions without asking for confirmation at each step.
|
||||
This dramatically speeds up autonomous coding workflows.
|
||||
|
||||
The Dev Container configuration enables this via two Visual Studio Code settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"claudeCode.allowDangerouslySkipPermissions": true,
|
||||
"claudeCode.initialPermissionMode": "bypassPermissions"
|
||||
}
|
||||
```
|
||||
|
||||
## Network Sandboxing
|
||||
|
||||
Running an AI agent with full autonomy requires guardrails. The Dev Container includes
|
||||
a firewall script (`.devcontainer/init-firewall.sh`) that locks down outbound network
|
||||
access using `iptables` and `ipset`. Only the following destinations are allowed:
|
||||
|
||||
| Destination | Reason |
|
||||
| ------------------------------------------------- | ------------------------------- |
|
||||
| GitHub (`github.com`, `api.github.com`) | Git operations, API access |
|
||||
| Anthropic (`anthropic.com`) | Claude Code backend |
|
||||
| npm registry (`registry.npmjs.org`) | Node.js dependencies |
|
||||
| Packagist (`packagist.org`, `repo.packagist.org`) | PHP/Composer dependencies |
|
||||
| Visual Studio Code Marketplace | Extension downloads |
|
||||
| Sentry, Statsig | Telemetry (used by Claude Code) |
|
||||
| Host gateway IP | Communication with Docker host |
|
||||
|
||||
All other outbound connections are **rejected**. The firewall uses
|
||||
[dnsmasq](https://thekelleys.org.uk/dnsmasq/doc.html) to dynamically resolve
|
||||
and whitelist IPs for allowed domains, handling CDN IP rotation gracefully.
|
||||
|
||||
Inbound connections from the host gateway IP are allowed on all ports,
|
||||
and ports 80, 443 (TCP), and 443 (UDP/HTTP3) are open to any source
|
||||
so you can access your Symfony app from the host browser.
|
||||
|
||||
## Customizing the Allowed Domains
|
||||
|
||||
To allow additional domains (e.g., a private registry or API), edit
|
||||
`.devcontainer/init-firewall.sh` and add them to the `ipset` line in the
|
||||
dnsmasq configuration section:
|
||||
|
||||
```bash
|
||||
# Domains are '/'-separated, ending with the ipset name
|
||||
ipset=/github.com/anthropic.com/your-domain.com/allowed-domains
|
||||
```
|
||||
|
||||
Then rebuild the Dev Container for the changes to take effect.
|
||||
|
||||
## Using Other Agents
|
||||
|
||||
The Dev Container's network sandbox and project context (`.devcontainer/AGENTS.md`) work
|
||||
with any AI coding agent. You just need to install the agent and whitelist the domains it
|
||||
needs to reach.
|
||||
|
||||
### OpenAI Codex CLI
|
||||
|
||||
1. Add the OpenAI API domain to the firewall allowlist in `.devcontainer/init-firewall.sh`
|
||||
(see [Customizing the Allowed Domains](#customizing-the-allowed-domains)):
|
||||
|
||||
```bash
|
||||
ipset=/.../api.openai.com/allowed-domains
|
||||
```
|
||||
|
||||
2. Install and run Codex inside the container:
|
||||
|
||||
```console
|
||||
npm install -g @openai/codex
|
||||
export OPENAI_API_KEY=your-key
|
||||
codex --full-auto
|
||||
```
|
||||
|
||||
### opencode
|
||||
|
||||
1. Add the required API domain to the firewall allowlist (e.g., `api.anthropic.com`,
|
||||
`api.openai.com`, or your provider's domain).
|
||||
|
||||
2. Install and run opencode inside the container:
|
||||
|
||||
```console
|
||||
curl -fsSL https://opencode.ai/install | bash
|
||||
opencode
|
||||
```
|
||||
|
||||
### Other Agents
|
||||
|
||||
For any other agent, follow the same pattern:
|
||||
|
||||
1. Add the agent's API domain(s) to the firewall allowlist.
|
||||
2. Install the agent inside the container.
|
||||
3. Run it — the `.devcontainer/AGENTS.md` file provides project context
|
||||
to agents that support the convention.
|
||||
|
||||
## Using Without Visual Studio Code
|
||||
|
||||
The Dev Container configuration works with any tool that supports the
|
||||
[Dev Container specification](https://containers.dev/), including:
|
||||
|
||||
- [Dev Container CLI](https://github.com/devcontainers/cli) (`devcontainer up`)
|
||||
- [GitHub Codespaces](https://github.com/features/codespaces)
|
||||
- JetBrains IDEs (with the Dev Containers plugin)
|
||||
|
||||
To use Claude Code from the terminal inside the container:
|
||||
|
||||
```console
|
||||
claude
|
||||
```
|
||||
|
||||
To start directly in YOLO mode from the CLI:
|
||||
|
||||
```console
|
||||
claude --dangerously-skip-permissions
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Firewall blocks a required domain
|
||||
|
||||
If your agent or Composer/npm fails to reach a service, check the firewall
|
||||
logs and add the domain to the dnsmasq allowlist as described above.
|
||||
|
||||
### Container fails to start
|
||||
|
||||
Ensure Docker is running and that you have allocated enough resources
|
||||
(at least 2 GB of RAM for the container). The firewall setup requires
|
||||
`NET_ADMIN` capability, which the Dev Container configures automatically
|
||||
via Docker Compose.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Using Alpine Linux Instead of Debian
|
||||
|
||||
By default, Symfony Docker uses Debian-based FrankenPHP Docker images.
|
||||
This is the recommended solution.
|
||||
|
||||
Alternatively, it's possible to use Alpine-based images, which are smaller but
|
||||
are known to be slower, and have several known issues.
|
||||
|
||||
To switch to Alpine-based images, apply the following changes to the `Dockerfile`:
|
||||
|
||||
<!-- markdownlint-disable MD010 -->
|
||||
|
||||
```diff
|
||||
-FROM dunglas/frankenphp:1-php8.5 AS frankenphp_upstream
|
||||
+FROM dunglas/frankenphp:1-php8.5-alpine AS frankenphp_upstream
|
||||
|
||||
-SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
|
||||
+SHELL ["/bin/ash", "-euxo", "-c"]
|
||||
|
||||
-# hadolint ignore=DL3008
|
||||
-RUN <<-EOF
|
||||
- apt-get update
|
||||
- apt-get install -y --no-install-recommends \
|
||||
- file \
|
||||
- git
|
||||
+# hadolint ignore=DL3018
|
||||
+RUN <<-EOF
|
||||
+ apk add --no-cache \
|
||||
+ file \
|
||||
+ git
|
||||
install-php-extensions \
|
||||
|
||||
-# hadolint ignore=DL3008,SC3054,DL4006
|
||||
-RUN <<-'EOF'
|
||||
- apt-get update
|
||||
- apt-get install -y --no-install-recommends libtree
|
||||
+# hadolint ignore=DL3018,SC3054,DL4006
|
||||
+RUN <<-'EOF'
|
||||
+ apk add --no-cache libtree
|
||||
mkdir -p /tmp/libs
|
||||
- BINARIES=(frankenphp php file)
|
||||
- for target in $(printf '%s\n' "${BINARIES[@]}" | xargs -I{} which {}) \
|
||||
+ BINARIES="frankenphp php file"
|
||||
+ for target in $(printf '%s\n' $BINARIES | xargs -I{} which {}) \
|
||||
|
||||
- libtree -pv "$target" 2>/dev/null | grep -oP '(?:── )\K/\S+(?= \[)' | while IFS= read -r lib; do
|
||||
+ libtree -pv "$target" 2>/dev/null | sed -n 's/.*── \(\/[^ ]*\) \[.*/\1/p' | while IFS= read -r lib; do
|
||||
|
||||
- rm -rf /var/lib/apt/lists/*
|
||||
|
||||
-FROM debian:13-slim AS frankenphp_prod
|
||||
+FROM alpine:3 AS frankenphp_prod
|
||||
|
||||
-SHELL ["/bin/bash", "-euxo", "pipefail", "-c"]
|
||||
+SHELL ["/bin/ash", "-euxo", "-c"]
|
||||
|
||||
-COPY --from=frankenphp_prod_builder /usr/lib/file/magic.mgc /usr/lib/file/magic.mgc
|
||||
+COPY --from=frankenphp_prod_builder /usr/share/misc/magic.mgc /usr/share/misc/magic.mgc
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD010 -->
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 126 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 159 KiB |
@@ -0,0 +1,81 @@
|
||||
# Installing on an Existing Project
|
||||
|
||||
It's also possible to use Symfony Docker with existing projects!
|
||||
|
||||
First, [download this skeleton](https://github.com/dunglas/symfony-docker).
|
||||
|
||||
If you cloned the Git repository, be sure to not copy the `.git` directory
|
||||
to prevent conflicts with the `.git` directory already in your existing project.
|
||||
|
||||
You can copy the contents of the repository using Git and tar.
|
||||
This will not contain `.git` or any uncommitted changes.
|
||||
|
||||
```console
|
||||
git archive --format=tar HEAD | tar -xC my-existing-project/
|
||||
```
|
||||
|
||||
If you downloaded the skeleton as a ZIP you can just copy the extracted files:
|
||||
|
||||
```console
|
||||
cp -Rp symfony-docker/. my-existing-project/
|
||||
```
|
||||
|
||||
Enable the Docker support of Symfony Flex:
|
||||
|
||||
```console
|
||||
composer config --json extra.symfony.docker 'true'
|
||||
```
|
||||
|
||||
The [worker mode of FrankenPHP](https://frankenphp.dev/docs/worker/) is enabled by default.
|
||||
To use it with Symfony ≤ 7.3, install the FrankenPHP runtime:
|
||||
|
||||
```console
|
||||
composer require runtime/frankenphp-symfony
|
||||
```
|
||||
|
||||
Then update worker configuration:
|
||||
|
||||
<!-- markdownlint-disable MD010 -->
|
||||
|
||||
```diff
|
||||
worker {
|
||||
file ./public/index.php
|
||||
+ env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
|
||||
{$FRANKENPHP_WORKER_CONFIG}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD010 -->
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> You can disable worker mode by removing the `worker` directive from the `frankenphp`
|
||||
> global option in your `Caddyfile`.
|
||||
|
||||
Re-execute the recipes to update the Docker-related files according to
|
||||
the packages you use:
|
||||
|
||||
```console
|
||||
rm symfony.lock
|
||||
composer recipes:install --force --verbose
|
||||
```
|
||||
|
||||
Double-check the changes, revert the changes that you don't want to keep:
|
||||
|
||||
```console
|
||||
git diff
|
||||
```
|
||||
|
||||
Build the Docker images:
|
||||
|
||||
```console
|
||||
docker compose build --pull --no-cache
|
||||
```
|
||||
|
||||
Start the project!
|
||||
|
||||
```console
|
||||
docker compose up --wait
|
||||
```
|
||||
|
||||
Browse `https://localhost`, your Docker configuration is ready!
|
||||
@@ -0,0 +1,25 @@
|
||||
# Support for Extra Services
|
||||
|
||||
Symfony Docker is extensible.
|
||||
When you install a compatible Composer package using Symfony Flex,
|
||||
the recipe will automatically modify the `Dockerfile` and `compose.yaml` files
|
||||
to fulfill the requirements of this package.
|
||||
|
||||
The currently supported packages are:
|
||||
|
||||
- `symfony/orm-pack`: install a PostgreSQL service
|
||||
- `symfony/mercure-bundle`: use the Mercure.rocks module shipped with Caddy
|
||||
- `symfony/panther`: install Chromium and its drivers
|
||||
- `symfony/mailer`: install a Mailpit service
|
||||
- `blackfireio/blackfire-symfony-meta`: install a Blackfire service
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> If a recipe modifies the Dockerfile, the container needs to be rebuilt.
|
||||
|
||||
<!-- -->
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> We recommend that you use the `composer require` command inside the container
|
||||
> in development mode so that recipes can be applied correctly.
|
||||
@@ -0,0 +1,129 @@
|
||||
# Makefile
|
||||
|
||||
Here is a Makefile template. It provides some shortcuts for the most common tasks.
|
||||
To use it, create a new `Makefile` file at the root of your project. Copy/paste
|
||||
the content in the template section. To view all the available commands, run `make`.
|
||||
|
||||
For example, in the [getting started section](/README.md#getting-started), the
|
||||
`docker compose` commands could be replaced by:
|
||||
|
||||
1. Run `make build` to build fresh images
|
||||
2. Run `make up` (detached mode without logs)
|
||||
3. Run `make down` to stop the Docker containers
|
||||
|
||||
Of course, this template is basic for now. But, as your application is growing,
|
||||
you will probably want to add some targets like running your tests as described
|
||||
in [the Symfony book](https://symfony.com/doc/current/the-fast-track/en/17-tests.html#automating-your-workflow-with-a-makefile).
|
||||
You can also find a more complete example in this [snippet](https://www.strangebuzz.com/en/snippets/the-perfect-makefile-for-symfony).
|
||||
|
||||
If you want to run make from within the `php` container, in the [Dockerfile](/Dockerfile),
|
||||
add:
|
||||
|
||||
<!-- markdownlint-disable MD010 -->
|
||||
|
||||
```diff
|
||||
gettext \
|
||||
git \
|
||||
+ make \
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD010 -->
|
||||
|
||||
And rebuild the PHP image.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> If you are using Windows, you have to install [chocolatey.org](https://chocolatey.org/)
|
||||
> or [Cygwin](http://cygwin.com) to use the `make` command.
|
||||
>
|
||||
> Check out this [StackOverflow question](https://stackoverflow.com/q/2532234/633864)
|
||||
> for more explanations.
|
||||
|
||||
## The Template
|
||||
|
||||
<!-- markdownlint-disable MD010 MD013 -->
|
||||
|
||||
```Makefile
|
||||
# Executables (local)
|
||||
DOCKER_COMP = docker compose
|
||||
|
||||
# Docker containers
|
||||
PHP_CONT = $(DOCKER_COMP) exec php
|
||||
|
||||
# Executables
|
||||
PHP = $(PHP_CONT) php
|
||||
COMPOSER = $(PHP_CONT) composer
|
||||
SYMFONY = $(PHP) bin/console
|
||||
|
||||
# Misc
|
||||
.DEFAULT_GOAL = help
|
||||
.PHONY : help build up start down logs sh composer vendor sf cc test
|
||||
|
||||
## —— 🎵 🐳 The Symfony Docker Makefile 🐳 🎵 ——————————————————————————————————
|
||||
help: ## Outputs this help screen
|
||||
@grep -E '(^[a-zA-Z0-9\./_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}{printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/'
|
||||
|
||||
## —— Docker 🐳 ————————————————————————————————————————————————————————————————
|
||||
build: ## Builds the Docker images
|
||||
@$(DOCKER_COMP) build --pull --no-cache
|
||||
|
||||
up: ## Start the docker hub in detached mode (no logs)
|
||||
@$(DOCKER_COMP) up --detach
|
||||
|
||||
start: build up ## Build and start the containers
|
||||
|
||||
down: ## Stop the docker hub
|
||||
@$(DOCKER_COMP) down --remove-orphans
|
||||
|
||||
logs: ## Show live logs
|
||||
@$(DOCKER_COMP) logs --tail=0 --follow
|
||||
|
||||
sh: ## Connect to the FrankenPHP container
|
||||
@$(PHP_CONT) sh
|
||||
|
||||
bash: ## Connect to the FrankenPHP container via bash so up and down arrows go to previous commands
|
||||
@$(PHP_CONT) bash
|
||||
|
||||
test: ## Start tests with phpunit, pass the parameter "c=" to add options to phpunit, example: make test c="--group e2e --stop-on-failure"
|
||||
@$(eval c ?=)
|
||||
@$(DOCKER_COMP) exec -e APP_ENV=test php bin/phpunit $(c)
|
||||
|
||||
|
||||
## —— Composer 🧙 ——————————————————————————————————————————————————————————————
|
||||
composer: ## Run composer, pass the parameter "c=" to run a given command, example: make composer c='req symfony/orm-pack'
|
||||
@$(eval c ?=)
|
||||
@$(COMPOSER) $(c)
|
||||
|
||||
vendor: ## Install vendors according to the current composer.lock file
|
||||
vendor: c=install --prefer-dist --no-dev --no-progress --no-scripts --no-interaction
|
||||
vendor: composer
|
||||
|
||||
## —— Symfony 🎵 ———————————————————————————————————————————————————————————————
|
||||
sf: ## List all Symfony commands or pass the parameter "c=" to run a given command, example: make sf c=about
|
||||
@$(eval c ?=)
|
||||
@$(SYMFONY) $(c)
|
||||
|
||||
cc: c=c:c ## Clear the cache
|
||||
cc: sf
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD010 MD013 -->
|
||||
|
||||
## Adding and Modifying Jobs
|
||||
|
||||
Make sure to add this configuration to the [.editorconfig](/.editorconfig) file,
|
||||
so that it forces your editor to use tabs instead of spaces.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Makefiles are not compatible with spaces by default.
|
||||
|
||||
```.editorconfig
|
||||
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
```
|
||||
|
||||
If you still want to use space, you can configure the prefix in the Makefile itself.
|
||||
See [this answer on Stack Exchange](https://retrocomputing.stackexchange.com/a/20303).
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
# Using MySQL
|
||||
|
||||
The Docker configuration of this repository is extensible thanks to Flex recipes.
|
||||
By default, the recipe installs PostgreSQL.
|
||||
|
||||
If you prefer to work with MySQL, follow these steps:
|
||||
|
||||
First, install the `symfony/orm-pack` package as described:
|
||||
|
||||
```console
|
||||
docker compose exec php composer req symfony/orm-pack
|
||||
```
|
||||
|
||||
## Docker Configuration
|
||||
|
||||
Change the database image to use MySQL instead of PostgreSQL in `compose.yaml`:
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
```diff
|
||||
###> doctrine/doctrine-bundle ###
|
||||
- image: postgres:${POSTGRES_VERSION:-16}-alpine
|
||||
+ image: mysql:${MYSQL_VERSION:-8.0.32}
|
||||
environment:
|
||||
- POSTGRES_DB: ${POSTGRES_DB:-app}
|
||||
+ MYSQL_DATABASE: ${MYSQL_DATABASE:-app}
|
||||
# You should definitely change the password in production
|
||||
+ MYSQL_RANDOM_ROOT_PASSWORD: "true"
|
||||
- POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
|
||||
+ MYSQL_PASSWORD: ${MYSQL_PASSWORD:-!ChangeMe!}
|
||||
- POSTGRES_USER: ${POSTGRES_USER:-app}
|
||||
+ MYSQL_USER: ${MYSQL_USER:-app}
|
||||
healthcheck:
|
||||
- test: ["CMD", "pg_isready", "-d", "${POSTGRES_DB:-app}", "-U", "${POSTGRES_USER:-app}"]
|
||||
+ test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 60s
|
||||
volumes:
|
||||
- - database_data:/var/lib/postgresql/data:rw
|
||||
+ - database_data:/var/lib/mysql:rw
|
||||
# You may use a bind-mounted host directory instead, so that it is harder to accidentally remove the volume and lose all your data!
|
||||
- # - ./docker/db/data:/var/lib/postgresql/data:rw
|
||||
+ # - ./docker/db/data:/var/lib/mysql:rw
|
||||
###< doctrine/doctrine-bundle ###
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
Depending on the database configuration,
|
||||
modify the environment in the same file at `services.php.environment.DATABASE_URL`:
|
||||
|
||||
```yaml
|
||||
DATABASE_URL: mysql://${MYSQL_USER:-app}:${MYSQL_PASSWORD:-!ChangeMe!}@database:3306/${MYSQL_DATABASE:-app}?serverVersion=${MYSQL_VERSION:-8.0.32}&charset=${MYSQL_CHARSET:-utf8mb4}
|
||||
```
|
||||
|
||||
Since we changed the port, we also have to define this in the `compose.override.yaml`:
|
||||
|
||||
```diff
|
||||
###> doctrine/doctrine-bundle ###
|
||||
database:
|
||||
ports:
|
||||
- - "5432"
|
||||
+ - "3306"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
```
|
||||
|
||||
Last but not least, we need to install the MySQL driver in `Dockerfile`:
|
||||
|
||||
```diff
|
||||
###> doctrine/doctrine-bundle ###
|
||||
-RUN install-php-extensions pdo_pgsql
|
||||
+RUN install-php-extensions pdo_mysql
|
||||
###< doctrine/doctrine-bundle ###
|
||||
```
|
||||
|
||||
## Change Environment
|
||||
|
||||
Change the database configuration in `.env`:
|
||||
|
||||
```dotenv
|
||||
DATABASE_URL=mysql://${MYSQL_USER:-app}:${MYSQL_PASSWORD:-!ChangeMe!}@database:3306/${MYSQL_DATABASE:-app}?serverVersion=${MYSQL_VERSION:-8.0.32}&charset=${MYSQL_CHARSET:-utf8mb4}
|
||||
```
|
||||
|
||||
## Final steps
|
||||
|
||||
Rebuild the Docker environment:
|
||||
|
||||
```console
|
||||
docker compose down --remove-orphans && docker compose build --pull --no-cache
|
||||
```
|
||||
|
||||
Start the services:
|
||||
|
||||
```console
|
||||
docker compose up --wait
|
||||
```
|
||||
|
||||
Test your setup:
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
```console
|
||||
docker compose exec php bin/console dbal:run-sql -q "SELECT 1" && echo "OK" || echo "Connection is not working"
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
# Docker Build Options
|
||||
|
||||
You can customize the Docker build process using these environment variables.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> All Symfony-specific environment variables are used only if no `composer.json`
|
||||
> file is found in the project directory.
|
||||
|
||||
## Selecting a Specific Symfony Version
|
||||
|
||||
Use the `SYMFONY_VERSION` environment variable to select a specific Symfony version.
|
||||
|
||||
For instance, use the following command to install Symfony 6.4:
|
||||
|
||||
On Linux:
|
||||
|
||||
```console
|
||||
SYMFONY_VERSION=6.4.* docker compose up --wait
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```console
|
||||
set SYMFONY_VERSION=6.4.* && docker compose up --wait&set SYMFONY_VERSION=
|
||||
```
|
||||
|
||||
<!-- markdownlint-disable MD010 -->
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> If you're using Symfony 7.3 or earlier with FrankenPHP in worker mode, you also need to follow these steps
|
||||
>
|
||||
> ```console
|
||||
> composer require runtime/frankenphp-symfony
|
||||
> ```
|
||||
>
|
||||
> Add `env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime` in the `frankenphp/Caddyfile` in the `worker` section.
|
||||
>
|
||||
> ```diff
|
||||
> worker {
|
||||
> file ./public/index.php
|
||||
> + env APP_RUNTIME Runtime\FrankenPhpSymfony\Runtime
|
||||
> {$FRANKENPHP_WORKER_CONFIG}
|
||||
> }
|
||||
> ```
|
||||
|
||||
<!-- markdownlint-enable MD010 -->
|
||||
|
||||
## Installing Development Versions of Symfony
|
||||
|
||||
To install a non-stable version of Symfony,
|
||||
use the `STABILITY` environment variable during the build.
|
||||
|
||||
The value must be [a valid Composer stability option](https://getcomposer.org/doc/04-schema.md#minimum-stability).
|
||||
|
||||
For instance, use the following command to use the development branch of Symfony:
|
||||
|
||||
On Linux:
|
||||
|
||||
```console
|
||||
STABILITY=dev docker compose up --wait
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```console
|
||||
set STABILITY=dev && docker compose up --wait&set STABILITY=
|
||||
```
|
||||
|
||||
## Using Custom HTTP Ports
|
||||
|
||||
Use the environment variables `HTTP_PORT`, `HTTPS_PORT` and/or `HTTP3_PORT`
|
||||
to adjust the ports to your needs, e.g.
|
||||
|
||||
```console
|
||||
HTTP_PORT=8000 HTTPS_PORT=4443 HTTP3_PORT=4443 docker compose up --wait
|
||||
```
|
||||
|
||||
to access your application on [https://localhost:4443](https://localhost:4443).
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Let's Encrypt only supports the standard HTTP and HTTPS ports.
|
||||
> Creating a Let's Encrypt certificate for another port will not work,
|
||||
> you have to use the standard ports or to configure Caddy to use another provider.
|
||||
|
||||
## `Caddyfile` Options
|
||||
|
||||
You can also customize the `Caddyfile` by using the following environment variables
|
||||
to inject options block, directive or configuration.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> All the following environment variables can be defined in your `.env` file
|
||||
> at the root of the project to keep them persistent at each startup.
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
| Environment variable | Description | Default value |
|
||||
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| `CADDY_GLOBAL_OPTIONS` | the [global options block](https://caddyserver.com/docs/caddyfile/options#global-options), one per line | |
|
||||
| `CADDY_EXTRA_CONFIG` | the [snippet](https://caddyserver.com/docs/caddyfile/concepts#snippets) or the [named-routes](https://caddyserver.com/docs/caddyfile/concepts#named-routes) options block, one per line | |
|
||||
| `CADDY_SERVER_EXTRA_DIRECTIVES` | the [`Caddyfile` directives](https://caddyserver.com/docs/caddyfile/concepts#directives) | |
|
||||
| `CADDY_SERVER_LOG_OPTIONS` | the [server log options block](https://caddyserver.com/docs/caddyfile/directives/log), one per line | |
|
||||
| `SERVER_NAME` | the server name or address | `localhost` |
|
||||
| `FRANKENPHP_CONFIG` | a list of extra [FrankenPHP global directives](https://frankenphp.dev/docs/config/#caddyfile-config), one per line | |
|
||||
| `FRANKENPHP_WORKER_CONFIG` | a list of extra [FrankenPHP worker directives](https://frankenphp.dev/docs/config/#caddyfile-config), one per line | |
|
||||
| `MERCURE_PUBLISHER_JWT_KEY` | the JWT key to use for publishers | |
|
||||
| `MERCURE_PUBLISHER_JWT_ALG` | the JWT algorithm to use for publishers | `HS256` |
|
||||
| `MERCURE_SUBSCRIBER_JWT_KEY` | the JWT key to use for subscribers | |
|
||||
| `MERCURE_SUBSCRIBER_JWT_ALG` | the JWT algorithm to use for subscribers | `HS256` |
|
||||
| `MERCURE_EXTRA_DIRECTIVES` | a list of extra [Mercure directives](https://mercure.rocks/docs/hub/config), one per line | |
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
### Customizing the Server Name
|
||||
|
||||
```console
|
||||
SERVER_NAME="app.localhost" docker compose up --wait
|
||||
```
|
||||
@@ -0,0 +1,140 @@
|
||||
# Deploying in Production
|
||||
|
||||
Symfony Docker provides Docker images and a Docker Compose definition optimized
|
||||
for production usage.
|
||||
In this tutorial, we will learn how to deploy our Symfony application
|
||||
on a single server using Docker Compose.
|
||||
|
||||
## Preparing a Server
|
||||
|
||||
To deploy your application in production, you need a server.
|
||||
In this tutorial, we will use a virtual machine provided by DigitalOcean,
|
||||
but any Linux server can work.
|
||||
|
||||
If you already have a Linux server with Docker Compose installed,
|
||||
you can skip straight to [the next section](#configuring-a-domain-name).
|
||||
|
||||
Otherwise, use [this affiliate link](https://m.do.co/c/5d8aabe3ab80)
|
||||
to get $100 of free credit, create an account, then click on "Create a Droplet".
|
||||
Then, click on the "Marketplace" tab under the "Choose an image" section
|
||||
and search for the app named "Docker".
|
||||
This will provision an Ubuntu server with the latest versions of Docker and
|
||||
Docker Compose already installed!
|
||||
|
||||
For test purposes, the cheapest plans will be enough,
|
||||
even though you might want at least 2GB of RAM to execute Docker Compose
|
||||
for the first time.
|
||||
For real production usage,
|
||||
you'll probably want to pick a plan in the "general purpose" section
|
||||
to fit your needs.
|
||||
|
||||

|
||||
|
||||
You can keep the defaults for other settings, or tweak them according to your needs.
|
||||
Don't forget to add your SSH key or create a password
|
||||
then press the "Finalize and create" button.
|
||||
|
||||
Then, wait a few seconds while your Droplet is provisioning.
|
||||
When your Droplet is ready, use SSH to connect:
|
||||
|
||||
```console
|
||||
ssh root@<droplet-ip>
|
||||
```
|
||||
|
||||
## Configuring a Domain Name
|
||||
|
||||
In most cases, you'll want to associate a domain name with your site.
|
||||
If you don't own a domain name yet, you'll have to buy one through a registrar.
|
||||
|
||||
Then create a DNS record of type `A` for your domain name pointing
|
||||
to the IP address of your server:
|
||||
|
||||
```dns
|
||||
your-domain-name.example.com. IN A 207.154.233.113
|
||||
```
|
||||
|
||||
Example with the DigitalOcean Domains service ("Networking" > "Domains"):
|
||||
|
||||

|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Let's Encrypt, the service used by default by Symfony Docker to automatically
|
||||
> generate a TLS certificate doesn't support using bare IP addresses.
|
||||
> Using a domain name is mandatory to use Let's Encrypt.
|
||||
|
||||
## Deploying
|
||||
|
||||
Copy your project on the server using `git clone`, `scp`, or any other tool
|
||||
that may fit your need.
|
||||
If you use GitHub, you may want to use [a deploy key](https://docs.github.com/en/free-pro-team@latest/developers/overview/managing-deploy-keys#deploy-keys).
|
||||
Deploy keys are also [supported by GitLab](https://docs.gitlab.com/user/project/deploy_keys/).
|
||||
|
||||
Example with Git:
|
||||
|
||||
```console
|
||||
git clone git@github.com:<username>/<project-name>.git
|
||||
```
|
||||
|
||||
Go into the directory containing your project (`<project-name>`),
|
||||
and start the app in production mode:
|
||||
|
||||
```console
|
||||
# Build fresh production image
|
||||
docker compose -f compose.yaml -f compose.prod.yaml build --pull --no-cache
|
||||
|
||||
# Start container
|
||||
SERVER_NAME=your-domain-name.example.com \
|
||||
APP_SECRET=ChangeMe \
|
||||
CADDY_MERCURE_JWT_SECRET=ChangeThisMercureHubJWTSecretKey \
|
||||
docker compose -f compose.yaml -f compose.prod.yaml up --wait
|
||||
```
|
||||
|
||||
Be sure to replace `your-domain-name.example.com` with your actual domain name
|
||||
and to set the values of `APP_SECRET`, `CADDY_MERCURE_JWT_SECRET`
|
||||
to cryptographically secure random values.
|
||||
|
||||
Your server is up and running, and a HTTPS certificate has been automatically
|
||||
generated for you.
|
||||
Go to `https://your-domain-name.example.com` and enjoy!
|
||||
|
||||
> [!CAUTION]
|
||||
>
|
||||
> Docker can have a cache layer, make sure you have the right build
|
||||
> for each deployment or rebuild your project with `--no-cache` option
|
||||
> to avoid cache issues.
|
||||
|
||||
## Disabling HTTPS
|
||||
|
||||
Alternatively, if you don't want to expose an HTTPS server but only an HTTP one,
|
||||
run the following command:
|
||||
|
||||
```console
|
||||
SERVER_NAME=:80 \
|
||||
APP_SECRET=ChangeMe \
|
||||
CADDY_MERCURE_JWT_SECRET=ChangeThisMercureHubJWTSecretKey \
|
||||
docker compose -f compose.yaml -f compose.prod.yaml up --wait
|
||||
```
|
||||
|
||||
## Deploying on Multiple Nodes
|
||||
|
||||
If you want to deploy your app on a cluster of machines, you can use [Docker Swarm](https://docs.docker.com/engine/swarm/stack-deploy/),
|
||||
which is compatible with the provided Compose files.
|
||||
To deploy on Kubernetes, take a look
|
||||
at [the Helm chart provided with API Platform](https://api-platform.com/docs/deployment/kubernetes/),
|
||||
which can be easily adapted for use with Symfony Docker.
|
||||
|
||||
## Passing local environment variables to containers
|
||||
|
||||
By default, `.env.local` and `.env.*.local` files are excluded from production images.
|
||||
If you want to pass them to your containers, you can use the [`env_file` attribute](https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/#use-the-env_file-attribute):
|
||||
|
||||
```yaml
|
||||
# compose.prod.yaml
|
||||
|
||||
services:
|
||||
php:
|
||||
env_file:
|
||||
- .env.prod.local
|
||||
# ...
|
||||
```
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
# TLS Certificates
|
||||
|
||||
## Trusting the Authority
|
||||
|
||||
With a standard installation, the authority used to sign certificates
|
||||
generated in the Caddy container is not trusted by your local machine.
|
||||
|
||||
You must add the authority to the trust store of the host.
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
### Linux
|
||||
|
||||
```console
|
||||
docker cp $(docker compose ps -q php):/data/caddy/pki/authorities/local/root.crt /usr/local/share/ca-certificates/root.crt && sudo update-ca-certificates
|
||||
```
|
||||
|
||||
### Mac
|
||||
|
||||
```console
|
||||
docker cp $(docker compose ps -q php):/data/caddy/pki/authorities/local/root.crt /tmp/root.crt && sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/root.crt
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```console
|
||||
docker compose cp php:/data/caddy/pki/authorities/local/root.crt %TEMP%/root.crt && certutil -addstore -f "ROOT" %TEMP%/root.crt
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
## Using Custom TLS Certificates
|
||||
|
||||
By default, Caddy will automatically generate TLS certificates using Let's Encrypt
|
||||
or ZeroSSL.
|
||||
But sometimes you may prefer using custom certificates.
|
||||
|
||||
For instance, to use self-signed certificates created with [mkcert](https://github.com/FiloSottile/mkcert)
|
||||
do as follows:
|
||||
|
||||
1. Locally install `mkcert`
|
||||
2. Create the folder storing the certs:
|
||||
|
||||
```console
|
||||
mkdir -p frankenphp/certs
|
||||
```
|
||||
|
||||
3. Generate the certificates for your local host (example: "server-name.localhost"):
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
```console
|
||||
mkcert -cert-file frankenphp/certs/tls.pem -key-file frankenphp/certs/tls.key "server-name.localhost"
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
4. Add these lines to the `./compose.override.yaml` file about `CADDY_SERVER_EXTRA_DIRECTIVES`
|
||||
environment and volume for the `php` service:
|
||||
|
||||
```diff
|
||||
php:
|
||||
environment:
|
||||
+ CADDY_EXTRA_CONFIG: |
|
||||
+ https:// {
|
||||
+ tls /etc/caddy/certs/tls.pem /etc/caddy/certs/tls.key
|
||||
+ }
|
||||
# ...
|
||||
volumes:
|
||||
+ - ./frankenphp/certs:/etc/caddy/certs:ro
|
||||
# ...
|
||||
```
|
||||
|
||||
5. Restart your `php` service
|
||||
|
||||
## Disabling HTTPS for Local Development
|
||||
|
||||
To disable HTTPS, configure your environment to use HTTP by setting the following
|
||||
variables and starting the project with this command:
|
||||
|
||||
```console
|
||||
SERVER_NAME=http://localhost \
|
||||
MERCURE_PUBLIC_URL=http://localhost/.well-known/mercure \
|
||||
docker compose up --wait
|
||||
```
|
||||
|
||||
Ensure your application is accessible over HTTP by visiting `http://localhost`
|
||||
in your web browser.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Editing Permissions on Linux
|
||||
|
||||
If you work on Linux and cannot edit some of the project files right after
|
||||
the first installation, you can run the following command
|
||||
to set yourself as owner of the project files that were created by the Docker container:
|
||||
|
||||
```console
|
||||
docker compose run --rm php chown -R $(id -u):$(id -g) .
|
||||
```
|
||||
|
||||
## TLS/HTTPS Issues
|
||||
|
||||
See the [TLS section](tls.md) for more details.
|
||||
|
||||
## Production Issues
|
||||
|
||||
### How To Properly Build Fresh Images for Production Use
|
||||
|
||||
Remember that, by default, if you run `docker compose up --wait`,
|
||||
only the files `compose.yaml` and `compose.override.yaml` will be used.
|
||||
See ["How Compose works"](https://docs.docker.com/compose/intro/compose-application-model)
|
||||
and ["Merge Compose files"](https://docs.docker.com/compose/how-tos/multiple-compose-files/merge).
|
||||
|
||||
If you need to build images for production environment, you have to use the following
|
||||
command:
|
||||
|
||||
```console
|
||||
docker compose -f compose.yaml -f compose.prod.yaml build --pull --no-cache
|
||||
```
|
||||
|
||||
### Building Dev and Prod Images
|
||||
|
||||
Dev and prod images use distinct image names (`app-php-dev` and `app-php-prod`),
|
||||
so they won't conflict with each other.
|
||||
|
||||
To build and start the dev image:
|
||||
|
||||
```console
|
||||
docker compose up --wait
|
||||
```
|
||||
|
||||
To build and start the prod image:
|
||||
|
||||
```console
|
||||
docker compose -f compose.yaml -f compose.prod.yaml build --pull --no-cache
|
||||
docker compose -f compose.yaml -f compose.prod.yaml up --wait
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
>
|
||||
> The order of `-f` arguments matters.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Updating Your Project
|
||||
|
||||
To import the changes made to the _Symfony Docker_ template into your project,
|
||||
we recommend using [_template-sync_](https://github.com/coopTilleuls/template-sync):
|
||||
|
||||
1. Run the script to synchronize your project with the latest version of the skeleton:
|
||||
|
||||
<!-- markdownlint-disable MD013 -->
|
||||
|
||||
```console
|
||||
curl -sSL https://raw.githubusercontent.com/coopTilleuls/template-sync/main/template-sync.sh | sh -s -- https://github.com/dunglas/symfony-docker
|
||||
```
|
||||
|
||||
<!-- markdownlint-enable MD013 -->
|
||||
|
||||
2. Resolve conflicts, if any
|
||||
3. Run `git cherry-pick --continue`
|
||||
|
||||
For more advanced options, refer to [the documentation of _template sync_](https://github.com/coopTilleuls/template-sync#template-sync).
|
||||
@@ -0,0 +1,98 @@
|
||||
# Using Xdebug
|
||||
|
||||
The default development image is shipped with [Xdebug](https://xdebug.org/),
|
||||
a popular debugger and profiler for PHP.
|
||||
|
||||
When using [Dev Containers](https://containers.dev/), Xdebug is pre-configured and works out of the box.
|
||||
Open the **Run and Debug** panel in Visual Studio Code and start the **Debug PHP** launch configuration, then set your breakpoints and load a page.
|
||||
|
||||
For other setups, because it has a significant performance overhead, the step-by-step debugger
|
||||
is disabled by default.
|
||||
It can be enabled by including `debug` in the values of the `XDEBUG_MODE` environment variable.
|
||||
|
||||
On Linux and Mac:
|
||||
|
||||
```console
|
||||
XDEBUG_MODE=develop,debug docker compose up --wait
|
||||
```
|
||||
|
||||
On Windows:
|
||||
|
||||
```console
|
||||
set XDEBUG_MODE=develop,debug&& docker compose up --wait&set XDEBUG_MODE=
|
||||
```
|
||||
|
||||
## Debugging with Xdebug and PhpStorm
|
||||
|
||||
First, [create a PHP debug remote server configuration](https://www.jetbrains.com/help/phpstorm/creating-a-php-debug-server-configuration.html):
|
||||
|
||||
1. In the `Settings/Preferences` dialog, go to `PHP | Servers`
|
||||
2. Create a new server:
|
||||
- Name: `symfony` (or whatever you want to use for the variable `PHP_IDE_CONFIG`)
|
||||
- Host: `localhost` (or the one defined using the `SERVER_NAME` environment variable)
|
||||
- Port: `443`
|
||||
- Debugger: `Xdebug`
|
||||
- Check `Use path mappings`
|
||||
- Absolute path on the server: `/app`
|
||||
|
||||
You can now use the debugger!
|
||||
|
||||
1. In PhpStorm, open the `Run` menu and click on `Start Listening for PHP Debug Connections`
|
||||
2. Add the `XDEBUG_SESSION=PHPSTORM` query parameter to the URL of
|
||||
the page you want to debug, or use [other available triggers](https://xdebug.org/docs/step_debug#activate_debugger)
|
||||
|
||||
Alternatively, you can use [the **Xdebug extension**](https://xdebug.org/docs/step_debug#browser-extensions)
|
||||
for your preferred web browser.
|
||||
|
||||
3. On the command line, we might need to tell PhpStorm which
|
||||
[path mapping configuration](https://www.jetbrains.com/help/phpstorm/zero-configuration-debugging-cli.html#configure-path-mappings)
|
||||
should be used, set the value of the PHP_IDE_CONFIG environment variable to
|
||||
`serverName=symfony`, where `symfony` is the name of the debug server configured
|
||||
above.
|
||||
|
||||
Example:
|
||||
|
||||
```console
|
||||
XDEBUG_SESSION=1 PHP_IDE_CONFIG="serverName=symfony" php bin/console ...
|
||||
```
|
||||
|
||||
## Debugging with Xdebug and Visual Studio Code
|
||||
|
||||
1. Install necessary [PHP extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=DEVSENSE.phptools-vscode).
|
||||
2. Add [debug configuration](https://code.visualstudio.com/docs/debugtest/debugging-configuration#_launch-configurations)
|
||||
into your `.vscode\launch.json` file.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug PHP",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"pathMappings": {
|
||||
"/app": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
3. Use [Run and Debug](https://code.visualstudio.com/docs/debugtest/debugging#_start-a-debugging-session)
|
||||
options and run `Debug PHP` to listen for upcoming connections
|
||||
with [the **Xdebug extension**](https://xdebug.org/docs/step_debug#browser-extensions)
|
||||
installed and active.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Inspect the installation with the following command.
|
||||
The Xdebug version should be displayed.
|
||||
|
||||
```console
|
||||
$ docker compose exec php php --version
|
||||
|
||||
PHP ...
|
||||
with Xdebug v3.x.x ...
|
||||
```
|
||||
Reference in New Issue
Block a user