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:
root@61f9e2fe31cd:/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:
root@61f9e2fe31cd:/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!