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.

Related Posts

Setting up laravel with docker: Part 1

Setting up laravel with docker: Part 3