How to use PHP and Symfony with Docker for Development and Production [Part 1]

In this short series of blog entries we are going to create a development environment with Docker for a PHP application which is using the Symfony PHP framework. This development environment will also be the base for our production environment later.

As first part of this blog and this series of articles about building an advanced, data processing application in PHP with Symfony, I am going to start with the basic foundation: my infrastructure environment. Since my app is going to be a private project, I am not going to use a cloud solution like AWS or Google Cloud. Instead I am going to use Docker for Developement and Production.

As development environment I am using a MacBook Pro with Docker Desktop installed. You can download Docker from the official Docker website. It is free for private projects. While I used and tested my proposed solution only with my Mac, it should also work for Linux and Windows users as well.

My production live system will also be based on Docker. I am going to use a small dedicated root server as Docker host. For a professional, secure and clean deployment process, later we are going to build a deployment pipeline with Git in a separate post.

Application Components

That should be enough for context and as introduction, let us just dive in…

Since our application will be a pure CLI backend application, which is consuming some websocket streams, processing and persisting data and calling APIs we are going to need the following components:

  • PHP Server (we are going to use the php-fpm process manager)
  • MongoDB
  • NGINX webserver (if you want to have a web frontend)

PHP without Web Frontend?

For testing and demonstration purposes, we are going to include a NGINX webserver in our development environment. For my final app I intentionally do not use a webserver. I do not plan to offer access via a web frontend, so there is no reason to expose the app to the outside world.

If you plan to build a web frontend, of course, you are also going to need for production a webserver like NGINX. It will process web requests, offload SSL connections and redirect the web requests to your PHP server.

Creating the Project Skeleton

But now, let’s finally start our project. First of all create our project base directory.

mkdir -p myapp/
cd myapp/

Our complete environment and app is going to live in this directory. Of course, you can also separate between the environment and application files. But this should not be part of this tutorial.

Initiating the Git Repository and connect with Gitlab

It is a good time to initiate our git repository. We are going to use git for version control, back up and later also for deployment. I am using the SaaS solution of Gitlab with the free plan. I do recommend that you do also use Gitlab since we are going to need the specific Gitlab-CI function later for our pipeline. You can register here for free: https://about.gitlab.com/pricing/

On the git platform, create a new project. I call my new project „myapp“ but you are free to choose every name you like. Just think about changing the names in the following code snippets. You also need to create a SSH key and introduce it to Gitlab to allow a secure connection between your development machine and the Gitlab platform. Find more information about creating SSH keys for Gitlab: https://docs.gitlab.com/ee/user/ssh.html

Open your terminal, navigate into your project folder and execute the following commands.

cd myapp
git init --initial-branch=main
git remote add origin git@gitlab.com:yourname/myapp.git

Now our project folder is part of our local repository and it is connected with our remote repository at the Gitlab platform. We are able to push all our local commits to Gitlab. If you should have problems with the connection to the remote repo, you may have problems with your SSH key.

Start using Docker with Docker Compose

We are going to orchestrate the different components or containers via docker compose. I assume that you have a basic understanding of docker compose. Otherwise, please get your self familiar with it e.g. on the page: https://docs.docker.com/compose/

In the first step, we are going to create a customized PHP-FPM container, the NGINX webserver and install Symfony.

Start by creating the following files in your project folder. The first file is the docker-compose.yml file, which acts like a recipe for Docker how to initiate the container environment.

 # myapp/docker-compose.dev.yml
version: '3.7'

services:
  php-fpm:
    build:
      context: ./php-fpm
    restart: unless-stopped
    environment:
      - COMPOSER_MEMORY_LIMIT=-1
    volumes:
      - ./src:/var/www

  nginx:
    image: nginx:alpine
    restart: unless-stopped
    depends_on:
      - php-fpm
    ports:
      - "80:80"
    volumes:
      - ./src:/var/www
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./logs:/var/log

As mentioned earlier, we use the PHP-FPM process manager. Of course, we could just use a default image from Docker Hub. But we will need some customizations for our app later. So we are going to create our own Image which inherits from the official PHP-FPM 8.1 Alpine Image.

# myapp/php-fpm/Dockerfile
FROM php:8.1-fpm-alpine

RUN apk --update \
        --no-cache \
        add git make g++ autoconf rabbitmq-c-dev libtool tzdata \
    && pecl install mongodb \
    && docker-php-ext-enable mongodb 
ENV TZ=Europe/Berlin
RUN apk del --purge make g++ autoconf libtool \
    && rm -rf /var/cache/apk/*
RUN cd /usr/local/etc/php/conf.d/ && \
  echo 'memory_limit = -1' >> /usr/local/etc/php/conf.d/docker-php-ram-limit.ini

COPY --from=composer /usr/bin/composer /usr/bin/composer

WORKDIR /var/www

EXPOSE 9000

CMD php-fpm

To make our NGINX webserver work and allow it to forward PHP requests to our PHP-FPM process manager, we need to create the NGINX config file.

# myapp/nginx/nginx.conf
user  nginx;
worker_processes  4;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;
    access_log          /var/log/nginx/access.log;
    sendfile            on;
    keepalive_timeout   65;

    upstream php-upstream {
        server php-fpm:9000;
    }

    server {
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;

        server_name localhost;
        root /var/www/public;
        index index.php index.html index.htm;

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

        location ~ \.php$ {
            try_files $uri /index.php =404;
            fastcgi_pass php-upstream;
            fastcgi_index index.php;
            fastcgi_buffers 16 16k;
            fastcgi_buffer_size 32k;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_read_timeout 600;
            include fastcgi_params;
        }

        location ~ /\.ht {
            deny all;
        }
    }
}

In addition you need to create two folder inside your project folder so that you can start up your container without errors:

mkdir -p src/
mkdir -p logs/nginx/

Now we are ready to start our container for the first time. Make sure that Docker Desktop is running and then move again to the terminal:

docker compose -f docker-compose.dev.yml up -d

Congratulations, you just booted your basic development setup. You can type localhost/ in your browser… and you will get an error message „404 Not found“. Sounds bad but is actually exactly what we want right now. It means that your request is successfully answered by NGINX. We did not create content for the server to serve, so the error is correct.

Committing and Pushing to GitLab

Before we wrap up for today, we should commit our changes to our git repository and also push our commits to GitLab. You can do that within Visual Studio Code or with this commands in your terminal (make sure you are in the root folder of your project):

git add .
git commit -m "Set up Docker Compose File"
git push -u origin main

In the next blog post, we are going to fix the 404 error and install the current version of Symfony in your shiny new development environment.

Final Words

In this blog post we created the basics for our environment. For now we archived:

  • Creating a new project, initiate a git repository and connect it with GitLab
  • Set up Docker and initiated the docker-compose.yml file
  • Set up NGINX container which connects to our customized PHP-FPM container
  • Pushed our results to git

This was the first of four parts about setting up a docker environment for Symfony and PHP for development and production. You can find part 2 about installing Symfony in your Docker environment here.

Schreiben Sie einen Kommentar