Development
Jun 8, 2025
10 min read
9 views

Laravel Sail & Docker: Production-Ready Local Development Setup

Master Laravel Sail for consistent development environments that mirror production. Learn advanced Docker configurations, performance optimization, and team collaboration best practices.

Laravel Sail & Docker: Production-Ready Local Development Setup

Share this post

Laravel Sail has made local development much more straightforward, but there's more to explore beyond the basics. Here's how to set up a more robust local environment that can grow with your project and work well for teams.

This guide assumes basic Docker knowledge. If you're new to containerization, start with Docker's official documentation.

Why Sail Over Native Development?

Consistency

Every team member runs identical environments. No more "works on my machine" issues.

Isolation

Multiple projects with different PHP/Node versions run without conflicts.

Production Parity

Development closely mirrors production infrastructure, reducing deployment surprises.

Advanced Sail Configuration

Custom Docker Compose Override

Create a docker-compose.override.yml for team-specific configurations:

# docker-compose.override.yml
version: '3'
services:
  laravel.test:
    volumes:
      # Performance: Use delegated volumes on macOS
      - './:/var/www/html:delegated'
      # SSH agent forwarding for Git operations
      - '${SSH_AUTH_SOCK}:/ssh-agent'
    environment:
      - SSH_AUTH_SOCK=/ssh-agent
      # Custom PHP configuration
      - PHP_POST_MAX_SIZE=100M
      - PHP_UPLOAD_MAX_FILESIZE=100M
      - PHP_MEMORY_LIMIT=512M
    # Additional ports for debugging
    ports:
      - '${APP_PORT:-80}:80'
      - '9003:9003'  # Xdebug

  # Redis for caching and sessions
  redis:
    image: 'redis:alpine'
    ports:
      - '${REDIS_PORT:-6379}:6379'
    volumes:
      - 'sail-redis:/data'
    networks:
      - sail
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      retries: 3
      timeout: 5s

  # Elasticsearch for search functionality
  elasticsearch:
    image: 'elasticsearch:8.8.0'
    environment:
      - discovery.type=single-node
      - xpack.security.enabled=false
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    ports:
      - '${ELASTICSEARCH_PORT:-9200}:9200'
    volumes:
      - 'sail-elasticsearch:/usr/share/elasticsearch/data'
    networks:
      - sail

  # MinIO for S3-compatible object storage
  minio:
    image: 'minio/minio:latest'
    ports:
      - '${MINIO_PORT:-9000}:9000'
      - '${MINIO_CONSOLE_PORT:-8900}:8900'
    environment:
      MINIO_ROOT_USER: sail
      MINIO_ROOT_PASSWORD: password
    volumes:
      - 'sail-minio:/data/minio'
    networks:
      - sail
    command: minio server /data/minio --console-address ":8900"

volumes:
  sail-redis:
    driver: local
  sail-elasticsearch:
    driver: local
  sail-minio:
    driver: local

Custom PHP Configuration

Create docker/php/php.ini for production-like PHP settings:

[PHP]
; Production-like settings
expose_php = Off
memory_limit = 512M
post_max_size = 100M
upload_max_filesize = 100M
max_execution_time = 300
max_input_vars = 3000

; Error handling (adjust for development)
display_errors = On
display_startup_errors = On
log_errors = On
error_log = /var/log/php_errors.log

; OPcache settings
opcache.enable = 1
opcache.memory_consumption = 128
opcache.max_accelerated_files = 10000
opcache.revalidate_freq = 2
opcache.save_comments = 1

; Session configuration
session.gc_maxlifetime = 1440
session.cookie_httponly = 1
session.cookie_secure = 0
session.use_strict_mode = 1

; File uploads
file_uploads = On
upload_tmp_dir = /tmp

; Date settings
date.timezone = UTC

Xdebug Configuration for Development

Add Xdebug configuration to docker/php/xdebug.ini:

[xdebug]
zend_extension=xdebug.so
xdebug.mode=debug
xdebug.client_host=host.docker.internal
xdebug.client_port=9003
xdebug.start_with_request=yes
xdebug.discover_client_host=1
xdebug.idekey=VSCODE

Performance Optimization

macOS Volume Performance

macOS Docker performance can be slow. Here are several optimization strategies:

1. Use :delegated Volume Mounts

volumes:
  - './:/var/www/html:delegated'

2. Exclude Heavy Directories

# .dockerignore
node_modules
.git
vendor
storage/logs/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*

3. Use Named Volumes for Dependencies

volumes:
  - './:/var/www/html:delegated'
  - 'sail-node_modules:/var/www/html/node_modules'
  - 'sail-vendor:/var/www/html/vendor'

Database Performance Tuning

Optimize MySQL for development with custom configuration:

# docker/mysql/my.cnf
[mysql]
default-character-set = utf8mb4

[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci

# Performance settings
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT

# Query cache
query_cache_type = 1
query_cache_size = 32M
query_cache_limit = 2M

# Connection settings
max_connections = 100
wait_timeout = 600
interactive_timeout = 600

# Logging
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

# Disable DNS lookups
skip-name-resolve

Production-Ready Environment Variables

Structure your .env file for consistency:

# Application
APP_NAME="My Laravel App"
APP_ENV=local
APP_KEY=base64:YOUR_APP_KEY
APP_DEBUG=true
APP_URL=http://localhost

# Sail Configuration
APP_PORT=80
VITE_PORT=5173
WWWGROUP=1000
WWWUSER=1000

# Database
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=sail
DB_PASSWORD=password

# Redis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379

# Mail (using Mailpit for local development)
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"

# S3 Compatible Storage (MinIO)
AWS_ACCESS_KEY_ID=sail
AWS_SECRET_ACCESS_KEY=password
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=local
AWS_USE_PATH_STYLE_ENDPOINT=true
AWS_ENDPOINT=http://minio:9000
AWS_URL=http://localhost:9000/local

# Search (Elasticsearch)
SCOUT_DRIVER=elasticsearch
ELASTICSEARCH_HOST=elasticsearch:9200

# Logging
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

# Broadcasting
BROADCAST_DRIVER=log
CACHE_DRIVER=redis
FILESYSTEM_DISK=local
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=120

Team Collaboration Best Practices

Git Hooks for Consistency

Create .githooks/pre-commit to ensure code quality:

#!/bin/bash

echo "Running pre-commit checks..."

# Check if Sail is running
if ! ./vendor/bin/sail ps | grep -q "Up"; then
    echo "โŒ Sail containers are not running. Please start with: ./vendor/bin/sail up -d"
    exit 1
fi

# Run PHP CS Fixer
echo "๐Ÿ”ง Running PHP CS Fixer..."
./vendor/bin/sail exec laravel.test ./vendor/bin/php-cs-fixer fix --dry-run --diff
if [ $? -ne 0 ]; then
    echo "โŒ PHP CS Fixer found issues. Run: ./vendor/bin/sail exec laravel.test ./vendor/bin/php-cs-fixer fix"
    exit 1
fi

# Run PHPStan
echo "๐Ÿ” Running PHPStan..."
./vendor/bin/sail exec laravel.test ./vendor/bin/phpstan analyse
if [ $? -ne 0 ]; then
    echo "โŒ PHPStan found issues."
    exit 1
fi

# Run tests
echo "๐Ÿงช Running tests..."
./vendor/bin/sail test
if [ $? -ne 0 ]; then
    echo "โŒ Tests failed."
    exit 1
fi

echo "โœ… All checks passed!"

Makefile for Common Tasks

Create a Makefile for consistent commands:

.PHONY: install start stop restart test migrate fresh seed backup

# Installation and setup
install:
	@echo "๐Ÿš€ Setting up Laravel project..."
	cp .env.example .env
	./vendor/bin/sail up -d
	./vendor/bin/sail composer install
	./vendor/bin/sail artisan key:generate
	./vendor/bin/sail npm install
	./vendor/bin/sail npm run build
	make migrate
	@echo "โœ… Project setup complete!"

# Container management
start:
	./vendor/bin/sail up -d

stop:
	./vendor/bin/sail down

restart:
	./vendor/bin/sail down
	./vendor/bin/sail up -d

# Database operations
migrate:
	./vendor/bin/sail artisan migrate

fresh:
	./vendor/bin/sail artisan migrate:fresh --seed

seed:
	./vendor/bin/sail artisan db:seed

backup:
	./vendor/bin/sail exec mysql mysqldump -u sail -ppassword laravel > backup_$(shell date +%Y%m%d_%H%M%S).sql

# Development tools
test:
	./vendor/bin/sail test

test-coverage:
	./vendor/bin/sail test --coverage

analyze:
	./vendor/bin/sail exec laravel.test ./vendor/bin/phpstan analyse

fix:
	./vendor/bin/sail exec laravel.test ./vendor/bin/php-cs-fixer fix

# Asset compilation
dev:
	./vendor/bin/sail npm run dev

build:
	./vendor/bin/sail npm run build

watch:
	./vendor/bin/sail npm run dev -- --watch

# Utility commands
logs:
	./vendor/bin/sail logs -f

shell:
	./vendor/bin/sail shell

tinker:
	./vendor/bin/sail artisan tinker

queue:
	./vendor/bin/sail artisan queue:work

clear:
	./vendor/bin/sail artisan optimize:clear
	./vendor/bin/sail artisan view:clear
	./vendor/bin/sail artisan route:clear
	./vendor/bin/sail artisan config:clear

Debugging and Monitoring

Container Health Monitoring

Add health checks to your services:

services:
  laravel.test:
    # ... other configuration
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:80/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  mysql:
    # ... other configuration
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "sail", "-ppassword"]
      interval: 30s
      timeout: 10s
      retries: 3

Log Aggregation

Configure centralized logging with a custom log driver:

<?php
// config/logging.php
'channels' => [
    'stack' => [
        'driver' => 'stack',
        'channels' => ['single', 'docker'],
        'ignore_exceptions' => false,
    ],

    'docker' => [
        'driver' => 'single',
        'path' => '/dev/stdout',
        'level' => env('LOG_LEVEL', 'debug'),
        'formatter' => \Monolog\Formatter\JsonFormatter::class,
    ],
],

Security Considerations

๐Ÿ”’ Development Security Checklist

  • โ€ข Never commit .env files with real credentials
  • โ€ข Use strong passwords even for local databases
  • โ€ข Regularly update container images
  • โ€ข Limit exposed ports to necessary services only
  • โ€ข Use secrets management for sensitive configuration

Environment Isolation

Create environment-specific configurations:

# .env.local (for local development)
APP_ENV=local
APP_DEBUG=true
DB_HOST=mysql

# .env.staging (for staging environment)
APP_ENV=staging
APP_DEBUG=false
DB_HOST=staging-mysql.internal

# .env.production (for production - never commit!)
APP_ENV=production
APP_DEBUG=false
DB_HOST=prod-mysql.internal

Troubleshooting Common Issues

Problem: Slow file operations on macOS

Solution: Use :delegated volume mounts and exclude heavy directories

- './:/var/www/html:delegated'

Problem: Port conflicts

Solution: Use different ports in .env

APP_PORT=8080
MYSQL_PORT=3307

Problem: Permission issues

Solution: Match user IDs in .env

WWWUSER=$(id -u)
WWWGROUP=$(id -g)

Conclusion

A well-configured Laravel Sail environment becomes the foundation for successful project delivery. By investing time in proper setup, you eliminate countless hours of debugging environment-specific issues and create a development experience that scales with your team.

The key is treating your development environment as seriously as production. With these configurations, you'll have a robust, performant, and maintainable development setup that mirrors production and supports your entire team's workflow.

Ready to Optimize Your Development Workflow?

These configurations have helped teams reduce setup time from days to minutes and eliminate environment-related bugs entirely. Start with the basics and gradually add the advanced features as your team grows.

Remember: A great development environment is an investment that pays dividends throughout the project lifecycle.

Need help setting up a robust development infrastructure for your team? Let's chat about creating development workflows that scale with your startup's growth.

Chris Page

Chris Page

Fractional CTO and Software Engineer with 25+ years of experience. I help startups scale from 0 to 7 figures using AI-assisted development and proven frameworks.

Related Posts

Continue reading

Ready to Scale Your Startup?

Let's discuss how I can help you build your MVP or optimize your existing technology stack.