mwguerra / docker-local
Complete Docker development environment for Laravel - PHP 8.4, MySQL 9.1, PostgreSQL 17, Redis 8, Traefik 3.6
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 0
Forks: 0
Open Issues: 0
Language:Shell
pkg:composer/mwguerra/docker-local
Requires
- php: ^8.2
Requires (Dev)
- mockery/mockery: ^1.6
- pestphp/pest: ^3.0 || ^4.0
README
Complete Docker development environment for Laravel with a powerful CLI.
Global Composer Package — Install once, use everywhere. No per-project Docker configuration needed.
Quick Install
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 open
Need prerequisites first? See Installation for platform-specific setup guides.
Features
- 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)
Table of Contents
- 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
Requirements
All Platforms
| 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 |
System Requirements
- RAM: 8GB minimum, 16GB recommended
- Disk: 20GB free space
- CPU: 64-bit processor with virtualization support
Installation
Linux
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:dns
macOS
Tested 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:dns
Windows (WSL2)
Important: docker-local requires WSL2 on Windows. Native Windows is not supported.
Step 1: Install WSL2
# Run in PowerShell as Administrator wsl --install -d Ubuntu
Restart your computer when prompted.
Step 2: Install Docker Desktop
- 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
Step 3: Install docker-local (in WSL2 Ubuntu)
# 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:dns
Accessing Projects from Windows
Your WSL2 projects are accessible in Windows Explorer at:
\\wsl$\Ubuntu\home\<username>\projects
Or in VS Code:
# From WSL2 terminal code ~/projects/my-project
Quick Start
New 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 down
Existing Project
If 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 settings
Required .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=true
Quick 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-app
Checklist 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
CLI Commands
Setup & Diagnostics
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 images
Fix 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 changes
The 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
Environment Management
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 resources
Project Commands
docker-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
Development Commands
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 shell
Artisan Shortcuts
docker-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:command
Database Commands
docker-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 --seed
Queue Commands
docker-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 jobs
Xdebug Commands
docker-local xdebug on # Enable Xdebug docker-local xdebug off # Disable Xdebug (better performance) docker-local xdebug status # Show Xdebug status
Startup Commands
Configure 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 status
Platform-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 |
Environment Verification
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 .env
Configuration
Configuration 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"
}
}
Directory Structure
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
Package Structure (Source Code)
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
User Configuration (~/.config/docker-local/)
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
Projects Directory (~/projects/)
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
Understanding Environment Files
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 |
.env.example (Docker/Infrastructure)
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 configuration
This file is copied to ~/.config/docker-local/.env and read by docker-compose.yml via ${VARIABLE} syntax.
laravel.env.example (Application)
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 name
This file is copied to each project's .env (~/projects/my-app/.env) and read by Laravel via env() and config().
Why Both Files Exist
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.
Related Files
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.
Services
URLs
| Service | URL |
|---|---|
| Your Projects | https://<project>.test |
| Traefik Dashboard | https://traefik.localhost |
| Mailpit | https://mail.localhost |
| MinIO Console | https://minio.localhost |
Ports
| 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 |
Default Credentials
| Service | Username | Password |
|---|---|---|
| MySQL (root) | root | secret |
| MySQL (user) | laravel | secret |
| PostgreSQL | laravel | secret |
| MinIO | minio | minio123 |
All Included Services
All services are now enabled by default. Simply run:
docker-compose up -d
RTMP Server (Live Streaming)
The 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/recordings
Node.js Container
A 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 dev
PostgreSQL with pgvector
PostgreSQL 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;
AI/Whisper Transcription
Two options for speech-to-text transcription:
Option 1: Whisper API Container (Recommended)
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/health
Laravel Configuration (.env):
WHISPER_API_URL=http://whisper:8000 WHISPER_TIMEOUT=300 WHISPER_MODEL=base
Example 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
Option 2: PHP-AI Container (CLI)
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.mp3
Whisper 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=9501
Laravel Workers (Horizon, Reverb, Scheduler)
For 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 customize
Available 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
Multi-Project Support
docker-local supports multiple Laravel projects sharing the same Docker services. Each project gets complete automatic isolation to prevent data leakage between projects.
What Gets Created Automatically
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
Automatic Isolation Details
| 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 Database Allocation
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).
PostgreSQL vs MySQL
Both database engines are available. Use the --postgres flag:
# MySQL (default) docker-local make:laravel myapp # PostgreSQL with pgvector docker-local make:laravel myapp --postgres
PostgreSQL projects automatically get these extensions:
uuid-ossp- UUID generationpgcrypto- Cryptographic functionsvector- pgvector for AI embeddings
Conflict Detection
# Check current project docker-local env:check # Audit ALL projects for conflicts docker-local env:check --all
Example 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
Running Multiple Projects Simultaneously
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:work
Each 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)
Migrating from Project-Specific Docker
If your project has its own Docker configuration, you can migrate to docker-local for a shared, centralized environment.
What docker-local Provides
| 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 |
What Stays Project-Specific
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 |
Migration Steps
- 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-app
Example: pcast Migration
For 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 -d
Benefits:
- Shared services across all projects
- Centralized updates and maintenance
- Consistent development environment
- Smaller project footprint
IDE Integration
VS Code
- 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
PhpStorm
- 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"
Troubleshooting
General Diagnostics
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 logs
The fix command is the recommended first step when troubleshooting - it automatically diagnoses issues and attempts to fix them where possible.
Common Issues
"Docker daemon is not running"
# Linux sudo systemctl start docker # macOS open -a Docker # Windows (WSL2) # Start Docker Desktop from Windows
"Port already in use"
# 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
"Permission denied" errors
# Linux: Add user to docker group sudo usermod -aG docker $USER newgrp docker # Or fix project permissions sudo chown -R $USER:$USER ~/projects
"*.test domains not resolving"
# 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.
SSL Certificate Issues
# Regenerate certificates docker-local init --certs # Or manually with mkcert mkcert -install mkcert "*.test" "*.localhost"
Cleaning Up
# 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 init
Using Local PHP
If 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/sanctum
The setup:hosts command adds to /etc/hosts:
127.0.0.1 mysql postgres redis minio mailpit
Shell Completion
Bash
# Add to ~/.bashrc eval "$(docker-local completion bash)"
Zsh
# Add to ~/.zshrc eval "$(docker-local completion zsh)"
Updating
# Update docker-local CLI composer global update mwguerra/docker-local # Update Docker images docker-local update # Or combined docker-local self-update
Extending
Adding Custom Services
Create ~/.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 restart
Custom PHP Configuration
Create ~/.config/docker-local/php/custom.ini:
memory_limit = 512M upload_max_filesize = 100M post_max_size = 100M
Contributing
Contributions 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 --coverage
License
MIT License. See LICENSE for details.
Made with ❤️ for Laravel developers.