Back to Posts

Using Docker for Laravel Development

Muhammad Fauzun Naja

One of the biggest pain points in web development is environment inconsistency. What works on your machine breaks on your teammate's, and somehow everything falls apart in production. Docker solves this.

This guide walks through setting up a Laravel development environment with Docker, covering the essential services you'll need day-to-day.

Why Docker for Laravel?

Traditional Laravel development requires PHP, Composer, MySQL, Redis, and often more services installed directly on your machine. Each project might need different PHP versions or extensions, leading to conflicts.

With Docker, you get:

  • Isolated environments per project
  • Identical stacks across your team
  • Quick onboarding — new devs just run docker compose up
  • Production parity — your dev stack matches staging and production

Project Structure

laravel-app/
├── docker/
│   ├── php/
│   │   └── Dockerfile
│   └── nginx/
│       └── default.conf
├── docker-compose.yml
├── src/               # Laravel source
└── .env

Docker Compose Setup

Here's a solid docker-compose.yml for Laravel:

services:
  app:
    build:
      context: .
      dockerfile: docker/php/Dockerfile
    container_name: laravel-app
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - ./src:/var/www
      - ./docker/php/php.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - laravel

  webserver:
    image: nginx:alpine
    container_name: laravel-webserver
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./src:/var/www
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - laravel

  mysql:
    image: mysql:8.0
    container_name: laravel-mysql
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
    ports:
      - "3307:3306"
    volumes:
      - dbdata:/var/lib/mysql
    networks:
      - laravel

  redis:
    image: redis:alpine
    container_name: laravel-redis
    restart: unless-stopped
    ports:
      - "6380:6379"
    networks:
      - laravel

volumes:
  dbdata:

networks:
  laravel:
    driver: bridge

PHP Dockerfile

Multi-stage builds keep the image lean:

FROM php:8.3-fpm as base

RUN apt-get update && apt-get install -y \
    git curl libpng-dev libonig-dev libxml2-dev zip unzip \
    && docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd

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

WORKDIR /var/www

Nginx Configuration

server {
    listen 80;
    server_name localhost;
    root /var/www/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    index index.php;

    charset utf-8;

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

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

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

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

Environment Configuration

Your .env file for the Docker setup:

DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

REDIS_HOST=redis
REDIS_PORT=6379

Running the Stack

# Start containers
docker compose up -d

# Run Artisan commands inside the container
docker compose exec app php artisan migrate
docker compose exec app php artisan queue:work

# View logs
docker compose logs -f app

# Stop everything
docker compose down

Common Commands Cheatsheet

Task Command
Install Composer deps docker compose exec app composer install
Run migrations docker compose exec app php artisan migrate
Tinker docker compose exec app php artisan tinker
Clear cache docker compose exec app php artisan optimize:clear
NPM build docker compose exec app npm run build
MySQL shell docker compose exec mysql mysql -u laravel -p laravel

Tips for Production

When moving to production, consider:

  1. Use a non-root user in your Dockerfile for security
  2. Implement health checks for each service
  3. Use environment-specific compose filesdocker-compose.yml for dev, docker-compose.prod.yml for prod
  4. Set up a reverse proxy (Traefik, Caddy, or Nginx Proxy Manager)
  5. Use Docker secrets instead of plain .env for sensitive data

Conclusion

Docker eliminates the "it works on my machine" problem. For Laravel developers, containerization means you spend less time debugging environment issues and more time building features. Start with the setup above, customize it for your stack, and enjoy consistent development across your team.

DockerLaravelDevOpsPHP

Muhammad Fauzun Naja

Muhammad Fauzun Naja

Backend Developer sharing insights about Laravel, PHP, and DevOps.