Compose runs multiple containers as components of a single (sandboxed) application. It treats each container as a service and handles operations such as network sharing between containers. These features make Compose a great solution for containerized Symfony applications that require a database. For example, it allows communication between both containers without having the user explicitly create a Docker network and add each container to that network.
As will be discussed further below, Compose leverages the contents of a docker-compose.yml file for its instructions.
Prerequisites
While not a hard requirement, please consider reading How to create and containerize a Symfony application with a shared Docker image. It discusses the Dockerfile, resource permissions, and other concepts referenced in this guide.
Step 1: Define the MariaDB service
The Doctrine ORM package installed when creating a Symfony application creates the docker-compose.override.yml and docker-compose.yml files saved at the root of the project directory. These are Compose files configured to run a PostgreSQL database and a mailer service by default.
Reconfigure these Compose files to connect the Symfony application to a containerized MariaDB database as follows:
-
Replace the contents of the docker-compose.yml file with the following code:
version: '3' services: db: image: mariadb:10.7.8 restart: always ports: - "3306:3306" environment: MARIADB_DATABASE: ${MARIADB_DATABASE:-app} MARIADB_USER: ${MARIADB_USER:-app} MARIADB_PASSWORD: ${MARIADB_PASSWORD:-!ChangeMe!} MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-!ChangeMe!}The above instructs Compose to do the following:
- Use version 3 of Compose file format
- Create a database service named
dbusing mariadb version 10.7.8 Docker image. - Always restart the
dbservice if it dies while running. - Forward port 3306 of the host computer to port 3306 of the
dbservice’s container. - Set the following default values to the following environment variables and overwrite the default values if the user or container sets environment variables to different values.
Notice that the environment variables in the above Compose file are hard-coded. To override the default values hard-coded to the environment variables, set the same variables in the .env file at the root of the project directory as shown below:
MARIADB_DATABASE=<db_name> MARIADB_USER=<db_user> MARIADB_PASSWORD=<db_password> MARIADB_ROOT_PASSWORD=<db_root_password> -
Delete the docker-compose.override.yml file at the root of the project directory.
Step 2: Define the Symfony application service
Assuming the Symfony application has a Dockerfile similar to the one created in the Symfony application containerization guide, the project’s Dockerfile will look like the following:
FROM ghcr.io/linkorb/php-docker-base:php8
COPY --chown=www-data:www-data --from=jakzal/phpqa /tools /tools
ENV PATH="$PATH:/tools:/tools/.composer/vendor/bin"
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
RUN install-php-extensions opcache
USER www-data:www-data
ARG COMPOSER_HOME=/tools/.composer
RUN cd /tools/.composer && \
composer global bin phpstan require \
phpstan/phpstan-phpunit \
phpstan/phpstan-doctrine \
phpstan/phpstan-symfony
-
If you already have or intend to use a Dockerfile like the one above, copy the following code to the docker-compose.yml file, ensuring that
appservice is on the same tree level as thedbservice.app: build: context: . dockerfile: Dockerfile depends_on: - db ports: - "8000:80" volumes: .:/app environment: MARIADB_DATABASE: ${MARIADB_DATABASE:-app} MARIADB_USER: ${MARIADB_USER:-app} MARIADB_PASSWORD: ${MARIADB_PASSWORD:-!ChangeMe!} DATABASE_URL: "mysql://$MARIADB_USER:$MARIADB_PASSWORD@db:3306/$MARIADB_DATABASE?serverVersion=mariadb-10.7.8&charset=utf8mb4"The above instructs Compose to do the following:
-
Create a service named
appusing the directives in the Dockerfile. -
Inform Docker that the
appservice depends ondbservice to function.dbis the domain of the MariaDB service of the Compose application’s network in this example. If you give the database service a different name, please set the database domain to the name of that database service. -
Forward port 8000 of the host computer to port 8000 of the
appservice/container. -
Bind-mount the current working directory of the host computer to the /app directory of the
appservice/container. -
Set the following environment variables in the
appservice’s container. Use the default (hard-coded) values if different values are not provided.
Note that the
DATABASE_URLenvironment variable uses variable substitution. You must define theMARIADB_DATABASE,MARIADB_USER, andMARIADB_PASSWORDin theenvironmentfield (and optionally in the .env file) for this substitution to work.Doctrine DBAL configuration requires that you set the
server_versionproperty to the MariaDB version when using MariaDB. You may set this value in the config/packages/doctrine.yaml file of the Symfony application or theserverVersion(i.e.serverVersion=mariadb-10.7.8) query of theDATABASE_URLenvironment variable. The database server version in this example is the same as the MariaDB version in thedbservice. Please see Doctrine DBAL configuration for more information.Passing a Dockerfile to a Compose service as shown above allows for better control over the container image. You can, for example, extend and customise the Docker image before passing it to Compose.
If you want to use a Docker image instead of a Dockerfile in a Compose service, please replace the build field (and its properties) in the
appservice above withimage. For example, the following uses the LinkORB’s PHP 8 image:app: image: ghcr.io/linkorb/php-docker-base:php8 #... -
-
Place the
#symbol before the uncommentedDATABASE_URLenvironment variable in the .env file as shown below to disable it.# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=15&charset=utf8"Recall that the
appservice uses variable substitution to dynamically specify theDATABASE_URLenvironment variable in the docker-compose.yml file. That variable will be available in the Symfony application container, so you do not need to keep this one.
The docker-compose.yml file
The complete docker-compose.yml file should look like the code block below at this point.
version: '3'
services:
db:
image: mariadb:10.7.8
restart: always
ports:
- "3306:3306"
environment:
MARIADB_DATABASE: ${MARIADB_DATABASE:-app}
MARIADB_USER: ${MARIADB_USER:-app}
MARIADB_PASSWORD: ${MARIADB_PASSWORD:-!ChangeMe!}
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD:-!ChangeMe!}
app:
build:
context: .
dockerfile: Dockerfile
depends_on:
- db
ports:
- "8000:80"
volumes:
- .: /app
environment:
MARIADB_DATABASE: ${MARIADB_DATABASE:-app}
MARIADB_USER: ${MARIADB_USER:-app}
MARIADB_PASSWORD: ${MARIADB_PASSWORD:-!ChangeMe!}
DATABASE_URL: "mysql://$MARIADB_USER:$MARIADB_PASSWORD@db:3306/$MARIADB_DATABASE?serverVersion=mariadb-10.7.8&charset=utf8mb4"
Step 3: Run the Symfony application and MariaDB as Compose services
-
Run the Compose application locally using the following command.
docker compose --env-file=.env upYou may view and follow the logs of an individual service in the Compose service with the following command.
docker compose logs -f <service-name>E.g. To view the Symfony app’s logs, run the following command
docker compose logs -f app -
Click the globe icon in the Local Address field of the Ports tab in VSCode’s integrated terminal to open the containerized application in the browser.
The Symfony application container has access to a containerized MariaDB database now. You may open the locally stored project files, make changes, view the changes in a browser, or push the code to GitHub.
-
Run the following command if you wish to stop the Compose application.
docker compose down
Run binaries within a Compose service
You may run binaries available in a compose service/container using docker compose exec as follows:
-
Ensure the Compose application is running or start it by running the following command in the terminal.
docker compose --env-file up -d -
Run
docker compose exec <service-name> <command>. The following example creates a database (using a Doctrine ORM) in the MariaDB service/container.docker compose exec app php bin/console doctrine:database:createNote that you must run the above commands at the root of the project directory for this to work.
Develop the containerized Symfony and MariaDB application in a devcontainer
A devcontainer requires an interactive user account. This, among other things, means you need to create and configure a custom user account in the Dockerfile used in the app service as shown below. Please see
create a non-root user with shell access for more information.
FROM ghcr.io/linkorb/php-docker-base:php8
RUN groupadd devuser && \
useradd -m -g devuser devuser && \
chsh -s /bin/bash devuser && \
chown -hR devuser:devuser /app && \
sed -i 's/Listen 80/Listen 8000/g' /etc/apache2/ports.conf && \
sed -i "s/*:80>/*:8000>/g" /etc/apache2/sites-enabled/000-default.conf
COPY --chown=devuser:devuser --from=jakzal/phpqa /tools /tools
ENV PATH="$PATH:/tools:/tools/.composer/vendor/bin"
COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/bin/
RUN install-php-extensions opcache
USER devuser:devuser
ARG COMPOSER_HOME=/tools/.composer
RUN cd /tools/.composer && \
composer global bin phpstan require \
phpstan/phpstan-phpunit \
phpstan/phpstan-doctrine \
phpstan/phpstan-symfony
Assuming you have updated the Dockerfile, create a devcontainer as follows:
-
Create a .devcontainer directory at the root of the project directory.
-
Create a devcontainer.json file inside the .devcontainer directory.
-
Add the following code to the devcontainer.json file
{ "name": "my-symfony-app", "dockerComposeFile": "../docker-compose.yml", "service": "app", "workspaceFolder": "/app", "postCreateCommand": "composer install --no-cache --no-interaction", "forwardPorts": [ 8000 ], "customizations": { "vscode": { "extensions": [ "whatwedo.twig" ] } }, "remoteUser": "devuser", "shutdownAction": "stopCompose" }The above code instructs the devcontainer to do the following:
- Create a devcontainer for
my-symfony-appusing the ../docker-compose.yml file. - Attach to the
appservice and run other (i.e. database) services in the background when the devcontainer starts. - Forward incoming connections to port 8000 of the devcontainer to port 8000 of the
appservice. - If the container is running in VSCode or a Codespace, install Twig extension.
- Set
devuser(created in the Dockerfile) as the devcontainer’s user. - Stop Docker Compose when the devcontainer or Codespace stops.
- Create a devcontainer for
-
Install the Dev Containers extension in VSCode.
-
Click Reopen in container on the popup message at the bottom right of the screen or click the green button at the bottom left of VSCode’s window and select Reopen in container. Restart VSCode if Reopen in container does not appear on the screen.
Develop the containerized Symfony and MariaDB application in a Codespace
Please see Develop the containerized Symfony application within a GitHub Codespace for Codespace usage guidance.
Share the containerized application with the team
Please update the README.md file to document how to build, run, and develop the application before pushing the completed project to GitHub. A few points to note in the README.md file include:
-
Contributors need to run the following commands to install the application’s dependencies when working outside a devcontainer.
- Start the Compose services with this command.
docker compose up - Install the application’s dependencies.
docker compose exec app composer install
- Start the Compose services with this command.
-
To run their forks of the repository in a Codespace, contributors need to add Codespace secrets listed in Develop a containerized Symfony application within a GitHub Codespace to their forks.