Installing and Running Laravel with Docker

Download and install Laravel from repository:

Clone Laravel and create project from repository:

git clone https://github.com/laravel/laravel.git laravel-sandbox

Get into the folder and run composer

cd laravel-sandbox
sudo docker run --rm -v "$(pwd)":/app composer install

Set permissions on the project directory so that it is owned by your non-root user:

sudo chown -R $USER:$USER .

Create and set up docker-compose.yml

Since we feel lucky today, we’ll create our docker-compose file with some customization over the containers names and ports redirects, in order not to use too common names and ports, that might conflict with some other containers on our host.

You can take a look at the final version of the docker-compose.yml file some lines below.

We’ll create 3 containers:

  • app named as app101 which holds our laravel app. Feeds from the Dockerfile we’ll create later.
  • webserver named as webserver2 which holds our web-server; nginx for this example. This service exposes port 8101 for default 80, and 8102 for 443, ad it feeds from the image nginx:alpine
  • db named as db2 which holds the MySQL service. This service exposes port 8103 for default 3306. We also must set the database name (laravel_docker) and the mysql password for root user (password admin for this example).

Persisting Data

We’ll add some extra configuration to our docker-compose file; we need to add volumes to our db service. Without them, every time the container stops or fails, all data would be missing.

db:
  ...
    volumes:
      - dbdata:/var/lib/mysql
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network
  ...

So here we are telling that everything inside /var/lib/mysql will be replicated into the dbdata folder of our host. Plus, we persist the configuration of the database on /etc/mysql/my.cnf.

We also set the volume to be mounted on the local driver:

#Volumenes
volumes:
  dbdata:
    driver: local

Our final docker-compose.yml file looks like:

version: '3'
services:

  #PHP
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: laravel-docker
    container_name: app101
    restart: unless-stopped
    tty: true
    environment:
      SERVICE_NAME: app
      SERVICE_TAGS: dev
    working_dir: /var/www
    volumes:
      - ./:/var/www
      - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - app-network

  #Nginx
  webserver:
    image: nginx:alpine
    container_name: webserver2
    restart: unless-stopped
    tty: true
    ports:
      - "8101:80"
      - "8102:443"
    volumes:
      - ./:/var/www
      - ./nginx/conf.d/:/etc/nginx/conf.d/
    networks:
      - app-network

  #MySQL
  db:
    image: mysql:5.7.22
    container_name: db2
    restart: unless-stopped
    tty: true
    ports:
      - "8103:3306"
    environment:
      MYSQL_DATABASE: laravel_docker
      MYSQL_ROOT_PASSWORD: admin
      SERVICE_TAGS: dev
      SERVICE_NAME: mysql
    volumes:
      - dbdata:/var/lib/mysql/
      - ./mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - app-network

#Redes
networks:
  app-network:
    driver: bridge
#Volumenes
volumes:
  dbdata:
    driver: local

Create Dockerfile

Now for making docker-compose to run, we need to create a Dockerfile file to set the image we named as laravel-sandbox, and set php, folders and ports as well.


FROM php:7.2-fpm

# Copiar composer.lock y composer.json
COPY composer.lock composer.json /var/www/

# Configura el directorio raiz
WORKDIR /var/www

# Instalamos dependencias
RUN apt-get update && apt-get install -y \
    build-essential \
    mysql-client \
    libpng-dev \
    libjpeg62-turbo-dev \
    libfreetype6-dev \
    locales \
    zip \
    jpegoptim optipng pngquant gifsicle \
    vim \
    unzip \
    git \
    curl

# Borramos cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Instalamos extensiones
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl
RUN docker-php-ext-configure gd --with-gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include/
RUN docker-php-ext-install gd

# Instalar composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer

# agregar usuario para la aplicación laravel
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Copiar el directorio existente a /var/www
COPY . /var/www

# copiar los permisos del directorio de la aplicación
RUN chown -R www-data:www-data /var/www

# cambiar el usuario actual por www
USER www

# exponer el puerto 9000 e iniciar php-fpm server
EXPOSE 9000
CMD ["php-fpm"]

Setting PHP

Create file laravel-sandbox/php/local.ini

upload_max_filesize=100M
post_max_size=100M

Setting Nginx

Create file laravel-sandox/nginx/conf.d/app.conf

server {
    listen 80;
    index index.php index.html;
    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/public;
    location ~ \.php$ {
        try_files $uri =404;
        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;
    }
    location / {
        try_files $uri $uri/ /index.php?$query_string;
        gzip_static on;
    }
}

Setting MySQL

Create file laravel-sandbox/mysql/my.cnf

[mysqld]
general_log = 1
general_log_file = /var/lib/mysql/general.log

Running the container

Firt copy and rename the .env envioroment file for our app:

cp .env.example .env

And set the correct values for the database connection due the values set on our docker-compose.yml

DB_CONNECTION=mysql
DB_HOST=db
DB_PORT=3306
DB_DATABASE=laravel_docker
DB_USERNAME=admin
DB_PASSWORD=somesecretkey

And finally we can run the container

docker-compose up -d

12- Now, if everything went as expected and if type from the console

docker ps

We should see something as:

CONTAINER ID        IMAGE                     COMMAND                  CREATED             STATUS              PORTS                                         NAMES
b031eb9c5eb2        laravel-docker            "docker-php-entryp..."   5 minutes ago         Up 5 minutes          9000/tcp                                      app101
02b74ec07bcb        mysql:5.7.22              "docker-entrypoint..."   5 minutes ago         Up 5 minutes          0.0.0.0:8103->3306/tcp                        db2
6bbce3b899fc        nginx:alpine              "nginx -g 'daemon ..."   5 minutes ago         Up 5 minutes          0.0.0.0:8101->80/tcp, 0.0.0.0:8102->443/tcp   webserver2

We can run now the 2 extra following commands from the console for adding some security to our app.

docker-compose exec app php artisan key:generate
docker-compose exec app php artisan config:cache

So we added our encrypted key for the container and cached our configuration.

Migrating data

Now we have everything up && running, we should be able to see our app from the browser (http://localhost)

For migrating data, we first must create an user for the database. So we first we access to the database this way:

docker-compose exec db bash

And then we can access to the mysql client

mysql -u root -p

The console will prompt asking for the password (MYSQL_ROOT_PASSWORD param on docker-compose.yml)

Now we should be able to see the database by typing:

mysql> show databases;

And create the user by typing the following commands:

mysql> GRANT ALL ON laravel_docker.* TO 'admin'@'%' IDENTIFIED BY 'somesecretkey';
mysql> FLUSH PRIVILEGES;
mysql> EXIT;

Now we can skip out from the container;

exit

Now we can permform the migrations;

docker-compose exec app php artisan migrate

ADDING AUTH AND SEEDING A USER FOR LOGIN

At this point, we might be interested on adding a user to the databases and performing actions for loged-in users. So from the console we perform make:auth:

docker-compose exec app php artisan make:auth

Then we can add some /database/seeds/UsersTableSeeder.php to our project and enable it on DatabaseSeeder.php

DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
}

UsersTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        \DB::table('users')->insert(array(
            'name'  =>  'admin',
            'email' =>  'admin@domain.com',
            'password'  => bcrypt('adminpwd')
        ));
    }
}

Now we execute composer dump-autoload in order to make noticeof the new generated file:

docker-compose exec app composer dump-autoload

And fillally we proceed to execute the seeder

docker-compose exec app php artisan db:seed

Now we can login into our app with user specified on UsersTableSeeder.php!