In this example we are going to run composer to install application dependencies then copy vendor folder and the rest of the application files into container image. With this way our image will always come with the application by default so all we have to do is create a container and start using it. This is a well known practise but I just wanted to touch upon a basic implementation. The main purpose is to isolate application code and not expose it to host OS. I am not going to list the benefits here because they all have been written on the Internet. The main problem with this solution is, the size of the final docker image will depend on the application and vendor folder size.


Structure


.
├── composer.json
├── docker
│   ├── docker-compose.yml
│   ├── Makefile
│   └── php
│   ├── Dockerfile
│   ├── php.ini
│   └── www.conf
├── .dockerignore
├── .gitignore
└── src
└── index.php

Files


I am just showing the content of important files. The rest is not too important.


Makefile


build:
@docker-compose build

up:
@docker-compose up -d

Dockerfile


#
# STAGE 1: composer
#
FROM composer:1.8.5 as composer

# Copy composer files from project root into composer container's working dir
COPY composer.* /app/

# Run composer to build dependencies in vendor folder
RUN set -xe \
&& composer install --no-dev --no-scripts --no-suggest --no-interaction --prefer-dist --optimize-autoloader

# Copy everything from project root into composer container's working dir
COPY . /app

# Generated optimized autoload files containing all classes from vendor folder and project itself
RUN composer dump-autoload --no-dev --optimize --classmap-authoritative

#
# STAGE 2: php
#
FROM php:7.2.13-fpm-alpine3.8

# Set container's working dir
WORKDIR /app

# Copy everything from project root into php container's working dir
COPY . /app
# Copy vendor folder from composer container into php container
COPY --from=composer /app/vendor /app/vendor

# Copy necessary files
COPY docker/php/php.ini /usr/local/etc/php/conf.d/php.override.ini
COPY docker/php/www.conf /usr/local/etc/php-fpm.d/www.conf

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

.dockerignore


.dockerignore
.gitignore
*.md
.git/
.idea/
.DS_Store/

docker-compose.yaml


This is optional but I am leaving it if you want to use it. You could use $ make -sC docker/ build up command in project root if you don't want to use native docker commands as I am using. Up to you!


version: "3.4"

services:

php:
build:
context: ".."
dockerfile: "docker/php/Dockerfile"
hostname: "php"

Build


Build image


$ docker build -t my_php_app:latest -f /absolute/path/to/project/docker/php/Dockerfile .

Successfully built c62632b82433
Successfully tagged my_php_app:latest

$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
my_php_app latest c62632b82433 5 minutes ago 77.7MB

Create container


$ docker run -itd --name my_php_app my_php_app:latest sh
4ec090d70437cf3312d735f754bcb5abcd96759fd8f76f24fbeb43e42c04f140

$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
4ec090d70437 my_php_app:latest "docker-php-entrypoi…" 4 seconds ago Up 3 seconds 9000/tcp my_php_app

Verify container content


$ docker exec -it my_php_app sh
/app # ls -l
-rw-r--r-- 1 root root 133 Jun 3 20:23 composer.json
drwxr-xr-x 4 root root 4096 Jun 3 20:33 docker
drwxr-xr-x 2 root root 4096 Jun 3 20:39 src
drwxr-xr-x 4 root root 4096 Jun 3 20:56 vendor

If you run php src/index.php command inside container, it should work.