← Back to Blogs

Docker for PHP Developers — Containerizing Your Laravel Application

January 2, 2026 · 10 min read

Docker PHP Laravel Nginx MySQL Redis

Introduction

Docker has transformed how PHP applications are developed, tested, and deployed. Instead of wrestling with platform-specific setups — Homebrew on macOS, apt on Ubuntu, XAMPP on Windows — you define your entire stack in code and run it consistently everywhere. For Laravel developers, Docker provides a reproducible environment with Nginx, PHP-FPM, MySQL, and Redis that matches production. This guide walks through a production-grade Docker setup from scratch.

Docker vs Vagrant vs Homestead

Laravel Homestead is a Vagrant box that provisions a full Ubuntu VM. It works well but is heavy — a VM reserves gigabytes of RAM and CPU upfront. Docker containers, by contrast, share the host kernel and start in seconds. Vagrant is better when you need a full OS environment with systemd services; Docker excels for lightweight, isolated service stacks. For most Laravel projects, Docker provides the best balance of performance and fidelity.

Dockerfile for PHP 8.x

The Dockerfile is the blueprint for your PHP-FPM image. It installs PHP with the extensions Laravel needs — PDO, Mbstring, BCMath, GD, Redis, and others.

FROM php:8.3-fpm

RUN apt-get update && apt-get install -y \
    libpng-dev libjpeg-dev libfreetype6-dev zip unzip git curl \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install pdo_mysql bcmath gd

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

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

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

EXPOSE 9000
CMD ["php-fpm"]

docker-compose.yml with Multi-Service Stack

Docker Compose orchestrates multiple containers. Below is a four-service stack: Nginx as the web server, PHP-FPM to process PHP, MySQL for the database, and Redis for caching.

version: '3.9'
services:
  app:
    build: .
    volumes:
      - .:/var/www
    networks:
      - laravel

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

  mysql:
    image: mysql:8.0
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_USER: laravel
      MYSQL_PASSWORD: secret
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - laravel

  redis:
    image: redis:alpine
    networks:
      - laravel

volumes:
  db_data:

networks:
  laravel:

Running Artisan Commands in Containers

Once the stack is running (docker compose up -d), you interact with the application container to run Artisan commands. Use docker compose exec to run commands inside the running service:

docker compose exec app php artisan migrate
docker compose exec app php artisan cache:clear
docker compose exec app php artisan tinker

Laravel Sail as an Alternative

Laravel Sail is a lightweight command-line tool for interacting with a default Docker development environment. It ships with Laravel 11 and provides convenient commands like ./vendor/bin/sail up and sail artisan migrate. Sail wraps docker-compose and is perfect for individual developers. However, for custom stacks — multiple PHP versions, custom Nginx configs, or complex service dependencies — writing your own Docker setup gives you full control.

Multi-Stage Builds for Production

In production, you want a lean image without Composer, dev dependencies, or build tools. Multi-stage builds let you compile assets and install dependencies in a build stage, then copy only the runtime artifacts to the final image. The result is an image that is often 80% smaller — critical for faster deployments and lower bandwidth costs.

Common Pitfalls

Permission issues top the list — the www-data user inside the container must own storage/ and bootstrap/cache/ for Laravel to write logs and cached views. Cache invalidation is another: OPcache and route caches live inside the container and reset on rebuild. Use Redis for shared cache across container restarts. Performance can suffer if you mount the entire project as a bind mount on macOS (filesystem overhead); use Docker's :cached or :delegated volume flags to mitigate this.

Conclusion

Docker brings consistency and portability to PHP development. By defining your infrastructure as code, you eliminate "it works on my machine" problems and streamline onboarding for new team members. Start with the Dockerfile and compose file above, then customize for your specific stack. Laravel Sail is a great starting point, but understanding the underlying Docker setup empowers you to build production-ready, scalable environments.

Need help containerizing your Laravel application? Our team designs Docker workflows for development and production that scale from solo developers to enterprise teams.

Get in Touch