Javier Feliz 3d7683c8ac
All checks were successful
Build & Push Docker Image to Registry / build (release) Successful in 5m1s
Add hooks as well as scheduler and queue worker config
2025-08-02 16:19:05 -04:00

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 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

# 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.