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
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
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
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
Step3. Create a Laravel Project
$ make create-project
Step4. Show the Laravel Welcome page
Step5. First commit and push
$ git add .
$ git commit -m "laravel install"
$ git push -u origin HEAD
How to use an existing project
(Optional) Delete the local repository
$ docker-compose down -v --rmi all
$ cd ..
$ rm -rf my-project
Step1. Clone your GitHub repository
$ git clone git@github.com:ucan-lab/my-project.git
$ cd my-project
Step2. Initialize your local environment
$ make init
Step3. Show Laravel Welcome page
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.*" .
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}
version
Docker Compose file version
- https://docs.docker.com/compose/compose-file
- https://docs.docker.com/compose/compose-file/compose-versioning/#version-3
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"
version: "3"
version: "3.0"
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
-
https://hub.docker.com/_/php
FROM <image>:<verion>-<variant>-<os>
-
https://laravel.com/docs/8.x/deployment#server-requirements
- PHP >= 7.3
- BCMath PHP Extension
- Ctype PHP Extension
- Fileinfo PHP Extension
- JSON PHP Extension
- Mbstring PHP Extension
- OpenSSL PHP Extension
- PDO PHP Extension
- Tokenizer PHP Extension
- XML PHP Extension
./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
./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
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
./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;
}
}
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
./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
Top comments (3)
@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?
@ucan_lab Time to update the article to match with the latests updates?
good stuff