Skip to content

Instantly share code, notes, and snippets.

@devhammed
Last active May 4, 2026 13:33
Show Gist options
  • Select an option

  • Save devhammed/425f32c2a81dff86bfd36e6308308d2d to your computer and use it in GitHub Desktop.

Select an option

Save devhammed/425f32c2a81dff86bfd36e6308308d2d to your computer and use it in GitHub Desktop.
Laravel Docker Setup (Caddy, Frankenphp, Queue Worker, Octane, Scheduler, Reverb)
{
{$CADDY_GLOBAL_OPTIONS}
admin {$CADDY_SERVER_ADMIN_HOST}:{$CADDY_SERVER_ADMIN_PORT}
frankenphp {
worker {
file "{$APP_PUBLIC_PATH}/frankenphp-worker.php"
{$CADDY_SERVER_WORKER_DIRECTIVE}
{$CADDY_SERVER_WATCH_DIRECTIVES}
}
}
}
{$CADDY_EXTRA_CONFIG}
{$CADDY_SERVER_SERVER_NAME} {
log {
level {$CADDY_SERVER_LOG_LEVEL}
# Redact the authorization parameters...
format filter {
wrap {$CADDY_SERVER_LOGGER}
fields {
request>headers>X-Xsrf-Token replace REDACTED
request>query>authorization replace REDACTED
}
}
}
route {
root * "{$APP_PUBLIC_PATH}"
encode zstd br gzip
# Mercure configuration is injected here...
{$CADDY_SERVER_EXTRA_DIRECTIVES}
request_body {
max_size 256MB
}
# Favicon/robots/sitemaps: skip noisy logs
@meta path /favicon.ico /robots.txt /sitemap.xml
log_skip @meta
# Static assets (long cache)
@static {
path *.css *.css.map *.js *.js.map *.jpg *.jpeg *.png *.gif *.ico *.cur *.heic *.webp *.tif *.tiff *.mp3 *.m4a *.aac *.ogg *.midi *.mid *.wav *.mp4 *.mov *.webm *.mpeg *.mpg *.avi *.ogv *.flv *.wmv *.htc *.gz *.svg *.svgz *.woff2 *.woff
}
header @static Cache-Control "public, immutable, stale-while-revalidate, max-age=31536000"
# Fonts/SVG: allow cross-origin usage (cache header inherited from @static)
@fonts {
path *.svg *.svgz *.ttf *.ttc *.otf *.eot *.woff *.woff2
}
header @fonts Access-Control-Allow-Origin "*"
# Short-lived static
@staticshort {
path *.json *.xml *.rss
}
header @staticshort Cache-Control "no-cache, max-age=3600"
# Block PHP execution in storage directory to prevent uploaded malicious PHP files from running
# Reference: Livewire arbitrary file upload (GHSA-29cq-5w36-x7w3)
@storage-php path_regexp ^/storage/.*\.php$
respond @storage-php 403
# Block access to files that may expose sensitive information
@rejected {
path *.bak *.conf *.config *.dist *.inc *.ini *.log *.sh *.sql *.swp *.swo *~ */.*
# EXCEPTION: /.well-known/* is allowed per RFC 8615 for ACME challenges
# https://www.rfc-editor.org/rfc/rfc8615
not path /.well-known/*
}
respond @rejected 403
# Security Headers
# https://owasp.org/www-project-secure-headers/
header {
defer
# Prevent clickjacking attacks by disabling iframe embedding
X-Frame-Options "SAMEORIGIN"
# Prevent MIME type sniffing attacks
X-Content-Type-Options "nosniff"
# Control referrer information sent with requests
Referrer-Policy "strict-origin-when-cross-origin"
# Enable strict transport security (HSTS)
Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Remove server identification headers
-Server
-X-Powered-By
}
@reverbApp path /app/*
reverse_proxy @reverbApp 127.0.0.1:8080
@reverbApps path /apps/*
reverse_proxy @reverbApps 127.0.0.1:8080
php_server {
index frankenphp-worker.php
try_files {path} frankenphp-worker.php
# Required for the public/storage/ directory...
resolve_root_symlink
}
file_server
}
}
# Setup base image
FROM dunglas/frankenphp:1-php8.5-bookworm
# Setup application root
WORKDIR /var/www/html
# Copy application source
COPY . .
# Install dependencies
RUN apt-get update && apt-get install -y \
git \
zip \
unzip \
libpq-dev \
libpng-dev \
libonig-dev \
libxml2-dev \
libicu-dev \
libuv1-dev \
curl \
supervisor \
ffmpeg \
&& install-php-extensions pdo_pgsql mbstring bcmath pcntl gd exif intl redis \
&& pecl install uv-beta \
&& docker-php-ext-enable uv \
&& rm -rf /var/lib/apt/lists/* \
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
&& composer install --prefer-dist --no-ansi --no-dev --no-interaction --no-plugins --no-progress --no-scripts --optimize-autoloader --ignore-platform-reqs \
&& composer clear-cache \
&& composer dump-autoload --optimize \
&& php artisan storage:link \
&& curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
&& apt-get install -y nodejs \
&& npm install -g npm \
&& npm ci \
&& mkdir -p /var/www/html/storage/xdg/{data,config} \
&& chown -R www-data:www-data /var/www/html \
&& cp .docker/Caddyfile /etc/caddy/Caddyfile \
&& cp .docker/php.ini /usr/local/etc/php/conf.d/user-php.ini \
&& cp .docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf \
&& cp .docker/run.sh /usr/local/bin/run.sh \
&& chmod +x /usr/local/bin/run.sh
# Switch user
USER www-data
# Set XDG directories
ENV XDG_DATA_HOME=/var/www/html/storage/xdg/data
ENV XDG_CONFIG_HOME=/var/www/html/storage/xdg/config
# Expose FrankenPHP
EXPOSE 8000
# Start container
ENTRYPOINT ["/usr/local/bin/run.sh"]
upload_max_filesize = 128M
post_max_size = 256M
memory_limit = 512M
max_execution_time = 256
#!/bin/bash
# Exit on error
set -eux
# Run database migrations
php artisan migrate --force --seed
# Clear Laravel caches
php artisan optimize:clear
# Rebuild Laravel caches
php artisan optimize
# Build frontend assets
npm run build
# Start supervisor
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
[supervisord]
user=www-data
nodaemon=true
minfds=50000
[program:frankenphp]
command=php artisan octane:frankenphp --host 0.0.0.0 --port 8000 --caddyfile /etc/caddy/Caddyfile --log-level INFO
directory=/var/www/html
autostart=true
autorestart=true
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:queue-worker]
command=php artisan queue:work database --sleep=3 --tries=3 --max-time=3600
process_name=%(program_name)s_%(process_num)02d
numprocs=10
directory=/var/www/html
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stopwaitsecs=3600
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:scheduler]
command=php artisan schedule:work --run-output-file=/dev/stdout
directory=/var/www/html
autostart=true
autorestart=true
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
[program:reverb]
command=php artisan reverb:start --host=127.0.0.1 --port=8080
directory=/var/www/html
autostart=true
autorestart=true
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment