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.

Description
No description provided
Readme 38 KiB
v0.5.0 Latest
2025-08-02 17:27:49 -06:00
Languages
Shell 59.9%
Dockerfile 40.1%