Complete Docker development environment for Laravel with a powerful CLI.
Global Composer Package — Install once, use everywhere. No per-project Docker configuration needed.
For experienced developers — get up and running in 60 seconds:
# Install globally via Composer
composer global require mwguerra/docker-local
# Add to PATH (add this line to ~/.bashrc or ~/.zshrc for persistence)
export PATH="$HOME/.composer/vendor/bin:$PATH"
# Initialize the environment
docker-local init
# Create your first Laravel project
docker-local make:laravel my-app
# Open in browser (https://my-app.test)
docker-local openNeed prerequisites first? See Installation for platform-specific setup guides.
- PHP 8.4 with Xdebug 3.4, FFmpeg, and all Laravel extensions
- MySQL 9.1 and PostgreSQL 17 with pgvector (AI embeddings)
- Redis 8 for cache, sessions, and queues
- MinIO S3-compatible object storage
- Traefik 3.6 reverse proxy with automatic SSL
- Mailpit for email testing
- RTMP Server (optional) for live streaming with HLS
- Whisper AI (optional) for audio transcription
- Node.js 20 (optional) standalone container for asset builds
- 50+ CLI commands for rapid development
- Multi-project support with automatic isolation
- Cross-platform - Linux, macOS, and Windows (WSL2)
- Quick Install
- Requirements
- Installation
- Quick Start
- CLI Commands
- Configuration
- Services
- Optional Services
- Multi-Project Support
- Migrating from Project-Specific Docker
- IDE Integration
- Troubleshooting
- Contributing
- License
| Software | Minimum Version | Check Command |
|---|---|---|
| Docker | 24.0+ | docker --version |
| Docker Compose | 2.20+ | docker compose version |
| PHP | 8.2+ | php --version |
| Composer | 2.6+ | composer --version |
- RAM: 8GB minimum, 16GB recommended
- Disk: 20GB free space
- CPU: 64-bit processor with virtualization support
Tested on Ubuntu 22.04+, Debian 12+, Fedora 38+, and Arch Linux.
# 1. Install Docker (if not already installed)
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
newgrp docker
# 2. Install PHP and Composer (Ubuntu/Debian)
sudo apt update
sudo apt install php8.3 php8.3-{cli,curl,mbstring,xml,zip} unzip
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# 3. Install docker-local
composer global require mwguerra/docker-local
# 4. Add Composer bin to PATH (add to ~/.bashrc or ~/.zshrc)
export PATH="$HOME/.composer/vendor/bin:$PATH"
# 5. Reload shell and run setup
source ~/.bashrc # or source ~/.zshrc
docker-local init
# 6. (Optional) Configure DNS for *.test domains
sudo "$(which docker-local)" setup:dnsTested on macOS 12 (Monterey) and later.
# 1. Install Homebrew (if not already installed)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# 2. Install Docker Desktop
brew install --cask docker
# Launch Docker Desktop from Applications
# 3. Install PHP and Composer
brew install php composer
# 4. Install docker-local
composer global require mwguerra/docker-local
# 5. Add Composer bin to PATH (add to ~/.zshrc)
export PATH="$HOME/.composer/vendor/bin:$PATH"
# 6. Reload shell and run setup
source ~/.zshrc
docker-local init
# 7. (Optional) Configure DNS for *.test domains
sudo "$(which docker-local)" setup:dnsImportant: docker-local requires WSL2 on Windows. Native Windows is not supported.
# Run in PowerShell as Administrator
wsl --install -d UbuntuRestart your computer when prompted.
- Download Docker Desktop for Windows
- During installation, ensure "Use WSL 2 based engine" is checked
- After installation, go to Settings > Resources > WSL Integration
- Enable integration with your Ubuntu distribution
# Open Ubuntu from Start Menu, then run:
# Install PHP and Composer
sudo apt update
sudo apt install php8.3 php8.3-{cli,curl,mbstring,xml,zip} unzip
curl -sS https://getcomposer.org/installer | php
sudo mv composer.phar /usr/local/bin/composer
# Install docker-local
composer global require mwguerra/docker-local
# Add to PATH (add to ~/.bashrc)
echo 'export PATH="$HOME/.composer/vendor/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Run setup
docker-local init
# (Optional) Configure DNS
sudo "$(which docker-local)" setup:dnsYour WSL2 projects are accessible in Windows Explorer at:
\\wsl$\Ubuntu\home\<username>\projects
Or in VS Code:
# From WSL2 terminal
code ~/projects/my-project# Create a new Laravel project (everything is configured automatically)
docker-local make:laravel my-app
# With PostgreSQL instead of MySQL
docker-local make:laravel my-app --postgres
# Navigate to project
cd ~/projects/my-app
# Open in browser (https://my-app.test)
docker-local open
# Run artisan commands
docker-local tinker
docker-local new:model Post -mcr
# View logs
docker-local logs
docker-local logs:laravel
# Stop environment
docker-local downIf you have an existing Laravel project, copy it to ~/projects/ and configure it:
# 1. Copy your project to the projects directory
cp -r /path/to/existing-project ~/projects/my-existing-app
# 2. Navigate to the project
cd ~/projects/my-existing-app
# 3. Create the database
docker-local db:create my_existing_app
# 4. Update your .env file with docker-local settingsRequired .env changes for existing projects:
# Database - use Docker service names, not localhost
DB_HOST=mysql # or 'postgres' for PostgreSQL
DB_PORT=3306 # or 5432 for PostgreSQL
DB_DATABASE=my_existing_app # your project's database name
DB_USERNAME=laravel
DB_PASSWORD=secret
# Redis - use Docker service name
REDIS_HOST=redis
REDIS_PORT=6379
# IMPORTANT: Unique isolation values (prevent conflicts with other projects)
CACHE_PREFIX=my_existing_app_
REDIS_CACHE_DB=0 # Use different numbers if you have multiple projects
REDIS_SESSION_DB=1 # Project 1: 0-2, Project 2: 3-5, etc.
REDIS_QUEUE_DB=2
# Mail - use Mailpit
MAIL_HOST=mailpit
MAIL_PORT=1025
# MinIO/S3 (optional)
AWS_ENDPOINT=http://minio:9000
AWS_ACCESS_KEY_ID=minio
AWS_SECRET_ACCESS_KEY=minio123
AWS_BUCKET=my_existing_app
AWS_USE_PATH_STYLE_ENDPOINT=trueQuick setup script for existing projects:
# Create database
docker-local db:create my_existing_app
# Create MinIO bucket (optional)
docker exec minio mc mb local/my_existing_app --ignore-existing
# Install dependencies
docker exec -w /var/www/my-existing-app php composer install
# Generate key if needed
docker exec -w /var/www/my-existing-app php php artisan key:generate
# Run migrations
docker exec -w /var/www/my-existing-app php php artisan migrate
# Open in browser
docker-local open my-existing-appChecklist for existing projects:
- Project copied to
~/projects/<name>/ - Database created (
docker-local db:create <name>) -
.envupdated with Docker service names (mysql,redis,mailpit) - Unique
CACHE_PREFIXset (e.g.,myproject_) - Unique
REDIS_*_DBnumbers assigned (if running multiple projects) - Dependencies installed (
composer install) - Migrations run (
php artisan migrate) - Host added to
/etc/hostsor dnsmasq configured
docker-local init # Complete initial setup
docker-local doctor # Full system health check
docker-local fix [options] # Diagnose and auto-fix common issues
docker-local config # View current configuration
docker-local setup:hosts # Add Docker hostnames to /etc/hosts (sudo)
docker-local setup:dns # Configure dnsmasq for *.test (sudo)
docker-local update # Update Docker imagesFix command options:
docker-local fix # Run all checks, auto-fix what's possible
docker-local fix --dns # Only check/fix DNS issues
docker-local fix --docker # Only check/fix Docker daemon
docker-local fix --services # Only check/fix container services
docker-local fix --hosts # Only check/fix /etc/hosts
docker-local fix --verbose # Show detailed diagnostic info
docker-local fix --dry-run # Show what would be fixed without making changesThe fix command automatically detects and resolves issues like:
- Docker daemon not running
- Stopped containers
- Missing systemd-resolved configuration for *.test DNS
- Missing dnsmasq configuration
- /etc/hosts not configured
docker-local up # Start all containers
docker-local down # Stop all containers
docker-local restart # Restart all containers
docker-local status # Show service status
docker-local logs [service] # View logs (all or specific service)
docker-local clean # Clean caches and unused Docker resourcesdocker-local list # List all Laravel projects
docker-local make:laravel NAME # Create new Laravel project (MySQL, full isolation)
docker-local make:laravel NAME --postgres # Create with PostgreSQL + pgvector
docker-local clone REPO # Clone and setup existing project
docker-local open [name] # Open project in browser
docker-local open --mail # Open Mailpit
docker-local open --minio # Open MinIO Console
docker-local open --traefik # Open Traefik Dashboard
docker-local ide [editor] # Open in IDE (code, phpstorm)make:laravel creates everything automatically:
- Laravel project via Composer
- Database (MySQL or PostgreSQL) + testing database
- MinIO bucket for file storage
- Unique Redis DB numbers for cache/session/queue
- Unique cache prefix and Reverb credentials
- Configured
.envwith all Docker service connections
docker-local tinker # Laravel Tinker REPL
docker-local test [options] # Run tests (supports --coverage, --parallel)
docker-local require PACKAGE # Install Composer package with suggestions
docker-local logs:laravel # Tail Laravel logs
docker-local shell # Open PHP container shelldocker-local new:model NAME [-mcr] # make:model (with migration, controller, resource)
docker-local new:controller NAME [--api] # make:controller
docker-local new:migration NAME # make:migration
docker-local new:seeder NAME # make:seeder
docker-local new:factory NAME # make:factory
docker-local new:request NAME # make:request
docker-local new:resource NAME # make:resource
docker-local new:middleware NAME # make:middleware
docker-local new:event NAME # make:event
docker-local new:job NAME # make:job
docker-local new:mail NAME # make:mail
docker-local new:command NAME # make:commanddocker-local db:mysql # Open MySQL CLI
docker-local db:postgres # Open PostgreSQL CLI
docker-local db:redis # Open Redis CLI
docker-local db:create NAME # Create new database
docker-local db:dump [name] # Export database to SQL
docker-local db:restore FILE # Import SQL file
docker-local db:fresh # migrate:fresh --seeddocker-local queue:work # Start queue worker
docker-local queue:restart # Restart queue workers
docker-local queue:failed # List failed jobs
docker-local queue:retry ID # Retry failed job (or 'all')
docker-local queue:clear # Clear all queued jobsdocker-local xdebug on # Enable Xdebug
docker-local xdebug off # Disable Xdebug (better performance)
docker-local xdebug status # Show Xdebug statusConfigure docker-local to start automatically when your computer boots:
docker-local startup enable # Start on OS boot
docker-local startup disable # Disable startup on boot
docker-local startup status # Show startup statusPlatform-specific behavior:
| Platform | Method | Location |
|---|---|---|
| Linux | systemd service | ~/.config/systemd/user/docker-local.service |
| macOS | LaunchAgent | ~/Library/LaunchAgents/com.mwguerra.docker-local.plist |
| WSL2 | bashrc script | Entry in ~/.bashrc |
docker-local env:check # Verify current project .env
docker-local env:check --all # Audit ALL projects for conflicts
docker-local make:env # Generate new .env with unique IDs
docker-local update:env # Update existing .envConfiguration is stored in ~/.config/docker-local/config.json:
{
"version": "2.0.0",
"projects_path": "~/projects",
"editor": "code",
"mysql": {
"version": "9.1",
"port": 3306,
"root_password": "secret",
"database": "laravel",
"user": "laravel",
"password": "secret"
},
"postgres": {
"version": "17",
"port": 5432,
"database": "laravel",
"user": "laravel",
"password": "secret"
},
"redis": {
"version": "8",
"port": 6379
},
"minio": {
"api_port": 9000,
"console_port": 9001,
"root_user": "minio",
"root_password": "minio123"
},
"mailpit": {
"smtp_port": 1025,
"web_port": 8025
},
"xdebug": {
"enabled": true,
"mode": "develop,debug"
}
}docker-local operates across three locations:
~/.composer/vendor/mwguerra/docker-local/ # Package source (Composer managed)
~/.config/docker-local/ # User configuration (persistent)
~/projects/ # Your Laravel projects
docker-local/
├── bin/
│ └── docker-local # CLI entry point (symfony/console)
│
├── src/ # PHP application code
│ ├── Commands/ # CLI command classes
│ ├── Config/
│ │ ├── ConfigManager.php # Loads/saves config.json
│ │ ├── ConfigValidator.php # Validates configuration
│ │ └── PathResolver.php # Resolves ~ and relative paths
│ ├── DockerLocal.php # Main application class
│ └── cli-helper.php # Helper functions for CLI
│
├── lib/
│ └── config.sh # Bash helper functions
│
├── scripts/ # Shell scripts for operations
│ ├── setup.sh # Initial environment setup
│ ├── new-project.sh # Create new Laravel project
│ ├── generate-certs.sh # SSL certificate generation
│ ├── setup-dns.sh # Configure dnsmasq for *.test
│ ├── setup-hosts.sh # Add entries to /etc/hosts
│ ├── create-database.sh # Create MySQL/PostgreSQL databases
│ ├── make-env.sh # Generate .env files
│ ├── artisan.sh # Run artisan commands in container
│ ├── composer.sh # Run composer in container
│ ├── status.sh # Show service status
│ ├── test-connections.sh # Test database/redis connections
│ ├── add-host.sh # Add single host entry
│ └── install-cli.sh # Install CLI globally
│
├── stubs/ # Templates with placeholders
│ ├── .env.stub # Docker environment template
│ ├── laravel.env.stub # Laravel .env template ({{PROJECT_NAME}})
│ ├── config.json.stub # Default configuration
│ └── docker-compose.override.yml.stub # Override template
│
├── templates/
│ ├── install.sh # Installation script template
│ └── hooks/
│ ├── pre-install.sh # Runs before project install
│ └── post-install.sh # Runs after project install
│
├── tests/ # Pest PHP tests
│ ├── Pest.php # Pest configuration
│ ├── Unit/
│ │ ├── ConfigManagerTest.php
│ │ ├── ConfigValidatorTest.php
│ │ └── PathResolverTest.php
│ └── Feature/ # (Integration tests)
│
├── docs/ # Extended documentation
│ ├── README.md # Docs index
│ ├── architecture.md # System architecture
│ ├── cli-reference.md # Full CLI documentation
│ ├── getting-started.md # Quick start guide
│ ├── services.md # Service configuration
│ ├── templates.md # Template system docs
│ └── troubleshooting.md # Common issues
│
├── resources/docker/ # Alternative/reference Docker files
│ └── docker-compose.yml # Reference compose file
│
│── Docker Service Configurations ─────────────────────────────
│
├── docker-compose.yml # Main orchestration file
├── .env.example # Docker environment template
├── laravel.env.example # Laravel .env template (manual use)
│
├── php/ # PHP-FPM container
│ ├── Dockerfile # PHP 8.4 with extensions
│ ├── php.ini # PHP configuration
│ └── xdebug.ini # Xdebug settings
│
├── php-ai/ # PHP + AI tools container
│ ├── Dockerfile # PHP with Whisper/FFmpeg
│ └── php-ai.ini # AI container PHP config
│
├── nginx/
│ └── default.conf # Dynamic multi-project routing
│
├── mysql/
│ ├── my.cnf # MySQL configuration
│ └── init/
│ └── 01-create-databases.sql # Runs on first start
│
├── postgres/
│ └── init/
│ └── 01-create-databases.sql # Creates DBs + pgvector
│
├── redis/
│ └── redis.conf # Redis configuration
│
├── traefik/
│ └── dynamic/
│ └── tls.yml # TLS/SSL configuration
│
├── rtmp/
│ └── nginx-rtmp.conf # RTMP streaming config
│
│── Root Files ─────────────────────────────────────────────────
│
├── composer.json # PHP dependencies
├── composer.lock # Locked versions
├── LICENSE # MIT License
├── README.md # This file
└── PRD.md # Product requirements document
Created by docker-local init, persists across updates:
~/.config/docker-local/
├── config.json # Your custom settings (ports, paths, etc.)
├── .env # Active Docker environment variables
├── certs/ # SSL certificates (mkcert generated)
│ ├── localhost.pem
│ └── localhost-key.pem
└── docker-compose.override.yml # Optional: your custom services
Each Laravel project is automatically accessible via HTTPS:
~/projects/
├── blog/ → https://blog.test
│ ├── .env # Project-specific Laravel config
│ ├── app/
│ └── ...
├── api/ → https://api.test
└── shop/ → https://shop.test
docker-local uses two separate .env files for different purposes:
| File | Scope | Used By | Location |
|---|---|---|---|
.env.example |
Docker infrastructure | docker-compose.yml |
~/.config/docker-local/.env |
laravel.env.example |
Laravel application | Laravel framework | ~/projects/<project>/.env |
Controls how Docker containers are built and connected:
PROJECTS_PATH=~/projects # Where your projects live
MYSQL_PORT=3306 # Port exposed to your host machine
MYSQL_ROOT_PASSWORD=secret # Container MySQL password
XDEBUG_ENABLED=true # PHP container configurationThis file is copied to ~/.config/docker-local/.env and read by docker-compose.yml via ${VARIABLE} syntax.
Controls how Laravel connects to services from inside the container:
DB_HOST=mysql # Docker service name (NOT localhost!)
DB_PORT=3306 # Internal container port
REDIS_HOST=redis # Docker service name
MAIL_HOST=mailpit # Docker service nameThis file is copied to each project's .env (~/projects/my-app/.env) and read by Laravel via env() and config().
Key insight: The same service has different addresses depending on where you're accessing it from:
| Accessing From | MySQL Address | Why |
|---|---|---|
| Your host (TablePlus, DBeaver) | localhost:3306 |
Uses exposed port |
| Inside PHP container (Laravel) | mysql:3306 |
Uses Docker DNS |
The Docker .env configures what ports are exposed to your machine, while the Laravel .env configures how to reach services via Docker's internal network.
docker-local/
├── .env.example # Docker infrastructure template
├── laravel.env.example # Laravel application template (manual use)
└── stubs/
├── .env.stub # Docker template (for CLI automation)
└── laravel.env.stub # Laravel template with {{PLACEHOLDERS}}
The stubs/ versions contain placeholders like {{PROJECT_NAME}} for automated project creation via docker-local make:laravel.
| Service | URL |
|---|---|
| Your Projects | https://<project>.test |
| Traefik Dashboard | https://traefik.localhost |
| Mailpit | https://mail.localhost |
| MinIO Console | https://minio.localhost |
| Service | Port | Purpose |
|---|---|---|
| Traefik HTTP | 80 | HTTP (redirects to HTTPS) |
| Traefik HTTPS | 443 | HTTPS |
| MySQL | 3306 | Database |
| PostgreSQL | 5432 | Database |
| Redis | 6379 | Cache/Queue |
| MinIO API | 9000 | S3 API |
| MinIO Console | 9001 | Web UI |
| Mailpit SMTP | 1025 | |
| Mailpit Web | 8025 | Email UI |
| Service | Username | Password |
|---|---|---|
| MySQL (root) | root | secret |
| MySQL (user) | laravel | secret |
| PostgreSQL | laravel | secret |
| MinIO | minio | minio123 |
All services are now enabled by default. Simply run:
docker-compose up -dThe RTMP server provides live streaming with HLS delivery:
RTMP Configuration:
| Endpoint | URL |
|---|---|
| RTMP Ingest | rtmp://localhost:1935/live/<stream_key> |
| HLS Playback | http://localhost:8088/hls/<stream_key>.m3u8 |
| HLS (via Traefik) | https://stream.localhost/hls/<stream_key>.m3u8 |
| Stats | http://localhost:8088/stat |
Customizing RTMP:
To add project-specific webhooks (e.g., on_publish callbacks), create a custom config:
# docker-compose.override.yml
services:
rtmp:
volumes:
- ./docker/rtmp/nginx-rtmp.conf:/etc/nginx/nginx.conf:ro
- ./storage/app/hls:/var/www/hls
- ./storage/app/recordings:/var/www/recordingsA dedicated Node.js 20 container for long-running build processes:
# Run npm commands
docker-compose exec node npm install
docker-compose exec node npm run devPostgreSQL 17 now includes the pgvector extension for AI embeddings:
-- Enabled automatically, just use it
CREATE TABLE items (
id SERIAL PRIMARY KEY,
embedding vector(1536)
);
-- Similarity search
SELECT * FROM items ORDER BY embedding <-> '[...]' LIMIT 10;Two options for speech-to-text transcription:
A dedicated Whisper ASR webservice with OpenAI-compatible HTTP API (using faster-whisper-server):
# API endpoint (internal Docker network)
http://whisper:8000/v1/audio/transcriptions
# API endpoint (from host machine)
http://localhost:9501/v1/audio/transcriptions
# Health check
curl http://localhost:9501/healthLaravel Configuration (.env):
WHISPER_API_URL=http://whisper:8000
WHISPER_TIMEOUT=300
WHISPER_MODEL=baseExample API Call:
curl -X POST http://localhost:9501/v1/audio/transcriptions \
-F "file=@audio.mp3" \
-F "model=base" \
-F "language=en" \
-F "response_format=json"Web UI: https://whisper.localhost
For direct CLI access to Whisper:
# Run transcription via CLI
docker-compose exec php-ai whisper audio.mp3 --model base --language en
# Or from your Laravel app
docker-compose exec php-ai php artisan transcribe:audio path/to/audio.mp3Whisper Models:
| Model | Size | Memory | Speed | Accuracy |
|---|---|---|---|---|
| tiny | 39M | ~1GB | Fastest | Lower |
| base | 74M | ~1GB | Fast | Good |
| small | 244M | ~2GB | Medium | Better |
| medium | 769M | ~5GB | Slow | High |
| large | 1550M | ~10GB | Slowest | Best |
Configure the model in .env:
WHISPER_MODEL=base
WHISPER_LANGUAGE=en
WHISPER_PORT=9501For Laravel-specific services, use the override stub as a template:
# Copy the stub
cp ~/.composer/vendor/mwguerra/docker-local/stubs/docker-compose.override.yml.stub \
~/.config/docker-local/docker-compose.override.yml
# Uncomment the services you need and customizeAvailable templates:
- Horizon - Queue worker with Laravel Horizon
- Reverb - WebSocket server for real-time features
- Scheduler - Cron-like task scheduler
- Elasticsearch/Meilisearch - Full-text search
- Soketi - Open-source Pusher alternative
docker-local supports multiple Laravel projects sharing the same Docker services. Each project gets complete automatic isolation to prevent data leakage between projects.
When you create a project with docker-local make:laravel myapp, everything is set up automatically:
Creating Laravel project: myapp
Database: MySQL
Redis DBs: cache=0, session=1, queue=2
✓ Project created successfully!
✓ MySQL database 'myapp' created
✓ MySQL database 'myapp_testing' created
✓ MinIO bucket 'myapp' created
✓ .env configured with complete isolation
Isolation settings (multi-project):
✓ Database: myapp (MySQL)
✓ Redis Cache DB: 0
✓ Redis Session DB: 1
✓ Redis Queue DB: 2
✓ Cache Prefix: myapp_
✓ MinIO Bucket: myapp
✓ Reverb App ID: 847291
| Resource | How It's Isolated | Example Value |
|---|---|---|
| Database | Unique DB per project | myapp, myapp_testing |
| Redis Cache | Separate Redis DB number | REDIS_CACHE_DB=0 |
| Redis Session | Separate Redis DB number | REDIS_SESSION_DB=1 |
| Redis Queue | Separate Redis DB number | REDIS_QUEUE_DB=2 |
| Cache Prefix | Unique prefix per project | CACHE_PREFIX=myapp_ |
| MinIO Bucket | Separate S3 bucket | AWS_BUCKET=myapp |
| Reverb/WebSockets | Unique credentials | Random REVERB_APP_ID/KEY/SECRET |
| Horizon Prefix | Unique queue prefix | HORIZON_PREFIX=myapp_horizon: |
Redis has 16 databases (0-15). Each project uses 3 databases:
| Project | Cache DB | Session DB | Queue DB |
|---|---|---|---|
| 1st project | 0 | 1 | 2 |
| 2nd project | 3 | 4 | 5 |
| 3rd project | 6 | 7 | 8 |
| 4th project | 9 | 10 | 11 |
| 5th project | 12 | 13 | 14 |
This allows up to 5 fully isolated projects. Beyond that, DB numbers wrap around (with a warning).
Both database engines are available. Use the --postgres flag:
# MySQL (default)
docker-local make:laravel myapp
# PostgreSQL with pgvector
docker-local make:laravel myapp --postgresPostgreSQL projects automatically get these extensions:
uuid-ossp- UUID generationpgcrypto- Cryptographic functionsvector- pgvector for AI embeddings
# Check current project
docker-local env:check
# Audit ALL projects for conflicts
docker-local env:check --allExample conflict output:
┌─ Cross-Project Conflicts ─────────────────────────────────────────┐
⚠ CACHE_PREFIX conflict with 'other-project'
Both projects use: laravel_cache_
Why: Cache data will be shared/corrupted between projects
Fix: Change CACHE_PREFIX in one of the projects' .env files
All projects can run at the same time without conflicts:
# Terminal 1 - Work on blog
cd ~/projects/blog
docker-local tinker
# Terminal 2 - Work on api
cd ~/projects/api
docker-local test
# Terminal 3 - Work on admin
cd ~/projects/admin
docker-local queue:workEach project has its own:
- Database (no shared tables)
- Cache (no key collisions)
- Sessions (users stay logged in to their project)
- Queues (jobs don't mix between projects)
- File storage (separate MinIO buckets)
If your project has its own Docker configuration, you can migrate to docker-local for a shared, centralized environment.
| Service | Included | Notes |
|---|---|---|
| PHP 8.4 FPM | Yes | With FFmpeg, ImageMagick, 50+ extensions |
| PostgreSQL 17 | Yes | With pgvector for AI embeddings |
| MySQL 9.1 | Yes | Innovation release |
| Redis 8 | Yes | With persistence |
| MinIO | Yes | S3-compatible storage |
| Mailpit | Yes | Email testing |
| Nginx | Yes | Dynamic multi-project routing |
| Traefik | Yes | Reverse proxy with SSL |
| RTMP Server | Yes | Live streaming with HLS |
| Whisper API | Yes | HTTP API for speech-to-text |
| PHP-AI | Yes | PHP with Whisper CLI |
| Node.js 20 | Yes | Frontend build tooling |
These should remain in your project's docker-compose.override.yml:
| Service | Reason |
|---|---|
| Laravel Horizon | Uses app container, just different command |
| Laravel Reverb | WebSocket server specific to your app |
| Scheduler | Cron jobs specific to your app |
| E2E Testing (Playwright) | Test infrastructure is project-specific |
| Custom AI Models | Specialized ML models beyond Whisper |
- Copy your project's custom services to an override file:
# Create override in project root
touch ~/projects/your-app/docker-compose.override.yml- Add Laravel-specific services:
# docker-compose.override.yml
services:
horizon:
image: php # Uses docker-local's PHP image
container_name: your-app-horizon
working_dir: /var/www/your-app
volumes:
- ${PROJECTS_PATH:-../projects}:/var/www:cached
networks:
- laravel-dev
command: php artisan horizon
depends_on:
- redis
- postgres
reverb:
image: php
container_name: your-app-reverb
working_dir: /var/www/your-app
volumes:
- ${PROJECTS_PATH:-../projects}:/var/www:cached
ports:
- "8080:8080"
networks:
- laravel-dev
command: php artisan reverb:start --host=0.0.0.0 --port=8080
scheduler:
image: php
container_name: your-app-scheduler
working_dir: /var/www/your-app
volumes:
- ${PROJECTS_PATH:-../projects}:/var/www:cached
networks:
- laravel-dev
command: sh -c "while true; do php artisan schedule:run; sleep 60; done"
networks:
laravel-dev:
external: true- Update your .env for docker-local:
# Database (uses docker-local's PostgreSQL)
DB_CONNECTION=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_DATABASE=your_app
DB_USERNAME=laravel
DB_PASSWORD=secret
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
# MinIO
FILESYSTEM_DISK=s3
AWS_ENDPOINT=http://minio:9000
AWS_ACCESS_KEY_ID=minio
AWS_SECRET_ACCESS_KEY=minio123
AWS_BUCKET=your-app
AWS_USE_PATH_STYLE_ENDPOINT=true
# Mail
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025- For RTMP/streaming features:
# RTMP is included by default, just start docker-local
cd ~/projects/docker-environment
docker-compose up -d
# Create custom RTMP config with your callbacks (optional)
mkdir -p ~/projects/your-app/docker/rtmp
# Edit nginx-rtmp.conf with on_publish webhooks- Remove old Docker files from your project:
cd ~/projects/your-app
rm -rf docker/
rm docker-compose.yml
rm docker-compose.test.yml
# Keep docker-compose.override.yml for project-specific services- Start using docker-local:
docker-local up
docker-local open your-appFor a complex streaming application like pcast:
Before (project-specific):
pcast/
├── docker/
│ ├── app/Dockerfile # Custom PHP with Whisper
│ ├── nginx/ # nginx configs
│ ├── playwright/ # E2E testing
│ ├── rtmp-tester/ # Test tools
│ └── webrtc-tester/ # Test tools
├── docker-compose.yml # 12 services
├── docker-compose.test.yml # Testing
└── docker-compose.testing.yml # E2E testing
After (docker-local):
pcast/
├── docker/
│ └── rtmp/nginx-rtmp.conf # Only: Custom RTMP callbacks
├── docker-compose.override.yml # Horizon, Reverb, Scheduler
└── .env # Updated for docker-local
Start docker-local (all features included):
cd ~/projects/docker-environment
docker-compose up -dBenefits:
- Shared services across all projects
- Centralized updates and maintenance
- Consistent development environment
- Smaller project footprint
- Install PHP Debug extension
- Create
.vscode/launch.json:
{
"version": "0.2.0",
"configurations": [{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/my-project": "${workspaceFolder}"
}
}]
}- Start debugging: F5
- Settings → PHP → Debug → Port:
9003 - Settings → PHP → Servers:
- Name:
docker - Host:
localhost, Port:443 - Path mappings:
/var/www/project→~/projects/project
- Name:
- Click "Start Listening for PHP Debug Connections"
docker-local fix # Auto-diagnose and fix common issues
docker-local fix --dns -v # Detailed DNS troubleshooting
docker-local doctor # Full health check (read-only)
docker-local status # Service status
docker-local logs # View all logs
docker-local logs mysql # View specific service logsThe fix command is the recommended first step when troubleshooting - it automatically diagnoses issues and attempts to fix them where possible.
# Linux
sudo systemctl start docker
# macOS
open -a Docker
# Windows (WSL2)
# Start Docker Desktop from Windows# Find what's using the port
lsof -i :3306 # or :5432, :6379, etc.
# Or change the port in config
# Edit ~/.config/docker-local/config.json# Linux: Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
# Or fix project permissions
sudo chown -R $USER:$USER ~/projects# Quick fix - run the fix command
docker-local fix --dns
# Follow the suggested commands (requires sudo)
sudo "$(which docker-local)" setup:dns
# Manual verification
dig test.test @127.0.0.1 # Should return 127.0.0.1 (dnsmasq working)
ping test.test # Should resolve to 127.0.0.1 (full system working)If dnsmasq is working but system DNS isn't:
# Check systemd-resolved configuration (Linux)
cat /etc/systemd/resolved.conf.d/docker-local.conf
# Should contain:
# [Resolve]
# DNS=127.0.0.1#53
# Domains=~test. ~localhost.# Regenerate certificates
docker-local init --certs
# Or manually with mkcert
mkcert -install
mkcert "*.test" "*.localhost"# Clean caches and logs
docker-local clean
# Full cleanup (removes volumes)
docker-local clean --all
# Reset everything
docker-local down
docker system prune -af
docker volume prune -f
docker-local initIf you prefer using your local PHP installation with Docker services:
# 1. Configure hostnames
sudo "$(which docker-local)" setup:hosts
# 2. Now use standard PHP commands
cd ~/projects/my-app
php artisan migrate
php artisan serve
composer require laravel/sanctumThe setup:hosts command adds to /etc/hosts:
127.0.0.1 mysql postgres redis minio mailpit
# Add to ~/.bashrc
eval "$(docker-local completion bash)"# Add to ~/.zshrc
eval "$(docker-local completion zsh)"# Update docker-local CLI
composer global update mwguerra/docker-local
# Update Docker images
docker-local update
# Or combined
docker-local self-updateCreate ~/.config/docker-local/docker-compose.override.yml:
services:
elasticsearch:
image: elasticsearch:8.11.0
container_name: elasticsearch
environment:
- discovery.type=single-node
- ES_JAVA_OPTS=-Xms512m -Xmx512m
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
networks:
- laravel-dev
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9200/_cluster/health"]
interval: 30s
timeout: 10s
retries: 5
volumes:
elasticsearch_data:Then restart:
docker-local restartCreate ~/.config/docker-local/php/custom.ini:
memory_limit = 512M
upload_max_filesize = 100M
post_max_size = 100MContributions are welcome! Please read our contributing guidelines before submitting PRs.
# Clone the repository
git clone https://github.com/mwguerra/docker-local.git
cd docker-local
# Install dependencies
composer install
# Run tests
./vendor/bin/pest
# Run tests with coverage
./vendor/bin/pest --coverageMIT License. See LICENSE for details.
Made with ❤️ for Laravel developers.