How i ship my php apps to production using docker php-fpm and nginx
By Jim van Duijsen
July 20, 2025
My PHP Setup for Production
Here's how I run a PHP application in production using Docker Compose to build the images, NGINX and PHP-FPM. This works perfectly for platforms that allow you to deploy apps using Docker Compose like Coolify, and you can also easily roll back because the code is baked into the image.
You can find the config on my github or copy below
Overview
This configuration sets up a classic PHP web application environment using two services:
- nginx: A web server that handles incoming HTTP requests
- app: A PHP-FPM service that executes the PHP code
The NGINX service is responsible for serving static files (like images, CSS) directly and forwarding requests for PHP files to the app service for processing.
Service-Specific Details
App Service (PHP)
Base Image: It starts with the official php:8.2.0-fpm
image, which provides a foundation with PHP and the FastCGI Process Manager (FPM).
System Dependencies: It installs necessary libraries (zlib1g-dev
, libzip-dev
, unzip
) for handling zip files.
PHP Extensions: It installs the zip PHP extension, which is needed by the project's dependencies.
Composer: It copies the Composer executable from the official Composer Docker image and then runs composer self-update
to ensure it's the latest version.
Application Code:
- It sets the working directory to
/var/www
- It copies the
composer.json
andcomposer.lock
files and runscomposer install
to install the PHP dependencies defined incomposer.json
- Finally, it copies the rest of the application code (your PHP files, etc.) into the container
NGINX Service
Base Image: It uses the lightweight nginx:1.24-alpine
image.
Configuration: It copies your custom nginx.conf
file into the container, overwriting the default NGINX configuration. This file defines how NGINX handles requests, including how it forwards PHP requests to the app service.
Static Files: It copies the public
directory from your project into the container at /var/www/public
. This allows NGINX to directly serve any static assets (images, CSS, etc.) found in that directory.
How They Work Together
Even though the ports section in docker-compose.yml
is commented out, the NGINX service is designed to listen for incoming HTTP requests on port 80 (the default). When a request comes in, NGINX will:
- Static files: If the request is for a static file (e.g.,
/css/index.css
), it will serve it directly from the/var/www/public
directory inside the NGINX container - PHP files: If the request is for a PHP file (e.g.,
/index.php
), it will forward the request to the app service using the FastCGI protocol. The app service will execute the PHP script and send the response back to NGINX, which then sends it to the user
You can find the config on my github or copy below
Docker compose
services:
app:
build:
context: ./
dockerfile: Dockerfile-php
container\_name: php-fpm
restart: always
working\_dir: /var/www/
nginx:
build:
context: ./
dockerfile: Dockerfile-nginx
container\_name: nginx
restart: always
\# ports: \#only for development
\# \- 8000:80
Dockerfile-php
FROM php:8.2.0-fpm
RUN apt-get update && apt-get install \-y \\
zlib1g-dev \\
libzip-dev \\
unzip
RUN rm \-rf /var/lib/apt/lists/\*
RUN docker-php-ext-install zip
COPY \--from=composer /usr/bin/composer /usr/bin/composer
RUN composer self-update
WORKDIR /var/www
COPY composer.json composer.lock ./
RUN composer install
COPY ./ ./
Dockerfile-nginx
FROM nginx:1.24-alpine
COPY ./nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY ./public /var/www/public
Ngnix.conf
Needs to go in the nginx folder
server {
listen 80;
index index.php;
error\_log /var/log/nginx/error.log;
access\_log /var/log/nginx/access.log;
error\_page 404 /index.php;
root /var/www/public;
location \~ \\.php$ {
try\_files $uri \=404;
fastcgi\_pass app:9000;
fastcgi\_index index.php;
include fastcgi\_params;
fastcgi\_param SCRIPT\_FILENAME $document\_root$fastcgi\_script\_name;
}
location / {
try\_files $uri $uri/ /index.php?$query\_string;
gzip\_static on;
}
}