In this example we are going to use Docker's "(named) volumes" option to keep our application files/data. This option offers better security as opposed to "bind(shared) volumes" in production environment. For more details read Manage data in Docker page. Since we are not using "bind(shared) volumes" (this is generally used in development environment) we will be copying the application code into our "data container" called my_app. PHP-FPM and Nginx containers will then use this container to access the application data.


Caution


When you deploy your code to production, the changes you did to your application code won't be available unless you assign a different name to your volume. e.g. If it was named app_code_volume_1 before it should be something like app_code_volume_2 next time.


Structure


├── docker
│   ├── docker-compose.yml
│   ├── Makefile
│   ├── nginx
│   │   ├── app.conf
│   │   ├── Dockerfile
│   │   └── nginx.conf
│   ├── php
│   │   ├── Dockerfile
│   │   └── www.conf
│   └── source
│   └── Dockerfile
├── index.php
└── Readme.md

Files


docker/nginx/app.conf


server {
listen 80 default_server;

server_name localhost;

root /app;

index index.php;

location ~ \.php$ {
try_files $uri =404;
fastcgi_pass my_php:9000;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}

error_log /var/log/nginx/app_error.log;
access_log /var/log/nginx/app_access.log;
}

docker/nginx/Dockerfile


FROM nginx:1.15.8-alpine

COPY app.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/nginx.conf

WORKDIR /app

docker/nginx/nginx.conf


user nginx;

worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;

keepalive_timeout 65;

include /etc/nginx/conf.d/*.conf;
}

docker/php/Dockerfile


FROM php:7.2.13-fpm-alpine3.8

COPY www.conf /usr/local/etc/php-fpm.d/www.conf

WORKDIR /app

CMD ["php-fpm", "--nodaemonize"]

docker/php/www.conf


[global]
daemonize=no

[www]
user=www-data
group=www-data

listen=my_nginx:9000

pm=dynamic
pm.max_children=40
pm.start_servers=2
pm.min_spare_servers=2
pm.max_spare_servers=4
pm.max_requests=500

docker/source/Dockerfile


FROM tianon/true

COPY . /app

docker/docker-compose.yml


version: "3.4"

services:

my_app:
build:
context: ".."
dockerfile: "docker/source/Dockerfile"
volumes:
- "app_code:/app"

my_php:
build:
context: "./php"
hostname: "my-php"
volumes:
- "app_code:/app"
depends_on:
- "my_app"
environment:
PS1: "\\u@\\h:\\w\\$$ "

my_nginx:
build:
context: "./nginx"
hostname: "my-nginx"
ports:
- "4080:80"
volumes:
- "app_code:/app"
depends_on:
- "my_php"
environment:
PS1: "\\u@\\h:\\w\\$$ "

volumes:
app_code:
name: "app_code_volume"

index.php


echo 'UP';

Build


$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker_my_app latest fa430e0dd199 21 minutes ago 48.6kB
docker_my_php latest 642a68c15625 22 minutes ago 77.7MB
docker_my_nginx latest 9ac2a0cd6fb0 22 minutes ago 16.1MB
tianon/true latest 183cb5fd5414 7 months ago 125B

The docker_my_app_1 container will never run so don't get excited about it!


$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e1327e7c84fa docker_my_nginx "nginx -g 'daemon of…" 22 minutes ago Up 22 minutes 0.0.0.0:4080->80/tcp docker_my_nginx_1
97a336f95a6b docker_my_php "docker-php-entrypoi…" 22 minutes ago Up 22 minutes 9000/tcp docker_my_php_1
2117d5b517ea docker_my_app "/true" 22 minutes ago Exited (0) 22 minutes ago docker_my_app_1

$ docker inspect app_code_volume
[
{
"CreatedAt": "2019-05-23T20:36:01+01:00",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "docker",
"com.docker.compose.version": "1.21.2",
"com.docker.compose.volume": "app_code_volume"
},
"Mountpoint": "/var/lib/docker/volumes/app_code_volume/_data",
"Name": "app_code_volume",
"Options": null,
"Scope": "local"
}
]

$ sudo ls -l /var/lib/docker/volumes/app_code_volume/_data
total 12
drwxr-xr-x 5 root root 4096 May 23 20:36 docker
-rw-r--r-- 1 root root 18 May 22 22:49 index.php
-rw-r--r-- 1 root root 124 May 23 19:27 Readme.md

Test


$ curl -i 0.0.0.0:4080
HTTP/1.1 200 OK
Server: nginx/1.15.8
Date: Thu, 23 May 2019 19:39:08 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive
X-Powered-By: PHP/7.2.13

UP

As you know we copied the application data into our data container but they are still accessible in PHP-FPM and Nginx containers. See below.


$ docker exec -it docker_my_php_1 ls -l
total 12
-rw-r--r-- 1 root root 124 May 23 18:27 Readme.md
drwxr-xr-x 5 root root 4096 May 23 19:36 docker
-rw-r--r-- 1 root root 18 May 22 21:49 index.php

$ docker exec -it docker_my_nginx_1 ls -l
total 12
-rw-r--r-- 1 root root 124 May 23 18:27 Readme.md
drwxr-xr-x 5 root root 4096 May 23 19:36 docker
-rw-r--r-- 1 root root 18 May 22 21:49 index.php