Development Workflow Guide
Development Workflow Guide
Optimize your local development experience with Cbox base images including Xdebug, hot-reload, testing, and efficient debugging.
Table of Contents
- Local Development Setup
- Xdebug Configuration
- Hot-Reload Setup
- Testing Workflow
- Debugging Strategies
- Database Management
- Git Workflow Integration
- Performance Tips
Local Development Setup
Using Pre-built Development Images (Recommended)
Cbox provides pre-built development images with Xdebug already installed and configured. These are the easiest way to get started with debugging.
Available dev images:
ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm-devghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.3-bookworm-devghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.2-bookworm-dev- Also available:
-debian-devvariant
Development docker-compose.yml
version: '3.8'
services:
app:
# Use the pre-built dev image with Xdebug included!
image: ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.4-bookworm-dev
ports:
- "8000:80"
- "9003:9003" # Xdebug port
volumes:
# Bind mount for hot-reload
- ./:/var/www/html
environment:
# Xdebug configuration
- XDEBUG_MODE=debug,develop,coverage
- XDEBUG_CONFIG=client_host=host.docker.internal client_port=9003
- PHP_IDE_CONFIG=serverName=docker
# Application
- APP_ENV=local
- APP_DEBUG=true
- DB_HOST=mysql
- REDIS_HOST=redis
depends_on:
- mysql
- redis
- mailhog
mysql:
image: mysql:8.3
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: development
MYSQL_USER: dev
MYSQL_PASSWORD: dev
volumes:
- mysql-data:/var/lib/mysql
# Seed database on startup
- ./docker/mysql/init:/docker-entrypoint-initdb.d:ro
command: --default-authentication-plugin=mysql_native_password
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailhog:
image: mailhog/mailhog:latest
ports:
- "8025:8025" # Web UI
- "1025:1025" # SMTP
volumes:
mysql-data:
PHP Development Configuration
Create docker/php/development.ini:
[PHP]
; Display all errors
display_errors = On
display_startup_errors = On
error_reporting = E_ALL
log_errors = On
; Generous limits for development
memory_limit = 256M
max_execution_time = 300
upload_max_filesize = 100M
post_max_size = 120M
; OPcache for development
opcache.enable = 1
opcache.validate_timestamps = 1
opcache.revalidate_freq = 0
opcache.max_accelerated_files = 10000
[xdebug]
; Xdebug 3 configuration
xdebug.mode = debug,coverage
xdebug.start_with_request = yes
xdebug.client_host = host.docker.internal
xdebug.client_port = 9003
xdebug.log_level = 0
; Coverage settings
xdebug.coverage_enable = 1
; Profiling (enable when needed)
; xdebug.mode = profile
; xdebug.output_dir = /var/www/html/storage/xdebug
Start Development Environment
# Start all services
docker-compose up -d
# View logs
docker-compose logs -f app
# Check services
docker-compose ps
# Access application
open http://localhost:8000
Xdebug Configuration
VS Code Setup
Install Extension:
- Search for "PHP Debug" by Xdebug
- Install the extension
Create .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"name": "Listen for Xdebug",
"type": "php",
"request": "launch",
"port": 9003,
"pathMappings": {
"/var/www/html": "${workspaceFolder}"
},
"log": false,
"xdebugSettings": {
"max_data": 65535,
"show_hidden": 1,
"max_children": 100,
"max_depth": 5
}
}
]
}
Usage:
- Set breakpoints in your PHP files
- Click "Run and Debug" (or press F5)
- Select "Listen for Xdebug"
- Access your application in browser
- Debugger will stop at breakpoints
PHPStorm Setup
Configure Xdebug:
- Go to Settings → PHP → Debug
- Set Xdebug port to
9003 - Enable "Break at first line in PHP scripts"
Configure Server:
- Go to Settings → PHP → Servers
- Add new server:
- Name:
docker - Host:
localhost - Port:
8000 - Debugger:
Xdebug
- Name:
- Enable "Use path mappings"
- Map project root to
/var/www/html
Usage:
- Set breakpoints
- Click "Start Listening for PHP Debug Connections"
- Access your application
- PHPStorm will stop at breakpoints
Xdebug CLI Debugging
Debug artisan commands or scripts:
# Enable Xdebug for CLI
docker-compose exec app sh -c 'export XDEBUG_MODE=debug && php artisan migrate'
# Debug specific script
docker-compose exec app sh -c 'export XDEBUG_MODE=debug && php script.php'
Xdebug Performance Impact
Xdebug can slow down your application. Disable when not debugging:
services:
app:
environment:
# Disable Xdebug
- XDEBUG_MODE=off
# Or enable only when needed
- XDEBUG_MODE=${XDEBUG_MODE:-off}
# Enable Xdebug
XDEBUG_MODE=debug docker-compose up -d
# Disable Xdebug
XDEBUG_MODE=off docker-compose up -d
macOS/Windows Performance Boost (Optional)
Bind mounts on Docker Desktop can feel sluggish with large projects. Use a file-sync helper (Mutagen or Docker Desktop VirtioFS) to keep DX fast without touching your Cbox stack.
Mutagen workflow:
brew install mutagen-io/mutagen/mutagen
# Start services as usual
docker compose up -d
# Sync host source to the container webroot
mutagen sync create ./ app://cbox-app/var/www/html
Replace
cbox-appwith the container name fromdocker compose ps. Mutagen mirrors files quickly while Cbox still sees a bind mount.
Prefer native tooling? Enable VirtioFS under Docker Desktop → Settings → Resources → File Sharing for a big speed-up on macOS/Windows.
Hot-Reload Setup
File Watching with Bind Mounts
Already configured with bind mounts:
services:
app:
volumes:
- ./:/var/www/html # Changes reflect immediately
How it works:
- File changes on host → Immediately visible in container
- OPcache revalidation enabled → PHP sees changes
- No container restart needed
Asset Compilation (Laravel Mix/Vite)
Install Node.js in container (Dockerfile.dev):
FROM ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.3-bookworm
# Install Node.js
RUN apt-get update && apt-get install -y nodejs npm
USER root
WORKDIR /var/www/html
Or run Node.js separately:
services:
# ... app service
node:
image: node:20-alpine
working_dir: /app
volumes:
- ./:/app
command: npm run dev
ports:
- "5173:5173" # Vite HMR
Laravel Vite Configuration:
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
export default defineConfig({
plugins: [
laravel({
input: ['resources/css/app.css', 'resources/js/app.js'],
refresh: true,
}),
],
server: {
host: '0.0.0.0', // Allow external connections
hmr: {
host: 'localhost',
},
},
});
Testing Workflow
PHPUnit Testing
Run tests in container:
# Run all tests
docker-compose exec app php artisan test
# Run specific test file
docker-compose exec app php artisan test tests/Feature/AuthTest.php
# Run with coverage
docker-compose exec app php artisan test --coverage
# Run specific test method
docker-compose exec app php artisan test --filter testUserCanLogin
Pest Testing (Laravel)
# Run all Pest tests
docker-compose exec app ./vendor/bin/pest
# Run specific file
docker-compose exec app ./vendor/bin/pest tests/Feature/AuthTest.php
# Watch mode (re-run on changes)
docker-compose exec app ./vendor/bin/pest --watch
# Coverage
docker-compose exec app ./vendor/bin/pest --coverage
Testing with Separate Database
services:
# ... app service
mysql-test:
image: mysql:8.3
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: testing
tmpfs:
- /var/lib/mysql # In-memory for speed
Update phpunit.xml:
<php>
<env name="DB_CONNECTION" value="mysql"/>
<env name="DB_HOST" value="mysql-test"/>
<env name="DB_DATABASE" value="testing"/>
</php>
Parallel Testing
# Install parallel testing
docker-compose exec app composer require --dev brianium/paratest
# Run tests in parallel
docker-compose exec app php artisan test --parallel
# Specify process count
docker-compose exec app php artisan test --parallel --processes=4
Debugging Strategies
Laravel Debugbar
Install:
docker-compose exec app composer require barryvdh/laravel-debugbar --dev
Configuration (.env):
DEBUGBAR_ENABLED=true
Access: Bottom of your Laravel application pages
Laravel Telescope
Install:
docker-compose exec app composer require laravel/telescope --dev
docker-compose exec app php artisan telescope:install
docker-compose exec app php artisan migrate
Access: http://localhost:8000/telescope
Logging Best Practices
Use structured logging:
// Laravel
Log::info('User logged in', [
'user_id' => $user->id,
'ip' => $request->ip(),
]);
// Symfony
$logger->info('User logged in', [
'user_id' => $user->getId(),
'ip' => $request->getClientIp(),
]);
View logs:
# Tail application logs
docker-compose logs -f app
# Search logs
docker-compose logs app | grep "ERROR"
# Export logs
docker-compose logs app > debug.log
Database Query Debugging
Laravel:
// Log all queries
DB::listen(function ($query) {
Log::info($query->sql, $query->bindings);
});
// Or use Laravel Debugbar (shows queries in browser)
Symfony:
# config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
API Debugging Tools
# Test API endpoints with curl
docker-compose exec app curl http://localhost/api/users
# Or use HTTPie
docker-compose exec app apt-get update && apt-get install -y httpie
docker-compose exec app http GET http://localhost/api/users
Database Management
Database GUI Tools
Adminer (Lightweight):
services:
adminer:
image: adminer:latest
ports:
- "8080:8080"
environment:
ADMINER_DEFAULT_SERVER: mysql
Access: http://localhost:8080
phpMyAdmin:
services:
phpmyadmin:
image: phpmyadmin:latest
ports:
- "8080:80"
environment:
PMA_HOST: mysql
PMA_USER: root
PMA_PASSWORD: root
Database Seeding
Laravel:
# Run migrations
docker-compose exec app php artisan migrate
# Seed database
docker-compose exec app php artisan db:seed
# Fresh migration with seed
docker-compose exec app php artisan migrate:fresh --seed
Symfony:
# Run migrations
docker-compose exec app php bin/console doctrine:migrations:migrate
# Load fixtures
docker-compose exec app php bin/console doctrine:fixtures:load
Database Backups
# Backup database
docker-compose exec mysql mysqldump -u root -proot development > backup.sql
# Restore database
docker-compose exec -T mysql mysql -u root -proot development < backup.sql
# Copy database to another container
docker-compose exec mysql mysqldump -u root -proot development | \
docker-compose exec -T mysql-test mysql -u root -proot testing
Git Workflow Integration
Pre-Commit Hooks
Install PHP CS Fixer:
docker-compose exec app composer require --dev friendsofphp/php-cs-fixer
Create .git/hooks/pre-commit:
#!/bin/bash
# Run PHP CS Fixer
docker-compose exec -T app ./vendor/bin/php-cs-fixer fix --dry-run --diff
if [ $? -ne 0 ]; then
echo "❌ Code style issues found. Run: docker-compose exec app ./vendor/bin/php-cs-fixer fix"
exit 1
fi
# Run tests
docker-compose exec -T app php artisan test
if [ $? -ne 0 ]; then
echo "❌ Tests failed"
exit 1
fi
echo "✅ Pre-commit checks passed"
# Make executable
chmod +x .git/hooks/pre-commit
Husky Alternative (PHP)
Install GrumPHP:
docker-compose exec app composer require --dev phpro/grumphp
Create grumphp.yml:
grumphp:
tasks:
phpcs:
standard: PSR12
phpunit:
always_execute: false
composer:
no_check_all: true
Branch-Based Environments
# docker-compose.override.yml (gitignored)
services:
app:
environment:
- APP_ENV=${GIT_BRANCH:-local}
- DB_DATABASE=app_${GIT_BRANCH:-local}
# Automatically use branch name
export GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
docker-compose up -d
Performance Tips
Optimize Docker Performance
macOS (Docker Desktop):
# Use delegated mode for better performance
services:
app:
volumes:
- ./:/var/www/html:delegated
Linux: Native performance, no optimization needed
Windows (WSL 2): Keep files in WSL filesystem
Reduce Build Time
Use BuildKit:
# Enable BuildKit
export DOCKER_BUILDKIT=1
# Build with cache
docker-compose build
Optimize Composer Install
# Cache composer dependencies
FROM ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.3-bookworm
COPY composer.json composer.lock ./
RUN composer install --no-scripts --no-autoloader
COPY . .
RUN composer dump-autoload --optimize
Development vs Production Images
Dockerfile.dev:
FROM ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.3-bookworm
# Install Xdebug
RUN apt-get update && apt-get install -y $PHPIZE_DEPS \
&& pecl install xdebug \
&& docker-php-ext-enable xdebug
# Install Node.js for asset compilation
RUN apt-get update && apt-get install -y nodejs npm
# Development tools
RUN apt-get update && apt-get install -y git vim
Dockerfile.prod:
FROM ghcr.io/cboxdk/php-baseimages/php-fpm-nginx:8.3-bookworm
# Production optimizations only
COPY --chown=www-data:www-data . /var/www/html
RUN composer install --no-dev --optimize-autoloader
Quick Development Commands
Makefile
Create Makefile:
.PHONY: help
help:
@echo "Available commands:"
@echo " make up - Start development environment"
@echo " make down - Stop development environment"
@echo " make logs - View logs"
@echo " make shell - Open shell in app container"
@echo " make test - Run tests"
@echo " make migrate - Run migrations"
@echo " make seed - Seed database"
@echo " make fresh - Fresh migration with seed"
up:
docker-compose up -d
down:
docker-compose down
logs:
docker-compose logs -f app
shell:
docker-compose exec app sh
test:
docker-compose exec app php artisan test
migrate:
docker-compose exec app php artisan migrate
seed:
docker-compose exec app php artisan db:seed
fresh:
docker-compose exec app php artisan migrate:fresh --seed
fix:
docker-compose exec app ./vendor/bin/php-cs-fixer fix
Usage:
make up
make test
make fresh
Troubleshooting
Container Won't Start
# Check logs
docker-compose logs app
# Rebuild without cache
docker-compose build --no-cache
# Remove all containers and volumes
docker-compose down -v
Permission Issues
# Fix permissions
docker-compose exec app chown -R www-data:www-data /var/www/html/storage
# Or set PUID/PGID
docker-compose exec app sh -c 'export PUID=1000 PGID=1000'
Xdebug Not Working
# Verify Xdebug is enabled
docker-compose exec app php -m | grep xdebug
# Check Xdebug configuration
docker-compose exec app php -i | grep xdebug
# Test connection
docker-compose exec app php -r "xdebug_info();"
Slow Performance
# Disable Xdebug when not debugging
XDEBUG_MODE=off docker-compose up -d
# Use delegated mounts (macOS)
volumes:
- ./:/var/www/html:delegated
# Check Docker resource allocation (Docker Desktop → Settings → Resources)
Related Documentation
- Laravel Guide - Laravel-specific development
- Symfony Guide - Symfony-specific development
- Extending Images - Custom development images
- Debugging Guide - Advanced debugging
Questions? Check common issues or ask in GitHub Discussions.