Setting up Laravel with Docker - Part 2
In part 1 of this post, we built a docker image to run Laravel application. In this post we will install the same Laravel application but with docker compose.
By Subash Adhikari · · 5 min read
Updated . Written in 2017. Commands, prices, and links in this piece may be out of date.

In part 1 of this series, we built a Docker image to run a Laravel application. In this post we'll install the same Laravel application, but this time with docker-compose. Docker compose makes it easy to work with multi container applications, and it saves you from typing the long Docker commands we used in part 1. We'll also add redis for caching and mysql for data storage, alongside nginx and PHP.
Instead of building our own images, we'll use the official PHP, nginx and redis images. Then we'll use docker-compose to glue them all together into a working Laravel application.
Directory structure#
The directory structure of the application will look like this:
+-- laravel
| +-- docker
| | +-- Dockerfile
| | +-- default
| +-- docker-compose.yml
| +-- appBuilding PHP image#
Let's start by writing the Dockerfile for our PHP container.
FROM php:7.0-fpm
LABEL maintainer="Subash Adhikari <me@subash.tech>"The Dockerfile uses the php:7.0-fpm image as its base and sets the maintainer details.
We use the RUN command to define the container's resources at runtime. We update the apt repository and install the dependencies needed by Laravel and Composer.
RUN apt-get update \
&& apt-get install -y git zlib1g-dev zip unzip \
&& php -r "readfile('http://getcomposer.org/installer');" | \
php -- --install-dir=/usr/bin/ --filename=composer \The PHP fpm container comes with a utility script called docker-php-ext-install that helps install PHP extensions. We use it to install the pdo_mysql extension, which Laravel needs to connect to the mysql database. We also install the PHP zip extension, which Composer needs.
&& docker-php-ext-install pdo_mysql zip \And finally we clean up.
&& apt-get -y autoremove && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/www/html/*Here's the final Dockerfile.
FROM php:7.0-fpm
LABEL maintainer="Subash Adhikari <me@subash.tech>"
RUN apt-get update \
&& apt-get install -y git zlib1g-dev zip unzip \
&& php -r "readfile('http://getcomposer.org/installer');" | \
php -- --install-dir=/usr/bin/ --filename=composer \
&& docker-php-ext-install pdo_mysql zip \
&& apt-get -y autoremove && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/www/html/*Docker compose#
Docker compose lets you work with multiple containers without typing long Docker commands. We'll start by adding the following to docker-compose.yml.
The first line says we want version 2 of docker compose. Then we create a couple of volumes and a network to share between containers. All the containers go into app-network so they can talk to each other.
version: '2'
volumes:
mysqldata:
driver: "local"
redisdata:
driver: "local"
networks:
app-network:
driver: "bridge"In docker compose, each container is called a service. Let's start with our main app container.
services:
app:
build:
context: ./docker
dockerfile: Dockerfile
image: adikari/laravel
volumes:
- ./app/:/var/www/html/
networks:
- app-networkThis tells docker compose to use the adikari/laravel image if it exists, and otherwise build one from the Dockerfile in the ./docker directory. We then mount the ./app directory to /var/www/html in the container. Finally, we add the container to app-network.
Next comes the nginx container. Instead of installing nginx by hand like in part 1, we'll just use the official nginx docker image.
nginx:
image: nginx:1.12-alpine
volumes:
- ./app/:/var/www/html/
- ./docker/default:/etc/nginx/conf.d/default.conf
ports:
- 8000:80
networks:
- app-networkWe build this container from the nginx:1.12-alpine image. We mount the app/ directory to /var/www/html in the container, and also put the ./docker/default nginx configuration inside it. Port 80 of the container is shared as port 8000 on the host machine. Finally, we add the container to the app-network we created.
Next we'll create the mysql container, using the official mysql docker image from Docker Hub.
mysql:
image: mysql:5.7
environment:
MYSQL_DATABASE: homestead
MYSQL_USER: homestead
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: secret
volumes:
- mysqldata:/var/lib/mysql
networks:
- app-networkWe set some environment variables for the mysql container. When it starts, it creates the homestead database with the given user, password and root password. We'll use these details later to connect to the database. We mount the volume we created earlier at /var/lib/mysql. Because it's a named volume, the data sticks around even after the container is destroyed. Finally we add the mysql container to the same app-network network.
Next is the redis container, using the official redis docker image. We mount the redisdata volume and add the container to the same network as the others.
redis:
image: redis:4.0-alpine
volumes:
- redisdata:/data
networks:
- app-networkHere's the final docker-compose.yml.
version: '2'
services:
app:
build:
context: ./docker
dockerfile: Dockerfile
image: adikari/laravel
volumes:
- ./app/:/var/www/html/
networks:
- app-network
nginx:
image: nginx:1.12-alpine
volumes:
- ./app/:/var/www/html/
- ./docker/default:/etc/nginx/conf.d/default.conf
ports:
- 8000:80
networks:
- app-network
mysql:
image: mysql:5.7
environment:
MYSQL_DATABASE: homestead
MYSQL_USER: homestead
MYSQL_PASSWORD: secret
MYSQL_ROOT_PASSWORD: secret
volumes:
- mysqldata:/var/lib/mysql
networks:
- app-network
redis:
image: redis:4.0-alpine
volumes:
- redisdata:/data
networks:
- app-network
volumes:
mysqldata:
driver: "local"
redisdata:
driver: "local"
networks:
app-network:
driver: "bridge"Nginx configuration#
Here's our nginx configuration. It's a normal nginx config except for fastcgi_pass app:9000;. app is the name of our PHP container, which we set in docker-compose.yml.
server {
root /var/www/html/public;
index index.html index.htm index.php;
server_name _;
charset utf-8;
location = /favicon.ico { log_not_found off; access_log off; }
location = /robots.txt { log_not_found off; access_log off; }
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
# include snippets/fastcgi-php.conf;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass app:9000;
fastcgi_index index.php;
}
error_page 404 /index.php;
location ~ /\.ht {
deny all;
}
}Installing Laravel#
We now have everything we need to install Laravel. Let's use the docker compose run command to execute composer create-project in the app container and install Laravel.
docker-compose run --rm -w /var/www/ app \
composer create-project --prefer-dist laravel/laravel htmlNext we install predis, the PHP package redis needs to work.
docker-compose run --rm -w /var/www/html app composer require predis/predisMake the following changes in the app/.env file.
DB_HOST=mysql
REDIS_HOST=redis
CACHE_DRIVER=redis
SESSION_DRIVER=redisRun this command to start all the Docker containers.
docker-compose upGo to http://localhost:8000 in your browser to see the Laravel application.
Next we'll scaffold all the routes and views we need for authentication.
docker-compose run --rm -w /var/www/html app php artisan make:authAnd finally, we run the migration to create the tables authentication needs.
docker-compose run --rm -w /var/www/html app php artisan migrateThat's it. Congratulations!
Conclusion#
We've dockerized a Laravel application with nginx, mysql and redis, using docker compose to glue several containers together to serve it. In part 3 we'll write a small utility script that makes it easy to work with the Docker containers.
You can download the full source code for this post from github.
Related Posts#
Setting up laravel with docker: Part 1
Setting up laravel with docker: Part 3
Related posts
Thanks for reading. You can also follow along on X or with RSS.