# Docker Laravel Base Image A production-ready Docker base image for Laravel applications using FrankenPHP and Laravel Octane. This image provides a lightweight, high-performance foundation for containerized Laravel apps. ## Features - **FrankenPHP**: Modern PHP application server with built-in HTTPS, HTTP/2, and HTTP/3 support - **Laravel Octane**: High-performance application server integration - **Multi-database Support**: Pre-installed extensions for PostgreSQL, MySQL, and SQLite - **Queue Workers**: Optional supervisor-managed Laravel queue workers - **Task Scheduler**: Optional supervisor-managed Laravel task scheduler - **Production Ready**: Optimized configuration for production deployments - **Extensible**: Hook system for custom initialization scripts - **Automatic Setup**: Handles migrations, key generation, and caching automatically ## Quick Start ### 1. Create Your Laravel App Dockerfile In your Laravel application root, create a `Dockerfile`: ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Copy your Laravel application COPY . /app # Install Composer dependencies RUN composer install --no-dev --optimize-autoloader # Set proper permissions RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache RUN chmod -R 775 /app/storage /app/bootstrap/cache EXPOSE 8000 ``` ### 2. Build and Run ```bash # Build your application image docker build -t my-laravel-app . # Run the container docker run -p 8000:8000 my-laravel-app ``` Your Laravel application will be available at `http://localhost:8000`. ## Environment Configuration The base image includes a default `.env` configuration optimized for containerized environments. You can override these settings by: ### Option 1: Custom .env File ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Replace the default .env with your custom one COPY ./.env.production /app/.env COPY . /app RUN composer install --no-dev --optimize-autoloader ``` ### Option 2: Environment Variables ```bash docker run -p 8000:8000 \ -e APP_NAME="My Laravel App" \ -e APP_ENV=production \ -e DB_CONNECTION=mysql \ -e DB_HOST=mysql \ -e DB_DATABASE=myapp \ my-laravel-app ``` ## Hook System Extend the base image's startup process by adding a `hook.sh` script that runs after Laravel initialization but before the server starts. ### Creating a Hook Script Create a `hook.sh` file in your project: ```bash #!/bin/sh set -e echo "Running custom initialization..." # Seed the database php /app/artisan db:seed --force # Clear and warm up caches php /app/artisan route:cache php /app/artisan event:cache # Start background workers (if using supervisor) # supervisord -c /etc/supervisor/conf.d/supervisord.conf echo "Custom initialization complete!" ``` ### Using the Hook in Your Dockerfile ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Copy your hook script COPY ./hook.sh /app/hook.sh # Copy your Laravel application COPY . /app RUN composer install --no-dev --optimize-autoloader RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache RUN chmod -R 775 /app/storage /app/bootstrap/cache EXPOSE 8000 ``` ### Hook Script Examples #### Database Seeding ```bash #!/bin/sh set -e # Only seed if tables are empty if [ "$(php /app/artisan tinker --execute='echo \App\Models\User::count();')" = "0" ]; then echo "Seeding database..." php /app/artisan db:seed --force fi ``` #### Queue Worker Setup ```bash #!/bin/sh set -e # Queue workers are managed by supervisor - just restart them php /app/artisan queue:restart # Custom queue configuration can be done here # php /app/artisan queue:prune-failed --hours=48 ``` #### Cache Warming ```bash #!/bin/sh set -e # Pre-compile views and routes for better performance php /app/artisan view:cache php /app/artisan route:cache php /app/artisan config:cache ``` ## Queue Workers and Scheduler Enable Laravel queue workers and task scheduler by setting environment variables: ### Basic Queue Worker Setup ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Enable queue worker ENV ENABLE_QUEUE_WORKER=true COPY . /app RUN composer install --no-dev --optimize-autoloader ``` ### Full Setup with Scheduler ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Enable both queue worker and scheduler ENV ENABLE_QUEUE_WORKER=true ENV ENABLE_SCHEDULER=true COPY . /app RUN composer install --no-dev --optimize-autoloader ``` ### Docker Compose with Queue Workers ```yaml version: '3.8' services: app: build: . ports: - "8000:8000" environment: - APP_ENV=production - ENABLE_QUEUE_WORKER=true - ENABLE_SCHEDULER=true - DB_CONNECTION=mysql - DB_HOST=mysql - DB_DATABASE=laravel - DB_USERNAME=laravel - DB_PASSWORD=secret - QUEUE_CONNECTION=redis - REDIS_HOST=redis depends_on: - mysql - redis mysql: image: mysql:8.0 environment: MYSQL_DATABASE: laravel MYSQL_USER: laravel MYSQL_PASSWORD: secret MYSQL_ROOT_PASSWORD: secret volumes: - mysql_data:/var/lib/mysql redis: image: redis:alpine ports: - "6379:6379" volumes: mysql_data: ``` ## Docker Compose Example For local development or multi-service deployments: ```yaml version: '3.8' services: app: build: . ports: - "8000:8000" environment: - APP_ENV=production - DB_CONNECTION=mysql - DB_HOST=mysql - DB_DATABASE=laravel - DB_USERNAME=laravel - DB_PASSWORD=secret depends_on: - mysql - redis mysql: image: mysql:8.0 environment: MYSQL_DATABASE: laravel MYSQL_USER: laravel MYSQL_PASSWORD: secret MYSQL_ROOT_PASSWORD: secret volumes: - mysql_data:/var/lib/mysql redis: image: redis:alpine ports: - "6379:6379" volumes: mysql_data: ``` ## Advanced Configuration ### Custom PHP Extensions If your application needs additional PHP extensions: ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest # Install additional PHP extensions RUN install-php-extensions \ gd \ imagick \ redis COPY . /app RUN composer install --no-dev --optimize-autoloader ``` ### Multi-stage Build For optimized production images: ```dockerfile # Build stage FROM composer:2 as composer COPY composer.json composer.lock /app/ WORKDIR /app RUN composer install --no-dev --optimize-autoloader --no-scripts # Production stage FROM gitgud.foo/thegrind/laravel-base:latest # Copy vendor dependencies from build stage COPY --from=composer /app/vendor /app/vendor # Copy application code COPY . /app # Set permissions RUN chown -R www-data:www-data /app/storage /app/bootstrap/cache RUN chmod -R 775 /app/storage /app/bootstrap/cache EXPOSE 8000 ``` ## Production Deployment ### Health Checks Add health checks to your Dockerfile: ```dockerfile FROM gitgud.foo/thegrind/laravel-base:latest COPY . /app RUN composer install --no-dev --optimize-autoloader # Add health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 EXPOSE 8000 ``` ### Environment Variables Key environment variables for production: - `APP_ENV=production` - `APP_DEBUG=false` - `APP_KEY` - Generate with `php artisan key:generate` - `DB_*` - Database connection settings - `CACHE_DRIVER` - Redis recommended for production - `SESSION_DRIVER` - Redis or database recommended - `QUEUE_CONNECTION` - Redis or database for background jobs - `ENABLE_QUEUE_WORKER=true` - Enable Laravel queue worker - `ENABLE_SCHEDULER=true` - Enable Laravel task scheduler ## Troubleshooting ### Permission Issues ```bash # Fix storage and cache permissions docker exec -it container-name chown -R www-data:www-data /app/storage /app/bootstrap/cache docker exec -it container-name chmod -R 775 /app/storage /app/bootstrap/cache ``` ### Database Connection Issues ```bash # Check database connectivity docker exec -it container-name php /app/artisan tinker >>> DB::connection()->getPdo(); ``` ### View Logs ```bash # View application logs docker exec -it container-name tail -f /app/storage/logs/laravel.log # View FrankenPHP logs docker logs container-name # View supervisor logs docker exec -it container-name tail -f /var/log/supervisor/supervisord.log # View queue worker logs docker exec -it container-name tail -f /var/log/supervisor/laravel-queue.log # View scheduler logs docker exec -it container-name tail -f /var/log/supervisor/laravel-scheduler.log ``` ### Queue Worker Management ```bash # Check supervisor status docker exec -it container-name supervisorctl status # Restart queue workers docker exec -it container-name supervisorctl restart laravel-queue:* # Start/stop scheduler docker exec -it container-name supervisorctl start laravel-scheduler:* docker exec -it container-name supervisorctl stop laravel-scheduler:* ``` ## Base Image Details - **Base**: `dunglas/frankenphp` - **PHP Extensions**: pcntl, pdo, pdo_pgsql, pdo_mysql, pdo_sqlite - **System Packages**: supervisor, cron - **Ports**: 8000 (HTTP), 2019 (Admin) - **Entrypoint**: Handles migrations, key generation, caching, supervisor services, and server startup - **Default Database**: SQLite (configurable) - **Queue Workers**: Supervisor-managed, optional via `ENABLE_QUEUE_WORKER=true` - **Scheduler**: Supervisor-managed, optional via `ENABLE_SCHEDULER=true` ## Contributing This base image is designed to be minimal and flexible. For feature requests or issues, please refer to the project repository.