Dockerizing Laravel with Nginx MySQL and Docker Compose on Ubuntu 18.04 LTS

Laravel is a free and open source PHP framework that implements the MVC (Model-View-Controller) design pattern. It's designed with ease of use and allows developers to create both simple and complex applications within no time. Laravel was created by Taylor Otwell in 2011, as an attempt to provide an advanced alternative to the CodeIgniter (CI) framework. In 2011, Laravel released version 1 and version 2, and the latest version 5.6 comes with more and improved features like Command-Line (CLI) support named 'artisan', support for different database systems, Route improvements, etc.

In this guide, we're going to show you how to Dockerize the Laravel project with PHP-FPM, MySQL database, and the Nginx web server using the Docker Compose on the Ubuntu Server 18.04. We're going to create a new docker image for the Laravel Project, and then create the docker-compose.yml script that contains some services including, the App/Laravel itself, Nginx web server, and MySQL database.

Prerequisites

  • Ubuntu 18.04
  • Root privileges

What we will do:

  1. Install Docker and Docker Compose
  2. Download Laravel and Install Dependencies
  3. Dockerizing the Laravel Project
    • Define Laravel App Service
    • Define Nginx HTTP Service
    • Define MySQL Database Service
    • Create Nginx Virtual Host for Laravel
    • Create Dockerfile for Laravel App
  4. Build the Laravel Project
  5. Laravel Post-Installation

Step 1 - Install Docker and Docker Compose

Firstly, we're going to install Docker and Docker Compose packages to the Ubuntu system. And we will be using Docker packages from the Official Ubuntu repository.

Before going any further, we need to update repositories on the Ubuntu system. Simply by running the following command.

sudo apt update

Now install Docker and Docker Compose packages using the apt command below.

sudo apt install docker.io -y
sudo apt install docker-compose -y

The Docker and Docker Compose packages should now installed on the system, check it using the following commands.

docker version
docker-compose version

As a result, you will get the version of Docker and Docker Compose on the system.

Check docker version

Next, we need to assign the non-root user to the docker group in order to run the Docker container for non-root users.

For this case, we're going to add the user called 'hakase' to the docker group by running the following command.

usermod -a -G docker hakase

And after that, log in to the 'hakase' user shell and run the docker 'hello-world' command.

su - hakase
docker run hello-world

Now you will be displayed the 'Hello World' message from Docker, and the Docker installation has been completed.

Add docker user

Step 2 - Download Laravel and Install Dependencies

In this step,  we're going to download Laravel web-framework to the 'hakase' home directory and then install Laravel dependencies using the PHP 'composer' docker image. So, ensure that you're logged in to the server as a non-root user.

Download Laravel project to the 'myapp' directory and go into it.

git clone https://github.com/laravel/laravel.git myapp/
cd myapp/

Now run the following docker command in order to install Laravel dependencies.

docker run --rm -v $(pwd):/app composer install

With the command above, we're going to run a new temporary docker container and mount the 'myapp' project directory to the '/app' directory on the container. The container based on the 'composer' docker image, and we're installing Laravel dependencies using the 'composer' command inside that temporary container.

Get Laravel Framework

Once the Laravel dependencies installation is finished, we must change the owner of 'myapp' directory to our own user using sudo command below.

sudo chown -R $USER:$USER ~/myapp

Change owner of app directory

Step 3 - Dockerizing the Laravel Project

After downloading the Laravel and installing its dependencies, we're going to create a new docker-compose.yml script and create a new Dockerfile for the Laravel project.

cd myapp/
vim docker-compose.yml

- Define the Laravel App Service

Firstly, we're going to define the Laravel project itself and build the docker image for the Laravel project using the Dockerfile.

Paste the docker compose script into it.

version: '3'
services:

  #Laravel App
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: hakase-labs/laravel
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www/html
    volumes:
      - ./:/var/www/html
    networks:
      - mynet

Details of the Laravel container service:

  • The Laravel container service will be named as 'app'. It's based on our custom docker image that will be created with our 'Dockerfile', and the new image will be named as 'hakase-labs/laravel'.
  • We want to mount the 'myapp' project directory to the '/var/www/html' inside the container service.
  • We're using the custom docker network for our setup, the network will be named as 'mynet'.

- Define Nginx HTTP Service

Now we will define the nginx container service.

Paste the following configuration after the 'app' container service line.

  #Nginx Service
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www/html
      - ./nginx/conf.d/:/etc/nginx/conf.d/
      - ./nginx/ssl/:/etc/nginx/ssl/
    networks:
      - mynet

Configuration details of the nginx container service:

  • We want to create a new container named 'nginx' based on the docker image 'nginx:alpine'.
  • The container service will open the HTTP and HTTPS ports.
  • The container will mount three different volumes. The 'myapp' project directory to the '/var/www/html' directory, the nginx virtual host configuration 'nginx/conf.d/' to the '/etc/nginx/conf.d' directory, and mount certificate files 'nginx/ssl/' to the '/etc/nginx/ssl' directory on the container.
  • The container service will be using the same network called 'mynet'.

- Define MySQL Database Service

And the last, we define the MySQL database service.

Paste the following configuration after the 'nginx' container service line.

  #MySQL Service
  db:
    image: mysql:5.7
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laraveldb
      MYSQL_USER: laravel
      MYSQL_PASSWORD: laravelpassworddb
      MYSQL_ROOT_PASSWORD: rootpasswordmysql
    volumes:
      - mysqldata:/var/lib/mysql/
    networks:
      - mynet

#Docker Networks
networks:
  mynet:
    driver: bridge
#Volumes
volumes:
  mysqldata:
    driver: local

Save and close the configuration.

Details MySQL container service:

  • The MySQL container service will be named as 'db', based on the 'mysql:5.7' docker image.
  • The 'db' service will open the default MySQL port '3306'.
  • The Laravel project will be using the database, user, and the password based on the environment variable of the 'db' service.
  • The MySQL 'db' service will mount the volume named 'mysqldata', and have the same network 'mynet'.
  • And we define the custom network 'mynet' with the 'bridge' driver, and the 'mysqldata' volume will be using the 'local' driver.

Below is the completed 'docker-compose.yml' configuration:

version: '3'
services:

  #Laravel App
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: hakase-labs/laravel
    container_name: app
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www/html
    volumes:
      - ./:/var/www/html
    networks:
      - mynet

  #Nginx Service
  nginx:
    image: nginx:alpine
    container_name: nginx
    restart: unless-stopped
    tty: true
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www/html
      - ./nginx/conf.d/:/etc/nginx/conf.d/
      - ./nginx/ssl/:/etc/nginx/ssl/
    networks:
      - mynet

  #MySQL Service
  db:
    image: mysql:5.7
    container_name: db
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: laraveldb
      MYSQL_USER: laravel
      MYSQL_PASSWORD: laravelpassworddb
      MYSQL_ROOT_PASSWORD: rootpasswordmysql
    volumes:
      - mysqldata:/var/lib/mysql/
    networks:
      - mynet

#Docker Networks
networks:
  mynet:
    driver: bridge
#Volumes
volumes:
  mysqldata:
    driver: local

- Create Nginx Virtual Host for Laravel

Within the 'myapp' project directory, create a new directory named 'nginx' that will contain two other directories 'conf.d' and 'ssl'. Then create a new nginx virtual host configuration 'laravel.conf' inside the 'conf.d' directory.

Run the following command.

mkdir -p nginx/{conf.d,ssl}
vim nginx/conf.d/laravel.conf
server {
    listen 80;
    server_name laravel.hakase-labs.io;

    return 301 https://laravel.hakase-labs.io$request_uri;
}


server {
    listen 443 ssl http2;
    server_name laravel.hakase-labs.io;

    ssl_certificate /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    # Log files for Debug
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;

    # Laravel web root directory
    root /var/www/html/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }

    # Nginx Pass requests to PHP-FPM
    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;
    }
}

Save and close.

After that, copy your ssl certificate file to the 'nginx/ssl/' directory.

sudo cp /path/to/ssl/fullchain.pem nginx/ssl/
sudo cp /path/to/ssl/privkey.pem nginx/ssl/

- Create Dockerfile for the Laravel App

Next, create a new Dockerfile for the Laravel project.

Run the following command.

vim Dockerfile

Paste configuration below.

# Set master image
FROM php:7.2-fpm-alpine

# Copy composer.lock and composer.json
COPY composer.lock composer.json /var/www/html/

# Set working directory
WORKDIR /var/www/html

# Install Additional dependencies
RUN apk update && apk add --no-cache \
    build-base shadow vim curl \
    php7 \
    php7-fpm \
    php7-common \
    php7-pdo \
    php7-pdo_mysql \
    php7-mysqli \
    php7-mcrypt \
    php7-mbstring \
    php7-xml \
    php7-openssl \
    php7-json \
    php7-phar \
    php7-zip \
    php7-gd \
    php7-dom \
    php7-session \
    php7-zlib

# Add and Enable PHP-PDO Extenstions
RUN docker-php-ext-install pdo pdo_mysql
RUN docker-php-ext-enable pdo_mysql

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

# Remove Cache
RUN rm -rf /var/cache/apk/*

# Add UID '1000' to www-data
RUN usermod -u 1000 www-data

# Copy existing application directory permissions
COPY --chown=www-data:www-data . /var/www/html

# Change current user to www
USER www-data

# Expose port 9000 and start php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

Save and close the configuration.

And we're ready to build the 'myapp' Laravel project and run container services that we've defined.

Step 4 - Build Laravel with Nginx and MySQL Services

Build the custom docker image for our Laravel project using the following command.

docker-compose build

Build Laravel with Nginx and MySQL Services

Then run again the following command.

docker-compose up -d

It will download all docker images as we need and then build container services based on the 'docker-compose.yml' configuration.

Download docker images

When it's complete, verify using the docker-compose command below.

docker-compose ps

And as a result, you will get the three container services are up and running. The 'app' that is running on default port '9000' PHP-FPM, the service 'nginx' is on the HTTP and HTTPS ports, and the MySQL 'db' service on the default MySQL port '3306'.

After that, verify again all available docker image and the open ports on the system.

docker-compose images
netstat -plntu

You will get the custom docker image 'hakase-labs/laravel' on the list, and the HTTP and HTTPS port are on the 'LISTEN' state.

Check docker setup with netstat

Step 5 - Laravel Post-Installation

Until this stage, the Laravel project is up and running as a Docker container. And now we're going to create a new '.env' file, generate the key and migrate the Laravel data using the Laravel command line 'artisan'.

Copy the example of '.env' file and edit it inside the container.

cp .env.example .env
docker-compose exec app vim .env

Change the database configuration as below.

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laraveldb
DB_USERNAME=laravel
DB_PASSWORD=laravelpassworddb

Save and close.

Next, generate the Laravel application key and clear the cache configuration.

docker-compose exec app php artisan key:generate
docker-compose exec app php artisan config:cache

After that, migrate the database using the following command.

docker-compose exec app php artisan migrate

Ensure there is no error.

Laravel post installation

Now open your web browser and type the domain name of your project.

http://laravel.hakase-labs.io/

And you will be redirected to the secure HTTPS connection and will be displayed the default Laravel home page as below.

Laravel with Docker successfully installed

Finally, the Dockerizing of Laravel project with Nginx web server and MySQL database has been completed successfully.

Share this page:

Suggested articles

1 Comment(s)

Add comment

Comments

By: Rafi at: 2019-05-02 01:17:54

Thank you very much for the tutorial. It's very straight forward and easy to understand. I've finished all the procedures, no error during the process. The porblem is, I dont have a SSL certificate. It this step, you have mentined 

After that, copy your ssl certificate file to the 'nginx/ssl/' directory.

 

sudo cp /path/to/ssl/fullchain.pem nginx/ssl/sudo cp /path/to/ssl/privkey.pem nginx/ssl/

Now, after putting the URL in the bowser, there is error that "The site can't be reach". Can you please help me out here?