Containerize Nginx, Laravel and MySQL with Docker Compose

In this article, we are going to use Docker Compose to run our Laravel application using Nginx, PHP, and MySQL. In the end, we will get three separate service containers.

  • An app service container
  • A db service container to run MySQL
  • An nginx service container

Project Directory Structure

First, create a directory with the name laravelapp that contains the Laravel project and docker related settings. We will keep the docker-compose.yml file in the root of the current docker project.

- laravelapp
  - .docker
  - db
  - src
  - docker-compose.yml
  • .docker This directory contains all the containers related configuration such as PHP, Nginx, MySQL and Docerfile
  • db directory mapping with the MySQL container
  • src directory contains the Laravel project
  • docker-compose.yml file contains all the containers settings

Now download the Laravel from the Github repository and extract the zip file under laravelapp/src directory.

Containerize Laravel with Docker

The next step is to create a Dockerfile under laravelapp/.docker/nginx directory. We required this file to create our custom images that include the PHP 7.4, Composer and System User.

Setup Dockerfile

Our Dockerfile starts with php:7.4-fpm official image from docker hub and takes user and uid from docker-compose file to create a system user. Next, we will install all the Laravel dependencies and PHP extensions that we require to run the Laravel application.

Next, we will install the composer that we require to install the Laravel packages. Later we will create a system user and assign it under www-data and the root group.

The final step for the Dockerfile is to set the working directory and assign the system user.

Copy the following content and paste it to your Dockerfile.

FROM php:7.4-fpm

# Get argument defined in docker-compose file
ARG user
ARG uid

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip \
    && docker-php-ext-install pdo_mysql \
    && docker-php-ext-install mbstring \
    && docker-php-ext-install exif \
    && docker-php-ext-install pcntl \
    && docker-php-ext-install bcmath \
    && docker-php-ext-install gd \
    && docker-php-source delete

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Get latest Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# Create system user to run Composer and Artisan Commands
RUN useradd -G www-data,root -u $uid -d /home/$user $user
RUN mkdir -p /home/$user/.composer && \
    chown -R $user:$user /home/$user

# Set working directory
WORKDIR /var/www

USER $user

Create Multiple Containers with Docker Compose

The Docker Compose helps us to build a fully customizable environment with multiple containers. We will run three containers for the Laravel application i.e. app, nginx and db containers. The app container will start with the help of the Dockerfile that we recently created. And we will use the official images for nginx and db containers

Let’s start by creating a docker-compose.yml file at the root of your project directory i.e. laravelapp.

We start the docker-compose file with version and each container will be defined under the services prefix.

version: "3.7"
services:

The app container

Let’s start with the app container.

version: "3.7"
services:
  app:
    build: 
      args: 
        user: dean
        uid: 1001
      context: ./.docker
      dockerfile: Dockerfile
    image: laravelapp
    container_name: laravelapp-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./src:/var/www
      - ./.docker/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    networks:
      - laravelapp
  • build: section holds arguments user and uid that we have accessed in our Dockerfile. The context is the path of our Dockerfile.
  • image: holds the name of the application, in our case laravelapp is the name of this image.
  • container_name: holds the name of the container.
  • restart: The container always restarts, until it stopped.
  • working_dir: Sets the default working directory for this container.
  • volumes: It is used to share the local files with a particular container. As you can see we have set the volume ./src:/var/www, where ./src directory holds the Laravel application that will be shared to /var/www container’s directory.
  • networks: Sets the service to use a network i.e. laravelapp.

The db container

version: "3.7"
services:
  ....
  ....
  ....

  db:
    image: mysql:5.7
    container_name: laravelapp-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: 'lara_db'
      MYSQL_ROOT_PASSWORD: 'root'
      MYSQL_PASSWORD: 'lara_password'
      MYSQL_USER: 'lara_user'
    volumes:
      - ./db/mysql:/var/lib/mysql
      - ./.docker/mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - laravelapp
  • image: Define the name of the docker image and the version. You can find the MySQL image from the docker hub.
  • container_name: holds the name of the container.
  • restart: The container always restarts, until it stopped.
  • environment: Define the database environment that you can find from the official MySQL image.
  • volumes: and networks: are the same as the app container.

The Nginx Container

version: "3.7"
services:
  ....
  ....
  ....

  nginx:
    image: nginx:1.17-alpine
    container_name: laravelapp-nginx
    restart: unless-stopped
    ports:
      - 8008:80
    volumes:
      - ./src:/var/www
      - ./.docker/nginx:/etc/nginx/conf.d
    networks:
      - laravelapp
  • image: Define the name of the docker image and the version. You can find the Official Nginx image from the docker hub.
  • container_name: holds the name of the container.
  • restart: The container always restarts, until it stopped.
  • ports: Sets the ports where 8008 to the web server running on 80 inside the nginx container.
  • volumes: and networks: are the same as the app container.

Nginx Configuration File for Docker

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass app: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 / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

The following configuration file let Nginx server to listen on port 80 and use the index.php file. The document root for our Laravel application is public directory which is located under /var/www directory.

Make sure to use the container name i.e. app instead of localhost for fastcgi_pass parameter.

PHP Configuration File for Docker

Create uploads.ini file under .docker/php directory and set the configuration as required.

file_uploads = On
memory_limit = 64M
upload_max_filesize = 64M
post_max_size = 64M
max_execution_time = 900

MySQL Configurtion File for Docker

Create my.cnf file under .docker/mysql directory and add the required configuration.

[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

Final Docker Compose File

version: "3.7"
services:
  app:
    build: 
      args: 
        user: dean
        uid: 1001
      context: ./.docker
      dockerfile: Dockerfile
    image: laravelapp
    container_name: laravelapp-app
    restart: unless-stopped
    working_dir: /var/www/
    volumes:
      - ./src:/var/www
      - ./.docker/php/uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    networks:
      - laravelapp

  db:
    image: mysql:5.7
    container_name: laravelapp-db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: 'lara_db'
      MYSQL_ROOT_PASSWORD: 'root'
      MYSQL_PASSWORD: 'lara_password'
      MYSQL_USER: 'lara_user'
    volumes:
      - ./db/mysql:/var/lib/mysql
      - ./.docker/mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - laravelapp

  nginx:
    image: nginx:1.17-alpine
    container_name: laravelapp-nginx
    restart: unless-stopped
    ports:
      - 8008:80
    volumes:
      - ./src:/var/www
      - ./.docker/nginx:/etc/nginx/conf.d
    networks:
      - laravelapp

Now run the following command to build your image.

docker-compose build

The command might take some time depends on the internet and the local image and will give you the following output.

Step 1/10 : FROM php:7.4-fpm
 ---> 14098ddb1a4e
Step 2/10 : ARG user
 ---> Using cache
 ---> 33aea6c3ff59
Step 3/10 : ARG uid
 ---> Using cache
 ---> 03fa25740298
Step 4/10 : RUN apt-get update && apt-get install -y     git     curl     libpng-dev     libonig-dev     libxml2-dev     zip     unzip     && docker-php-ext-install pdo_mysql     && docker-php-ext-install mbstring     && docker-php-ext-install exif     && docker-php-ext-install pcntl     && docker-php-ext-install bcmath     && docker-php-ext-install gd     && docker-php-source delete
 ---> Using cache
 ---> a15df90680bd
Step 5/10 : RUN apt-get clean && rm -rf /var/lib/apt/lists/*
 ---> Using cache
 ---> b79d72fdfb85
Step 6/10 : RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
 ---> Using cache
 ---> 369da557c7d4
Step 7/10 : RUN useradd -G www-data,root -u $uid -d /home/$user $user
 ---> Using cache
 ---> 7306589c9f2e
Step 8/10 : RUN mkdir -p /home/$user/.composer &&     chown -R $user:$user /home/$user
 ---> Using cache
 ---> bc6d6f5a255f
Step 9/10 : WORKDIR /var/www
 ---> Using cache
 ---> 1f9f25b46e60
Step 10/10 : USER $user
 ---> Using cache
 ---> cbd29387097c

Successfully built cbd29387097c
Successfully tagged laravelapp:latest

Now run your containers with the following command.

docker-compose up -d

It will give you the following output.

Creating laravelapp-nginx ... done
Creating laravelapp-db    ... done
Creating laravelapp-app   ... done

Now you can check the running containers through the following command.

docker ps

And you will see the following output.

CONTAINER ID   IMAGE               STATUS         PORTS
67527d83832a   nginx:1.17-alpine   Up 7 minutes   0.0.0.0:8008->80/tcp
f29812e8f5be   laravelapp          Up 7 minutes   9000/tcp
fa218dd63b3d   mysql:5.7           Up 7 minutes   3306/tcp, 33060/tcp

Working With Laravel in Docker Container

Now we have multiple options for working with Laravel in Docker container.

Get Access Through Container ID

The first option to access the Laravel development environment by accessing the container with Container ID.

Run the following command to get the container id.

docker ps

And you will get the following output

CONTAINER ID   NAMES              IMAGE               STATUS        PORTS
67527d83832a   laravelapp-nginx   nginx:1.17-alpine   Up 19 hours   0.0.0.0:8008->80/tcp
f29812e8f5be   laravelapp-app     laravelapp          Up 19 hours   9000/tcp
fa218dd63b3d   laravelapp-db      mysql:5.7           Up 19 hours   3306/tcp, 33060/tcp

Our Laravel app is running on f29812e8f5be container id. To access this container, we need to run the following command.

docker exec -it f29812e8f5be bash

The above command provides you the access to the Laravel working directory where you can run the composer or artisan commands.

dean@f29812e8f5be:/var/www$ php artisan -V

The above command will output you the current version of Laravel.

Laravel Framework 8.20.1

I already installed Laravel by running the composer install command.

Access Through Container Name

The second option to access the Laravel Development environment through Container Name.

docker-compose exec app php artisan -V

Please note that we have used the docker-compose instead of the docker. Where the app is suffix from the Laravel app container.

      Name                    Command              State          Ports        
-------------------------------------------------------------------------------
laravelapp-app     docker-php-entrypoint php-fpm   Up      9000/tcp            
laravelapp-db      docker-entrypoint.sh mysqld     Up      3306/tcp, 33060/tcp 
laravelapp-nginx   nginx -g daemon off;            Up      0.0.0.0:8008->80/tcp

We hope this article will help you to learn the how to containerize laravel, nginx and mysql with docker compose. If you like it then please follow us on Facebook and Twitter.