9.4 KiB
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
:
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
# 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
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
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:
#!/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
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
#!/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
#!/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
#!/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
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
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
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:
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:
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:
# 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:
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 withphp artisan key:generate
DB_*
- Database connection settingsCACHE_DRIVER
- Redis recommended for productionSESSION_DRIVER
- Redis or database recommendedQUEUE_CONNECTION
- Redis or database for background jobsENABLE_QUEUE_WORKER=true
- Enable Laravel queue workerENABLE_SCHEDULER=true
- Enable Laravel task scheduler
Troubleshooting
Permission Issues
# 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
# Check database connectivity
docker exec -it container-name php /app/artisan tinker
>>> DB::connection()->getPdo();
View Logs
# 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
# 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.