LinkORB Engineering
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.
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.
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 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:
db
using mariadb version 10.7.8 Docker image.db
service if it dies while running.db
service’s container.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.
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 app
service is on the same tree level as the db
service.
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 app
using the directives in the Dockerfile.
Inform Docker that the app
service depends on db
service to function.
db
is 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 app
service/container.
Bind-mount the current working directory of the host computer to the /app directory of the app
service/container.
Set the following environment variables in the app
service’s container. Use the default (hard-coded) values if different values are not provided.
Note that the DATABASE_URL
environment variable uses variable substitution. You must define the MARIADB_DATABASE
, MARIADB_USER
, and MARIADB_PASSWORD
in the environment
field (and optionally in the .env file) for this substitution to work.
Doctrine DBAL configuration requires that you set the server_version
property to the MariaDB version when using MariaDB. You may set this value in the config/packages/doctrine.yaml file of the Symfony application or the serverVersion
(i.e. serverVersion=mariadb-10.7.8
) query of the DATABASE_URL
environment variable. The database server version in this example is the same as the MariaDB version in the db
service. 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 customize 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 app
service above with image
. 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 uncommented DATABASE_URL
environment 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 app
service uses variable substitution to dynamically specify the DATABASE_URL
environment 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 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"
Run the Compose application locally using the following command.
docker compose --env-file=.env up
You 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
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:create
Note that you must run the above commands at the root of the project directory for this to work.
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:
my-symfony-app
using the ../docker-compose.yml file.app
service and run other (i.e. database) services in the background when the devcontainer starts.app
service.devuser
(created in the Dockerfile) as the devcontainer’s user.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.
Please see Develop the containerized Symfony application within a GitHub Codespace for Codespace usage guidance.
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.
docker compose up
docker compose exec app composer install
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.
#docker
)