Laravel docker header image

Intro

I started a new project yesterday, and as there are no backward compatibility considerations – Laravel 6 appeared on the horizon. As usual, when building the skeleton, I needed an updated docker configuration, to follow the mantra – Don’t work locally – DOCKERFILEEEES everywhere. Thus I decided to write out the process for making a fully working Laravel on docker config.

Suggested book (affiliate link): Laravel: Up & Running: A Framework for Building Modern PHP Apps

What’s inside:

  • Laravel 6.2
  • PHP 7.2
  • Nginx 1.16
  • MariaDB 10.4
  • Mailhog (latest)

Before we start

You have docker installed (download), you are excited about Laravel 6.x (docs), you know what Nginx is (refresher) and you have about 20 minutes. Right, let’s jump into it

Step 1. – The Dockerfiles

So, the way I structure my applications is as follows:

(...)
docker/
    app/
       Dockerfile
    web/
       Dockerfile
    db/
      Dockerfile
    (...)
(...)

As you can see, super clean and tidy. Go ahead and recreate the above in an empty new project, the Dockerfiles should be empty.

Step 1.1 – App Dockerfile

This is the big one, we need to set up the environment for the complete application container, think of this as – you’ve been given a freshly-installed Linux machine, and you need to get it up-and-running for Laravel to be added onto it. Here’s the app Dockerfile:

FROM php:7.3.0-fpm

# Pre-requisites installation
RUN apt-get update \
   && apt-get install -y libmcrypt-dev curl mcrypt git unzip zlib1g-dev libzip-dev zip \
   && pecl install mcrypt-1.0.2 \
   && docker-php-ext-install pdo_mysql bcmath zip \
   && docker-php-ext-enable mcrypt bcmath pdo_mysql sodium zip

# Composer installation
COPY --from=composer /usr/bin/composer /usr/bin/composer

So what exactly are we doing here? Well, first of all, we get the php 7.3.0 fpm base image that Docker provides us with (image), and then we start customizing it.

We install the crypt, curl, git AND zip php extensions, as Laravel lists them as pre-requisites, after a successful update & install of course.

Then we get the 1.0.2 mcrypt version, with pecl and install that

Finally, we run the docker-specific php extension installation and enabling procedures. The latter will be reflected in the enabled extensions within the container (you can check it by running php -m)

The last section, although quite ugly, I know, gets composer.phar that will be used for all the magical composer commands.

Step 1.2 – Web Dockerfile

This is the webserver layer, super simple stuff, feel free to customize it to your liking:

FROM nginx:1.16.0

COPY docker/vhosts.conf /etc/nginx/conf.d/default.conf

What is this vhosts.conf file you say, simply said – this is an instruction file for the server blocks distribution (an in-depth discussion). Add it to your base docker/ directory, with the following contents:

server {
   listen 80;
   index index.php index.html;
   root /var/www/html/public;

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

   location ~ \.php$ {
       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;
   }
}

Step 1.3 – DB Dockerfile

Again, super simple, nothing magical, expand it to your liking:

FROM mariadb:10.4

# Set the vars
ENV MYSQL_ROOT_PASSWORD rootpassword
ENV MYSQL_ALLOW_EMPTY_PASSWORD no
ENV MYSQL_RANDOM_ROOT_PASSWORD no
ENV MYSQL_USER meow
ENV MYSQL_PASSWORD meowpass
ENV MYSQL_DATABASE someProjectDB

EXPOSE 3306

The last section exposes the 3306 port, so that it’s accessible from my local machine so that I can hook PHPStorm’s database tab to it, and access the data from outside of the container. Think of it as a nice-to-have.

Step 2. – Wrap up

Wonderful, we have all the individual Dockerfiles defined and ready-to-go. To make it all work, we need an orchestrator, for local machines, this is usually docker-compose. The latter relies on one file that combines all of the above Dockefiles and makes a fully working application out of them – Maaaaagic.

The file we need is called docker-compose.yml file, and that sits at your root directory, don’t put it inside of your docker/ directory!

Here’s the content:

version: '3'
services:
  app:
    build:
      context: docker
      dockerfile: app/Dockerfile
    working_dir: /var/www/html
    networks:
      - your_project_network
  web:
    build:
      context: ./
      dockerfile: docker/web/Dockerfile
    working_dir: /var/www
    ports:
      - 8088:80
    volumes:
      - .:/var/www
    networks:
      - your_project_network
  mailhog:
    image: mailhog/mailhog
    ports:
      - 1025:1025
      - 8025:8025
    networks:
      - your_project_network
  database:
    container_name: your_project_db
    volumes:
      - ./docker/db:/var/lib/mysql
    build:
      context: docker
      dockerfile: database/Dockerfile
    ports:
      - 3306:3306
    networks:
      - your_project_network
networks:
  your_project_network:
    external: true

I’m not going to go into what each step does exactly, maybe in a latter tutorial post. Just a quick overview

  • We create 3 containers (app, web, DB)
  • We create the STMP sandbox (mailhog)
  • We bind volumes (local -> container && container -> local)
  • We link all of the containers in a singular network, with a bridge driver

Cool, so what’s next? Starting the thing of course!

Step 3. – Starting the stack

One thing you need to run as a pre-requisite – you need to create a new Docker network. Using the example naming above, here’s the command to do that:

docker network create your_project_network

Now, if you run the following command, you should see your new network with a bridge driver:

docker network ls

And the expected output:

$ docker network ls
NETWORK ID          NAME                   DRIVER              SCOPE
4039749a8702        bridge                 bridge              local
4c7f42423530        host                   host                local
18e8aa362cb8        none                   null                local
e4d641d7f8e2        your_project_network   bridge              local

Cool, now all you need to do is:

docker-compose up --build

After the above installation procedure is complete, you should have 4 containers running, check it with:

docker ps

And here’s the output (don’t worry about the difference in the container names):

$ docker ps
CONTAINER ID        IMAGE                COMMAND                  CREATED             STATUS              PORTS                                            NAMES
61f9e2fe31cd        project-t_app        "docker-php-entrypoi…"   19 hours ago        Up 19 hours         9000/tcp                                         project-t_app_1
a50670909b70        project-t_database   "docker-entrypoint.s…"   19 hours ago        Up 19 hours         0.0.0.0:3306->3306/tcp                           test_db
7289fcdfa358        project-t_web        "nginx -g 'daemon of…"   19 hours ago        Up 19 hours         0.0.0.0:8088->80/tcp                             project-t_web_1
46b8a170e2bf        mailhog/mailhog      "MailHog"                19 hours ago        Up 19 hours         0.0.0.0:1025->1025/tcp, 0.0.0.0:8025->8025/tcp   project-t_mailhog_1

Step 4. – Laravel YAY

If all of your containers are up and running, let’s install Laravel, so that you can finally start writing some code.

First, we need to jump into the container. The easiest way to do so, run the following command from within your project root directory:

docker-compose exec app bash

The above command will launch a bash session within the container, that is fully interactive.

Once within the container, run the following to double-check your pre-requisites:

php -v

We’re looking for this output:

[email protected]:/var/www# php -v
PHP 7.3.0 (cli) (built: Dec 29 2018 04:45:18) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies

And the following command (php extensions):

php -m

And, you’re looking for this list:

[email protected]:/var/www# php -m
[PHP Modules]
bcmath
Core
ctype
curl
date
dom
fileinfo
filter
ftp
hash
iconv
json
libxml
mbstring
mcrypt
mysqlnd
openssl
pcre
PDO
pdo_mysql
pdo_sqlite
Phar
posix
readline
Reflection
session
SimpleXML
sodium
SPL
sqlite3
standard
tokenizer
xml
xmlreader
xmlwriter
zip
zlib

[Zend Modules]

If the above match, follow the official Laravel install guide, found here: link

Here are the commands:

composer global require laravel/installer
----
composer create-project --prefer-dist laravel/laravel YOUR_PROJECT_NAME

Last step

Once the above installation has completed, visit localhost:8088, or change the port to whatever you fancy in the docker-compose file, and you should see the Laravel welcome screen:

Conclusion

You now have a fully working Laravel installation, map your DB creds and the Mailhog SMTP config settings and conquer the coding world!

Happy coding!