Docker — Best setup for Development Testing and Production for PHP

Andrew Pogulailo
3 min readAug 20, 2022

--

Photo by Ian Taylor

I’m not a DevOps engineer, but I like the infrastructure as a code paradigm, so I try to use Docker wherever possible. And I also really like to tidy up the code and make everything as convenient and understandable for use as possible. I’m a PHP Developer, so all the examples will be related to PHP, but I think it won’t be a problem if you use another programming language.

Common beginner mistakes

Let’s start with Docker newbies’ most common problems, then move on.

More than one process per container

I often see how beginners try to stuff everything they need into one container. I’m not an exception either, I remember how I tried to configure Supervisor in a container… But then I realized that Docker is already Supervisor on steroids. Therefore, these should be separate containers if you need a database, web server, or message broker.

Same image for development and deployment

You should not use one image for development and deployment. Usually, during development, you need a different environment setting, for deployment you don’t need test frameworks or static code analyzers. Therefore, it is better to spread images for deployment and development.

Different containers for Stagging and Production

You don’t need to make a new build of the container every time for different environments, because you won’t have guarantees that it will work the same way. Therefore, it is best to use the same container for Staging and Production.

Directories structure

I don’t like it when the application code and the infrastructure code are mixed, so I separate the docker files from the application code like this:

.
└── project-directory
├── app
├── docker
├── var
├── .gitignore
└── docker-compose.yml

app/ — contains application files such as Symfony, Laravel, Slim, etc.

docker/ — contains Docker files such as Dockerfiles, Nginx configs, etc.

var/ — contains temporary Docker files such as database files, etc.

Now let’s see what’s inside the docker/directory. I separate the files that will be used for application development and production.

.
└── docker
├── development
└── production

development/ — contains development Docker files.

production/ — contains production and staging Docker files.

Let’s consider the developmentdirectory as an example, the structure of the production directory will be similar.

.
└── development
├── nginx
├── php
├── php-cli
└── php-fpm

nginx/ — contains Nginx configurations and Dockerfile.

php/ — contains common configuration for php-cli and php-fpm containers.

php-cli/ — contains configuration specific for PHP CLI container. This container will be used to run console commands such as composer install, composer require, etc.

php-fpm/ — contains configuration specific for PHP FPM container. this container will be used to work with Nginx.

You can find more details in my repository. I will leave a link at the end of the article.

Dynamiс domain name changing

This is required to deploy the application in staging and production environments with different domain names. Instead of using Nginx configurations directly, I use templates to dynamically populate data from environment variables.

.
└── nginx
├── templates
│ └── default.conf.template
└── Dockerfile

Let’s see what is in the default.conf.template file

server {
server_name ${SERVER_NAME};
root /app/public;
...

And in the Dockerfile, we simply copy the contents of the templates/ directory to the corresponding directory inside the container.

FROM nginx:1.18-alpine

COPY ./nginx/templates /etc/nginx/templates

WORKDIR /app

Code examples

You can find more details in my GitHub repository: https://github.com/pogulailo/seadog

--

--

Andrew Pogulailo

I specialize in developing high-load systems. I'm passionate about using my skills to create innovative web solutions that drive business success.