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.
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 use docker-compose to do so. Docker compose allows to easily work with multi container applications and prevents from having to type large docker commands as we did in part 1. We will also use redis for caching and mysql for data storage along with nginx and PHP.
We will use official PHP, nginx and redis images instead of building our own. We will then use docker-compose
to glue them all together and have a working laravel application.
Directory structure
The directory structure of the application will look as follows:
+-- laravel
| +-- docker
| | +-- Dockerfile
| | +-- default
| +-- docker-compose.yml
| +-- app
Building PHP image
Lets start writing Dockerfile
and build our PHP container.
FROM php:7.0-fpm
LABEL maintainer="Subash Adhikari <[email protected]>"
Our Dockerfile
uses php:7.0-fpm
docker image as base and set the maintainer details.
We use RUN command to define the container’s resources on runtime. We update the apt repository and install dependencies required 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 \
PHP fpm container comes with a utility script docker-php-ext-install
that helps to install PHP extensions. We use that script to install pdo_mysql
extension which is required by laravel to connect to mysql
database. We also install php zip extension required by composer.
&& docker-php-ext-install pdo_mysql zip \
And finally we cleanup.
&& apt-get -y autoremove && apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /var/www/html/*
Our final Dockerfile looks as follows.
FROM php:7.0-fpm
LABEL maintainer="Subash Adhikari <me@subash.com.au>"
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 allows to easily work with multiple containers without having to type large docker commands. We will start by adding following commands to docker-compose.yml
.
We specify that we want to use version 2 of docker compose in first line. We then create some volumes and network to use between containers. We will add all containers in app-network
so they can communicate with each other.
version: '2'
volumes:
mysqldata:
driver: "local"
redisdata:
driver: "local"
networks:
app-network:
driver: "bridge"
Each containers in docker compose is referred as services
. Lets start by adding our main app
container.
services:
app:
build:
context: ./docker
dockerfile: Dockerfile
image: adikari/laravel
volumes:
- ./app/:/var/www/html/
networks:
- app-network
We specify docker compose to use image adikari/laravel
if available. If not create one using the Dockerfile
in the ./docker
directory. We then mount current working directory ./app
to /var/www/html
of the container. Finally, we add the container to app-network
.
Next, we will build nginx
container. Instead of installing manually like in part 1, we will simply 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-network
We are using nginx:1.12-alpine
docker image to build the container. We mount the app/
directory to /var/www/html
in the container. We also copy the ./docker/default
nginx configuration in the container. We then share port 80
of the container to port 8000
of host machine. Finally, we add the container in the app-network
that we have created.
Next, we will create the mysql container. We will use official mysql docker image from dockerhub.
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
We set some environment variables which will be used by mysql container
. When the container starts, homestead
database will be created with the specified user, password and root password. We will use this details later to connect to the mysql database. We share the volume that we created earlier to /var/lib/mysql
. We created the named volume to retain the data even after the container is destroyed. Finally we add the mysql container in the same app-network
network.
Next, we will build the redis container. We are using official redis docker image. We mount the redisdata
volume and add the container to the same network as other containers.
redis:
image: redis:4.0-alpine
volumes:
- redisdata:/data
networks:
- app-network
Following is our 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
Following is our nginx configuration. Everything in this file is usual nginx configuration except for fastcgi_pass app:9000;
. app
is the name of our PHP container. We have specified this in our 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
At this point we have everything that we require to install the laravel application. Lets use 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 html
Next we install predis
for PHP which is required by redis to work.
docker-compose run --rm -w /var/www/html app composer require predis/predis
In the app/.env
file make the following modifications.
DB_HOST=mysql
REDIS_HOST=redis
CACHE_DRIVER=redis
SESSION_DRIVER=redis
Run following command to start all docker containers.
docker-compose up
Navigate to http://localhost:8000
in your local browser to access the Laravel application.
Next, we will scaffold all of the routes and views we need for authentication.
docker-compose run --rm -w /var/www/html app php artisan make:auth
And finally, we run the migration to create tables required for authentication.
docker-compose run --rm -w /var/www/html app php artisan migrate
Thats all. Congratulations!!
Conclusion
We have successfully dockerized Laravel application with nginx, mysql and redis. We have used docker compose to glue multiple containers to work togther and serve a Laravel application. In part 3 we will write a small utility script that will allow us to easily work with the docker container.
Full source code for this post can be downloaded from github.