Symfony 5, Docker, MySQL example

Today I will write how to make Symfony 5 application with docker. If you haven’t, install docker on your machine.

Start by creating directory docker/nginx. Put in it default.conf file:

# docker/nginx/default.conf
server {
    listen 80;
    server_name localhost;

    index index.php index.html;
    root /app/public;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/index\.php(/|$) {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;

        # 'symfony_mysql_example_php' is from docker-compose.yml php container_name
        fastcgi_pass symfony_mysql_example_php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }

    location ~ \.php$ {
        return 404;
    }

    error_log /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
}

Then inside docker directory create directory php/conf.d and put xdebug.ini file. Or you can skip adding this file if you do not need xdebug. This xdebug config might not be working on macOS, not sure, because I am working with Xubuntu, just I remember when working in team of people with macOS and linuxes, those configs used to be slightly different.

# docker/php/conf.d/xdebug.ini

zend_extension=xdebug

[xdebug]
xdebug.mode=develop,debug
xdebug.client_host=host.docker.internal
xdebug.discover_client_host=true
xdebug.start_with_request=yes
xdebug.log="/tmp/xdebug.log"

Then create file docker/php/Dockerfile:

FROM php:8.0-fpm

RUN apt-get update -yqq && \
    apt-get install -yqq \
    git \
    curl \
    zip \
    unzip \
    gzip \
    libzip-dev \
    libicu-dev \
    nano

RUN docker-php-ext-configure intl

RUN docker-php-ext-install intl pdo pdo_mysql zip opcache bcmath sockets

RUN pecl install xdebug && docker-php-ext-enable xdebug opcache

RUN echo "opcache.max_accelerated_files = 20000" >> /usr/local/etc/php/conf.d/docker-php-ext-opcache.ini

RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" && \
    php composer-setup.php && \
    php -r "unlink('composer-setup.php');" && \
    mv composer.phar /usr/local/bin/composer && \
    chmod +x /usr/local/bin/composer

WORKDIR /app

Then create directory ‘scripts’ and place wait-for-it.sh file from here: https://github.com/vishnubob/wait-for-it/blob/master/wait-for-it.sh

Make sure wait-for-it.sh has rights to be executed. Here I allow execute to all, locally it does not matter, but maybe it would be enough to allow execute not to all – so that we use principle of least privilege. So if you will use it for production, you might want to think about it to have better security.

sudo chmod  a+x scripts/wait-for-it.sh 

docker-compose.yml:

version: "3"
services:
  php:
    build: docker/php
    volumes:
      - .:/app:cached

      # for xdebug
      - ./docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      # end for xdebug
    working_dir: /app

    # this container name has to be set also in default.conf
    container_name: symfony_mysql_example_php
    depends_on:
      - mysql

  nginx:
    image: nginx:1.15.0
    ports:
      - 127.0.0.1:${NGINX_PORT}:80
    volumes:
      - .:/app:cached
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:cached
    container_name: symfony_mysql_example_nginx
    depends_on:
      - php

  mysql:
    image: mysql:5.7
    volumes:
      - ./docker/mysql/data:/var/lib/mysql
    ports:
      - 127.0.0.1:${MYSQL_PORT}:3306
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DB_NAME: ${MYSQL_DB_NAME}
    container_name: symfony_mysql_example_mysql

Makefile:

.PHONY: start stop init

include .env
export $(shell sed 's/=.*//' .env)

start:
	docker-compose up -d

stop:
	docker-compose stop

init:
	docker-compose build
	docker-compose up -d
	#docker-compose exec php composer install
	#docker-compose exec php /app/scripts/wait-for-it.sh mysql:$(MYSQL_PORT) -- echo "mysql is up"
	#docker-compose exec php php bin/console doctrine:database:create
	#docker-compose exec php php bin/console doctrine:migrations:migrate --no-interaction
	#docker-compose exec php php bin/console doctrine:fixtures:load --no-interaction

Here in the init part I have commented few lines. We do not need them yet, we do not have composer.json, so cannot run composer install.

.env:

# In all environments, the following files are loaded if they exist,
# the latter taking precedence over the former:
#
#  * .env                contains default values for the environment variables needed by the app
#  * .env.local          uncommitted file with local overrides
#  * .env.$APP_ENV       committed environment-specific defaults
#  * .env.$APP_ENV.local uncommitted environment-specific overrides
#
# Real environment variables win over .env files.
#
# DO NOT DEFINE PRODUCTION SECRETS IN THIS FILE NOR IN ANY OTHER COMMITTED FILES.
#
# Run "composer dump-env prod" to compile .env files for production use (requires symfony/flex >=1.2).
# https://symfony.com/doc/current/best_practices.html#use-environment-variables-for-infrastructure-configuration

###> symfony/framework-bundle ###
APP_ENV=dev
APP_SECRET=141d460b2d7d75b1e3c2f4841d7a689a
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
#TRUSTED_HOSTS='^(localhost|example\.com)$'
###< symfony/framework-bundle ###

NGINX_PORT=8000
MYSQL_PORT=3000
MYSQL_ROOT_PASSWORD=notprod
MYSQL_DB_NAME=symfony_db

###> doctrine/doctrine-bundle ###
DATABASE_URL=mysql://root:notprod@symfony_mysql_example_mysql/symfony_db
###< doctrine/doctrine-bundle ###

See how DATABASE_URL is made – there is used notprod password as is defined in MYSQL_ROOT_PASSWORD. Also same with symfony_db. And symfony_mysql_example_mysql is taken from docker-compose.yml mysql container name.

The duplication does not look ideal, in general it is good to use DRY principle. But since this part is rarely changed, I did not try improving it.

By the way – I commit .env files are committed to git. Just do not commit sensitive data, set some example passwords and other data in it. You can read in Symfony docs about .env files. Also I have discussed this in Symfony devs slack channel on this topic, they confirmed that I should commit .env to git repository and I believe they know what they are talking about. I had sent the Symfony task homework to 2 companies when I was at job interview, they both counted it as mistake to commit .env to git. They argument was – it is not safe. I do not understand – how it is not safe. If we do not commit sensitive production data like production passwords, then what is unsafe there? I believe it was their mistake, not mine according to Symfony docs and guys from Symfony slack channel. But who knows – maybe really I and guys from slack misunderstand. I would like to hear how do you understand Symfony docs about those .env files and git.

Now we will run:

make init

This will install various things which we just set up.

Now since we have installed composer, we can create Symfony project with it:

docker-compose exec php composer create-project symfony/skeleton not_important_name

Do not care about not_important_name, we will delete it anyway.

After this command has been executed, we have Symfony application in not_important_name directory.

In not_important_name directory, there is .env file also. Remove it – we do not want to overwrite our current .env file.

Move files from not_important_name directory to the root directory. Because of permission errors I copied the files.

And delete the not_important_name directory, using sudo to not get those permission errors:

sudo rm -rf not_important_name/

To use doctrine with Symfony, we need to install ORM – at least I got this suggestion when running other commands:

docker-compose exec php composer require symfony/orm-pack

Now remove comment sign # from few more lines in init block in Makefile, init now looks like this:

init:
	docker-compose build
	docker-compose up -d
	docker-compose exec php composer install
	docker-compose exec php /app/scripts/wait-for-it.sh mysql:$(MYSQL_PORT) -- echo "mysql is up"
	docker-compose exec php php bin/console doctrine:database:create
	#docker-compose exec php php bin/console doctrine:migrations:migrate --no-interaction
	#docker-compose exec php php bin/console doctrine:fixtures:load --no-interaction

Run again:

make init

And if we go to http://localhost:8000/ – we should see Symfony welcome page.

Also there is docker/mysql directory created. We do not need it in git repository. Add to .gitnore. .gitignore file was created when we were installing Symfony, so I am just adding 2 lines now at the bottom .idea (for PhpStorm users) and docker/mysql, my file now looks like this:


###> symfony/framework-bundle ###
/.env.local
/.env.local.php
/.env.*.local
/config/secrets/prod/prod.decrypt.private.php
/public/bundles/
/var/
/vendor/
###< symfony/framework-bundle ###

.idea
docker/mysql

Also we have enabled xdebug. Xdebug is usually problematic to configure but luckily this time I just needed to do one thing in PhpStorm and it works – a mirracle:

Go to File > Settings > PHP > Servers and make it as in screenshot above. I needed only to add /app “Absolute path on the server” setting on the root directory, others were set.

If you do not use xdebug, then you can comment/remove this line in docker-compose.yml, because using xdebug makes your application slower:

- ./docker/php/conf.d/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini

The end

We have left commented lines for fixtures and migrations, I am leaving them for you – when you will add migrations or fixtures, you just remove comments from them. I was writing and trying the to execute what I have written, then when noticing errors – I fix. Hopefully for you it goes smoothly but forgive me if I have made some mistakes and please let me know. Ideally I should retest whole execution by the article but you know

Chuck Norris Masculine - ReaL men Test in Production

🙂

And versions used:

  • Ubuntu 18.04.5 LTS,
  • Docker version 20.10.8, build 3967b7d
  • Symfony 5.3.9
  • PHP Version 8.0.9
  • MySQL 5.7.35
  • PhpStorm 2021.2

I could add more details about those configs but not today, maybe some day later – as in programming – it is better to release something smaller than wait. And then add improvements later if needed.