Docker for PHP Developers: From Zero to Production

Docker for PHP Developers: From Zero to Production

A hands-on guide to containerizing PHP applications with Docker — covering Dockerfiles, multi-stage builds, Docker Compose, Nginx configuration, debugging, CI/CD integration, and production deployment strategies.

Docker has become an essential tool for modern PHP developers. It eliminates the classic "works on my machine" problem and creates consistent environments from development to production.

In this guide, I'll take you from zero Docker knowledge to running a full PHP application in production containers.


Why Docker for PHP?

Without Docker, setting up a PHP environment means installing PHP, a web server, database server, extensions, and tools individually. Docker solves this:

  • Consistent environments — Everyone runs the exact same stack
  • Easy onboarding — New developers run docker compose up and they're ready
  • Isolation — Projects don't conflict with each other
  • Production parity — What runs locally runs the same in production

Your First PHP Dockerfile

FROM php:8.3-fpm-alpine

RUN apk add --no-cache \
    git curl zip unzip libpng-dev libjpeg-turbo-dev \
    freetype-dev icu-dev oniguruma-dev libxml2-dev

RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo pdo_mysql mbstring exif pcntl bcmath gd intl opcache

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

WORKDIR /var/www/html
COPY . .
RUN composer install --no-dev --optimize-autoloader --no-interaction

RUN chown -R www-data:www-data storage bootstrap/cache

EXPOSE 9000
CMD ["php-fpm"]

Use Alpine-based images when possible. They are significantly smaller (50MB vs 400MB+) and have a smaller attack surface.


Docker Compose for Local Development

version: '3.8'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - .:/var/www/html
    depends_on:
      - mysql
      - redis

  nginx:
    image: nginx:alpine
    ports:
      - '8080:80'
    volumes:
      - .:/var/www/html
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - mysql-data:/var/lib/mysql
    ports:
      - '3306:3306'

  redis:
    image: redis:alpine
    ports:
      - '6379:6379'

volumes:
  mysql-data:

Nginx Configuration

server {
    listen 80;
    server_name localhost;
    root /var/www/html/public;
    index index.php;

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

    location ~ \.php$ {
        fastcgi_pass app:9000;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

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

Multi-Stage Builds for Production

Multi-stage builds let you use separate stages for building and running your app. The final image only contains what is needed:

# Stage 1: Install PHP dependencies
FROM composer:latest AS vendor
WORKDIR /app
COPY composer.json composer.lock ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

# Stage 2: Build frontend assets
FROM node:18-alpine AS frontend
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY resources/ ./resources/
COPY vite.config.js ./
RUN npm run build

# Stage 3: Final production image
FROM php:8.3-fpm-alpine
RUN docker-php-ext-install pdo pdo_mysql mbstring gd intl opcache
WORKDIR /var/www/html
COPY . .
COPY --from=vendor /app/vendor ./vendor
COPY --from=frontend /app/public/build ./public/build
RUN chown -R www-data:www-data storage bootstrap/cache
EXPOSE 9000
CMD ["php-fpm"]

Multi-stage builds can reduce your final image size by 60-80%. Build dependencies like Composer and Node.js are not included in the production image.


CI/CD with Docker

name: CI/CD Pipeline
on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
      - run: composer install
      - run: php artisan test

  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: docker build -f Dockerfile.production -t my-app:latest .
      - run: docker push my-app:latest

Conclusion

Docker is an essential skill for modern PHP development. It eliminates environment inconsistencies, simplifies onboarding, and creates a clear path from development to production.

Start with the basics — a simple Dockerfile and docker-compose.yml. Get comfortable running your daily workflow inside containers. Then gradually adopt multi-stage builds, health checks, and CI/CD integration.

The investment pays off quickly: fewer "works on my machine" issues, faster onboarding, and confidence that what you test locally is what runs in production.

Share This Article

Comments

Get In Touch

I'm always open to discussing new projects and opportunities.

Location Yassa/Douala, Cameroon
Availability Open for opportunities

Connect With Me

Send a Message

Have a project in mind? Let's talk about it.