DEV Community

Cover image for How to install and set up Laravel's local development environment with Docker Compose
ucan_lab
ucan_lab

Posted on • Updated on

How to install and set up Laravel's local development environment with Docker Compose

Introduction

This article explains how to build a LEMP environment for PHP, nginx, and MySQL with Docker and Docker Compose.

Prerequisites

  • Docker
  • Git
$ docker -v
Docker version 20.10.5, build 55c4c88

$ git --version
git version 2.31.1
Enter fullscreen mode Exit fullscreen mode

How to enable Docker Content Trust (DCT)

DCT is a security feature that protects your Docker image from spoofing and tampering.
It works automatically when the push, build, create, pull, or run command is executed.

$ echo export DOCKER_CONTENT_TRUST=1 >> ~/.zshrc
Enter fullscreen mode Exit fullscreen mode

Repository

Container Structures

It has a three-tier structure consisting of an application server, a web server, and a database server.

├── app - service running php-fpm
├── web - service running nginx
└── db - service running mysql
Enter fullscreen mode Exit fullscreen mode

How to use a new project

Step1. Create a new repository from template

Click Use this template.

Create a repository for my-project.

If you use the template repository, all commits will be combined into one.

Step2. Clone your GitHub repository

$ git clone git@github.com:ucan-lab/my-project.git
$ cd my-project
Enter fullscreen mode Exit fullscreen mode

Step3. Create a Laravel Project

$ make create-project
Enter fullscreen mode Exit fullscreen mode

Step4. Show the Laravel Welcome page

http://localhost

Step5. First commit and push

$ git add .
$ git commit -m "laravel install"
$ git push -u origin HEAD
Enter fullscreen mode Exit fullscreen mode

How to use an existing project

(Optional) Delete the local repository

$ docker-compose down -v --rmi all
$ cd ..
$ rm -rf my-project
Enter fullscreen mode Exit fullscreen mode

Step1. Clone your GitHub repository

$ git clone git@github.com:ucan-lab/my-project.git
$ cd my-project
Enter fullscreen mode Exit fullscreen mode

Step2. Initialize your local environment

$ make init
Enter fullscreen mode Exit fullscreen mode

Step3. Show Laravel Welcome page

http://localhost

If you want to specify the version of Laravel

Rewrite Makefile and execute.

laravel-install:
    docker-compose exec app composer create-project --prefer-dist "laravel/laravel=6.*" .
Enter fullscreen mode Exit fullscreen mode

Remarks

docker-compose.yml

version: "3.9"
volumes:
  php-fpm-socket:
  db-store:
services:
  app:
    build:
      context: .
      dockerfile: ./infra/docker/php/Dockerfile
    volumes:
      - type: volume
        source: php-fpm-socket
        target: /var/run/php-fpm
        volume:
          nocopy: true
      - type: bind
        source: ./backend
        target: /work/backend
    environment:
      - DB_CONNECTION=mysql
      - DB_HOST=db
      - DB_PORT=3306
      - DB_DATABASE=${DB_NAME:-laravel_local}
      - DB_USERNAME=${DB_USER:-phper}
      - DB_PASSWORD=${DB_PASS:-secret}

  web:
    build:
      context: .
      dockerfile: ./infra/docker/nginx/Dockerfile
    ports:
      - target: 80
        published: ${WEB_PORT:-80}
        protocol: tcp
        mode: host
    volumes:
      - type: volume
        source: php-fpm-socket
        target: /var/run/php-fpm
        volume:
          nocopy: true
      - type: bind
        source: ./backend
        target: /work/backend

  db:
    build:
      context: .
      dockerfile: ./infra/docker/mysql/Dockerfile
    ports:
      - target: 3306
        published: ${DB_PORT:-3306}
        protocol: tcp
        mode: host
    volumes:
      - type: volume
        source: db-store
        target: /var/lib/mysql
        volume:
          nocopy: true
    environment:
      - MYSQL_DATABASE=${DB_NAME:-laravel_local}
      - MYSQL_USER=${DB_USER:-phper}
      - MYSQL_PASSWORD=${DB_PASS:-secret}
      - MYSQL_ROOT_PASSWORD=${DB_PASS:-secret}
Enter fullscreen mode Exit fullscreen mode

version

Docker Compose file version

Note: When specifying the Compose file version to use, make sure to specify both the major and minor numbers. If no minor version is given, 0 is used by default and not the latest minor version. As a result, features added in later versions will not be supported. For example:

version: "3"
Enter fullscreen mode Exit fullscreen mode

version: "3"

version: "3.0"
Enter fullscreen mode Exit fullscreen mode

volumes

Define named volumes with the top-level volumes key to reuse volumes across multiple services.

services.*.build

services.*.ports

services.*.volumes

services.*.environment

app service

./infra/docker/php/Dockerfile

FROM php:8.0-fpm-buster
LABEL maintainer="ucan-lab <yes@u-can.pro>"
SHELL ["/bin/bash", "-oeux", "pipefail", "-c"]

# timezone environment
ENV TZ=UTC \
  # locale
  LANG=en_US.UTF-8 \
  LANGUAGE=en_US:en \
  LC_ALL=en_US.UTF-8 \
  # composer environment
  COMPOSER_ALLOW_SUPERUSER=1 \
  COMPOSER_HOME=/composer

COPY --from=composer:2.0 /usr/bin/composer /usr/bin/composer

RUN apt-get update && \
  apt-get -y install git libicu-dev libonig-dev libzip-dev unzip locales && \
  apt-get clean && \
  rm -rf /var/lib/apt/lists/* && \
  locale-gen en_US.UTF-8 && \
  localedef -f UTF-8 -i en_US en_US.UTF-8 && \
  mkdir /var/run/php-fpm && \
  docker-php-ext-install intl pdo_mysql zip bcmath && \
  composer config -g process-timeout 3600 && \
  composer config -g repos.packagist composer https://packagist.org

COPY ./infra/docker/php/php-fpm.d/zzz-www.conf /usr/local/etc/php-fpm.d/zzz-www.conf
COPY ./infra/docker/php/php.ini /usr/local/etc/php/php.ini

WORKDIR /work/backend
Enter fullscreen mode Exit fullscreen mode

./infra/docker/php/php.ini

zend.exception_ignore_args = off
expose_php = on
max_execution_time = 30
max_input_vars = 1000
upload_max_filesize = 64M
post_max_size = 128M
memory_limit = 256M
error_reporting = E_ALL
display_errors = on
display_startup_errors = on
log_errors = on
error_log = /dev/stderr
default_charset = UTF-8

[Date]
date.timezone = ${TZ}

[mysqlnd]
mysqlnd.collect_memory_statistics = on

[Assertion]
zend.assertions = 1

[mbstring]
mbstring.language = Neutral
Enter fullscreen mode Exit fullscreen mode

./infra/docker/php/php-fpm.d/zzz-www.conf

[www]
listen = /var/run/php-fpm/php-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0666
access.log = /dev/stdout
Enter fullscreen mode Exit fullscreen mode

web service

./infra/docker/nginx/Dockerfile

FROM node:16-alpine as node
FROM nginx:1.20-alpine
LABEL maintainer="ucan-lab <yes@u-can.pro>"
SHELL ["/bin/ash", "-oeux", "pipefail", "-c"]

ENV TZ=UTC

RUN apk update && \
  apk add --update --no-cache --virtual=.build-dependencies g++

# node command
COPY --from=node /usr/local/bin /usr/local/bin
# npm command
COPY --from=node /usr/local/lib /usr/local/lib
# yarn command
COPY --from=node /opt /opt
# nginx config file
COPY ./infra/docker/nginx/*.conf /etc/nginx/conf.d/

WORKDIR /work/backend
Enter fullscreen mode Exit fullscreen mode

./infra/docker/nginx/default.conf

access_log /dev/stdout main;
error_log /dev/stderr warn;

server {
    listen 80;
    root /work/backend/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
}
Enter fullscreen mode Exit fullscreen mode

db service

./infra/docker/mysql/Dockerfile

FROM mysql/mysql-server:8.0
LABEL maintainer="ucan-lab <yes@u-can.pro>"

ENV TZ=UTC

COPY ./infra/docker/mysql/my.cnf /etc/my.cnf
Enter fullscreen mode Exit fullscreen mode

./infra/docker/mysql/my.cnf

[mysqld]
# default
skip-host-cache
skip-name-resolve
datadir = /var/lib/mysql
socket = /var/lib/mysql/mysql.sock
secure-file-priv = /var/lib/mysql-files
user = mysql

pid-file = /var/run/mysqld/mysqld.pid

# character set / collation
character_set_server = utf8mb4
collation_server = utf8mb4_0900_ai_ci

# timezone
default-time-zone = SYSTEM
log_timestamps = SYSTEM

# Error Log
log-error = mysql-error.log

# Slow Query Log
slow_query_log = 1
slow_query_log_file = mysql-slow.log
long_query_time = 1.0
log_queries_not_using_indexes = 0

# General Log
general_log = 1
general_log_file = mysql-general.log

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4
Enter fullscreen mode Exit fullscreen mode

Top comments (3)

Collapse
 
tobinguyen profile image
Tobi

@ucan_lab
Hi, I'm learning docker and I clone your repo to try it but I have an issue with app source code permission. Docker create the laravel app with the files/directories owner is root and on the host machine, I'm using a non-root user so I can't edit files or directories. What should i do?

Collapse
 
patilswapnilv profile image
Swapnil V. Patil

@ucan_lab Time to update the article to match with the latests updates?

Collapse
 
calvez profile image
calvez

good stuff