When setting up a Laravel project, one of the biggest challenges is keeping your local environment consistent with production. PHP versions, Composer, database configs β€” they can vary from machine to machine and cause frustrating bugs.

Docker solves this problem by allowing you to package your environment in containers. In this post, I’ll show you how to set up a local Laravel development environment using Docker and Docker Compose.

We’ll use a custom Laravel-ready PHP image that I maintain on Docker Hub under jcadima/laravel-php, along with MySQL and Nginx. Once you follow these steps, you’ll have a reproducible environment you can spin up anywhere.

Why Choose Docker for Laravel Development?

Before diving into the setup, let's understand why Docker is an excellent choice for Laravel development:

Docker ensures your development environment matches production exactly, eliminating environment-related bugs and deployment surprises.

New team members can get up and running with a single command instead of spending hours installing and configuring dependencies.

Each project runs in its own containers, preventing conflicts between different PHP versions or extensions across projects.

Your entire environment configuration is stored in version-controlled files, making it easy to track changes and roll back if needed.

Prerequisites

Before we begin, make sure you have the following installed on your system:

  • Docker Desktop (Windows/macOS) or Docker Engine (Linux)
  • Docker Compose (usually included with Docker Desktop)
  • Git (for version control)
  • A code editor (Vim, VS Code, PhpStorm, etc.)
To verify your installation, run:

docker --version
docker-compose --version

Use the Laravel Docker Image

For this setup, we'll use a specially crafted Docker image that includes everything Laravel needs out of the box.

The image comes pre-configured with:

PHP 8.2 with FPM (FastCGI Process Manager)

  • Optimized for production-like performance
  • Better resource management than Apache mod_php
  • Seamless integration with Nginx

Composer Pre-installed

  • Latest stable version
  • Optimized for faster dependency resolution
  • Includes all necessary certificates

Essential PHP Extensions

  • pdo_mysql - Database connectivity
  • mbstring - Multibyte string handling
  • bcmath - Arbitrary precision mathematics
  • gd - Image processing capabilities
  • zip - Archive handling
  • intl - Internationalization support
  • opcache - Performance optimization

Security Features

  • Non-root user configuration
  • Proper file permissions
  • Minimal attack surface

You can find it here: πŸ‘‰ jcadima/laravel-php on Docker Hub

Setting Up Your Project Structure

Let's start by creating a well-organized project structure that separates your Laravel application from Docker configuration files:


mkdir my-laravel-project
cd my-laravel-project

Your project directory should follow this structure:


my-laravel-project/
β”œβ”€β”€ src/                           # Your Laravel application
β”‚   β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ .env
β”‚   └── ... (other Laravel files)
β”œβ”€β”€ docker-compose.yml             # Main Docker Compose configuration
β”œβ”€β”€ docker-compose/
β”‚   └── nginx/
β”‚       └── http.conf              # Custom Nginx configuration
β”œβ”€β”€ .gitignore                     # Git ignore rules
└── README.md                      # Project documentation

This structure keeps your Laravel code separate from Docker configuration, making it easier to manage and deploy.

Creating the Docker Compose Configuration

Here’s an example docker-compose.yml to get started:


version: "3.9"

services:
  app:
    image: jcadima/laravel-php:8.2
    container_name: laravel-app
    restart: unless-stopped
    working_dir: /var/www/laravel
    volumes:
      - ./src:/var/www/laravel
    networks:
      - laravel-net

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

  db:
    image: mysql:8
    container_name: laravel-db
    restart: unless-stopped
    env_file: ./src/.env
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: "${DB_DATABASE}"
      MYSQL_USER: "${DB_USERNAME}"
      MYSQL_PASSWORD: "${DB_PASSWORD}"
      MYSQL_ROOT_PASSWORD: "${DB_PASSWORD}"
    volumes:
      - db-data:/var/lib/mysql
    networks:
      - laravel-net

volumes:
  db-data:

networks:
  laravel-net:
    driver: bridge

Nginx Configuration for Laravel

The example above references a file docker-compose/nginx/http.conf.

Create the directory structure for Nginx configuration:


mkdir -p docker-compose/nginx

Create docker-compose/nginx/http.conf with this Laravel-optimized configuration:


server {
    listen 80;
    index index.php index.html;
    server_name localhost;

    root /var/www/laravel/public;

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

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

    location ~ /\.ht {
        deny all;
    }
}

Setting Up Your Laravel Environment

If you don't have a Laravel project yet, you can create one using the containerized environment:

Option 1: Create a New Laravel Project


# Create the src directory
mkdir src

# Create a new Laravel project using Docker
docker run --rm \
  -v $(pwd)/src:/app \
  composer \
  create-project --prefer-dist laravel/laravel .

Option 2: Use an Existing Project

If you have an existing Laravel project, simply move or copy it into the src directory.

Configure Your Laravel Environment

Update your src/.env file with the following database configuration:


APP_NAME="Laravel Docker App"
APP_ENV=local
APP_KEY=base64:your-app-key-here
APP_DEBUG=true
APP_URL=http://localhost:8080

LOG_CHANNEL=stack

DB_CONNECTION=mysql
DB_HOST=db  # <- important, this is the service name of your database container (not localhost)
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=laravel
DB_PASSWORD=secret

BROADCAST_DRIVER=log
CACHE_DRIVER=redis
FILESYSTEM_DRIVER=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=redis
SESSION_LIFETIME=120


MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=hello@example.com
MAIL_FROM_NAME="${APP_NAME}"

Starting Your Development Environment

Now that everything is configured, let's start the development environment:


# Start all containers in detached mode
docker compose up -d

# View the status of all containers
docker compose ps

# Watch the logs (optional)
docker compose logs -f

Accessing Your Application

Once all containers are running, you can access:

  • Laravel Application: http://localhost:8080
  • MySQL Database: 127.0.0.1:3306 (use your preferred MySQL client)

Essential Docker Commands for Daily Development

Here are the most useful commands you'll need for day-to-day development:


# Start the environment
docker compose up -d

# Stop the environment
docker compose down

# Restart a specific service
docker compose restart app

# View container status
docker compose ps

Additional Docker Container functions (Add it to your .bashrc or .zshrc)

dinspect – Quickly check container status

function dinspect() {
  docker inspect $1 | grep Status
}

Purpose: Shows the current status (running, exited, etc.) of a Docker container.

Usage:


dinspect laravel-app

Output might be something like:


"Status": "running",

dlogs – Tail container logs

function dlogs() {
  docker logs -f $1
}

Purpose: Stream logs from a container in real-time.

Usage:


dlogs laravel-app

Shows logs like Artisan output, errors, or any console messages.

-f means β€œfollow”, so new logs appear automatically.


dnetwork – Get container IP

function dnetwork() {
  docker inspect $1 | grep IP
}

Purpose: Quickly find the container’s IP address inside Docker.

Usage:


dnetwork mysql-db

Useful if you need to connect another container to this one or debug networking issues.


dmysql – Connect to MySQL inside a container

# $1: container name
# $2: database user
function dmysql() {
  docker exec -it $1 mysql -u $2 -p
}

Purpose: Open an interactive MySQL session inside a container.

Usage:


dmysql mysql-db root

Prompts for the password, then lets you run SQL queries inside the container.

Saves you from typing the long docker exec -it mysql-db mysql -u root -p every time.


dbash – Get a shell inside a container

function dbash() {
  docker exec -it $1 bash
}
Purpose: Opens a bash shell inside a running container for direct interaction. Usage:

dbash laravel-app

You can now run commands as if you were inside the container, e.g., ls, php artisan, or composer install.

Troubleshooting Common Issues

Port Conflicts

If ports 8080, 3306, or 6379 are already in use on your system, modify the port mappings in docker-compose.yml

Database Persistence

The database data is stored in a named volume (db-data), so your data persists even when containers are recreated.

You now have a fully functional, professional Laravel development environment running on Docker. This setup provides consistency across different machines, easy onboarding for team members, and a solid foundation for building Laravel applications.

The containerized approach eliminates the "works on my machine" problem and gives you confidence that your development environment closely mirrors production.