Home About Skills Products Work
Projects Services Experience
Learn
Tutorials Courses Blogs Resources
Contact
Blog · Docker & DevOps

Dockerizing a Laravel Application: A Complete Guide

Dockerizing a Laravel Application: A Complete Guide

Dockerizing a Laravel app means your entire team (and your production server) runs the exact same PHP version, extensions, and services — no more "works on my machine." Here's a complete, working setup.

The Dockerfile

FROM php:8.3-fpm

RUN apt-get update && apt-get install -y \
    libpq-dev libzip-dev unzip git \
    && docker-php-ext-install pdo pdo_mysql zip

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

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

CMD ["php-fpm"]

Multi-stage-copying Composer directly from its own official image (rather than installing it via curl) keeps the Dockerfile shorter and always pulls a known-good build.

docker-compose.yml: The Full Stack

services:
  app:
    build: .
    volumes:
      - .:/var/www
    depends_on:
      - db
      - redis

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

  db:
    image: mysql:8
    environment:
      MYSQL_DATABASE: laravel
      MYSQL_ROOT_PASSWORD: secret
    volumes:
      - dbdata:/var/lib/mysql

  redis:
    image: redis:alpine

volumes:
  dbdata:

Four services, one command to bring the whole stack up: docker compose up -d.

Nginx Configuration

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

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

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

fastcgi_pass app:9000 uses the service name app as the hostname — Docker Compose's internal DNS resolves it automatically, no hardcoded IPs needed.

Environment Variables Point at Service Names

# .env
DB_HOST=db
REDIS_HOST=redis

Inside the Docker network, containers reach each other by service name, not 127.0.0.1 — a very common first-time mistake is leaving DB_HOST=127.0.0.1 from local development and wondering why the app can't connect to the database.

Running Artisan Commands

docker compose exec app php artisan migrate
docker compose exec app php artisan queue:work

Once this is running, the exact same docker-compose.yml (with production-appropriate environment values swapped in) is what deploys to a real server — the dev and production environments are now provably identical.

CI/CD for Web Apps with GitHub Actions

CI/CD for Web Apps with GitHub Actions

Automated testing and deployment with GitHub Actions — running a real database in CI, and deploying automatically on merge to main.

Docker Compose for Multi-Service Development Environments

Docker Compose for Multi-Service Development Environments

Structuring a real multi-service Docker Compose stack — app, worker, database with health checks, and environment-specific overrides.

Esc