Production-ready PHP Docker images for Laravel (Alpine/Debian/FrankenPHP)
5.2K
PHP Docker images for Laravel applications. The modern build path targets ServerSideUp v5.0.2, which carries the PHP 8.5.12 / 8.4.27 / 8.3.36 security fixes for CVE-2026-109618 and CVE-2026-109619. PHP 8.1 is EOL and stays unpatched (8.1.34); move to 8.3 or newer.
🚀 Powered by ServerSideUp PHP
These images are built on top of the rock-solid, high-performance serversideup/php base images, extending them with:
Unlike standard workflows where you must build a custom Docker image for every project, these images are designed for Zero-Build Deployment (ideal for Coolify, Portainer, etc.):
dekadev/php:8.4-alpine) can run multiple different projects.REPO_URL env var, and the container automatically clones your code, runs migrations, and starts the app.Comparison:
| Feature | Standard Dockerfile | DEKADEV Image |
|---|---|---|
| Process | Build → Push → Pull → Run | Pull → Run |
| Source Code | Baked INSIDE image | Cloned dynamically (via git/volume) |
| Efficiency | 1 Image per Project | 1 Image for ALL Projects |
legacy/New Unified Naming: dekadev/php:<version>-<variant>
| PHP Version | Alpine | AMD64 | FrankenPHP | MySQL Client | Status |
|---|---|---|---|---|---|
| 8.5 | ✅ php:8.5-alpine | ✅ php:8.5-amd64 | ✅ php:8.5-frankenphp-amd64 | Alpine: MariaDB / AMD64: Oracle MySQL 9.1 | Active |
| 8.4 | ✅ php:8.4-alpine | ✅ php:8.4-amd64 | ✅ php:8.4-frankenphp-amd64 | Alpine: MariaDB 11.4 / AMD64: Oracle MySQL 9.1 | Active |
| 8.3 | ✅ php:8.3-alpine | ✅ php:8.3-amd64 | ✅ php:8.3-frankenphp-amd64 | Alpine: MariaDB 11.4 / AMD64: Oracle MySQL 9.1 | Active |
| 8.2 | ✅ php:8.2-alpine | ✅ php:8.2-amd64 | - | Alpine: MariaDB / AMD64: Oracle MySQL 9.1 | Active (security) |
| 8.1 | ✅ php:8.1-alpine | ✅ php:8.1-amd64 | - | Alpine: MariaDB / AMD64: Oracle MySQL 9.1 | PHP EOL; compatibility only |
MySQL Client Notes:
- Alpine images: Use MariaDB client (fully compatible with MySQL 5.7-9.1 servers)
- AMD64/FrankenPHP images: Use Oracle MySQL 9.1.0 client
- Deployment architecture:
build.shtargetslinux/amd64by default (DEKA_PLATFORMoverrides it). Published tags are amd64-only, except the Alpine tags8.2-alpine,8.3-alpine,8.4-alpineand8.5-alpine, which are multi-arch (linux/amd64+linux/arm64). Use them on ARM servers such as Oracle Cloud A1 (8.4-alpinesince 19 Oct 2026; 8.2, 8.3 and 8.5 since 22 Oct 2026).- No arm64 for the Debian variants (
*-amd64,-nodejs,-chromium):Dockerfile.amd64installs Oracle MySQL 9.1 Debian 12 packages, which Oracle publishes only for amd64 (the arm64 URLs return 404). Use Alpine on ARM.- All clients work seamlessly with MySQL/MariaDB servers, AWS RDS, Google Cloud SQL, Azure Database
Note: NodeJS variants are maintained for PHP 8.3, 8.4, and 8.5 on AMD64. Other specialized variants are included in all for PHP 8.3 only; explicitly requesting another supported PHP version emits a compatibility warning. Use unified naming dekadev/php:<version>-<base>-<variant>.
| Variant | Base | Purpose | Image Name | Status |
|---|---|---|---|---|
| NodeJS | PHP 8.3+ AMD64 | Full-stack with Node.js 24 LTS | php:8.4-amd64-nodejs | ✅ Active |
| FFmpeg | PHP 8.3 AMD64 | Media processing | php:8.3-amd64-ffmpeg | ✅ Active |
| Chromium | PHP 8.3+ AMD64 | Node 24 + Chromium for Browsershot PDF | php:8.4-amd64-chromium | ✅ Active |
| Puppeteer | PHP 8.3 AMD64 | Web scraping/PDF generation | php:8.3-amd64-puppeteer | ✅ Active |
| LibreOffice | PHP 8.3 Debian | Document processing | php:8.3-amd64-libreoffice | ✅ Active |
| Python | PHP 8.3 AMD64 | Python 3.11 + pip (venv /opt/venv on PATH) | php:8.3-amd64-python | ✅ Active |
| Queue | PHP 8.3 Alpine | Laravel Queue Workers | ⚠️ php83-alpine-queue (deprecated) | ⚠️ Deprecated\* |
| Schedule | PHP 8.3 Alpine | Laravel Task Scheduler | ⚠️ php83-alpine-schedule (deprecated) | ⚠️ Deprecated\* |
*Deprecated Notice: Queue and Schedule variants are deprecated. Use core images with
QUEUE_SERVICE_MODEand service configuration instead. See legacy/README.md for old approach documentation.
ServerSideUp v5 migration: Modern Dockerfiles pin upstream v5.0.2 and Debian Bookworm where applicable. The DEKA registry tag has no upstream release or build date in its name. Modern builds reject PHP 7.4/8.0 because upstream v5 no longer provides them. Existing registry tags are not automatically upgraded by changing this repository.
Image naming follows Docker conventions.
Old Naming (Deprecated, see legacy/ directory):
dekadev/php84-alpine → dekadev/php:8.4-alpinedekadev/php83-frankenphp-amd64 → dekadev/php:8.3-frankenphp-amd64For gradual migration or legacy builds, see legacy/README.md.
# Pull and run PHP 8.4 Alpine
docker run -d -p 8080:8080 dekadev/php:8.4-alpine
# Pull and run PHP 8.4 with queue support (via configuration)
docker run -d -p 8080:8080 -e QUEUE_SERVICE_MODE=horizon dekadev/php:8.4-alpine
version: "3.8"
services:
app:
image: dekadev/php:8.4-alpine
ports:
- "8080:8080"
environment:
- APP_BASE_DIR=/var/www/html
- QUEUE_SERVICE_MODE=default
- REVERB_SERVICE_ENABLED=true
volumes:
- ./your-app:/var/www/html
# Clone your Laravel app
git clone https://github.com/your/laravel-app.git
# Run with queue and scheduler support (via configuration)
docker run -d \
-p 8080:8080 \
-v $(pwd)/laravel-app:/var/www/html \
-e QUEUE_SERVICE_MODE=horizon \
-e REVERB_SERVICE_ENABLED=true \
dekadev/php:8.4-alpine
| Variable | Default | Description |
|---|---|---|
APP_BASE_DIR | /var/www/html | Application directory |
QUEUE_SERVICE_MODE | - | Queue mode: horizon or default (set off/empty to disable) |
QUEUE_SERVICE_PARAMS | - | Additional queue worker parameters |
REVERB_SERVICE_ENABLED | - | Enable Laravel Reverb WebSocket server |
REVERB_SERVICE_PORT | 8100 | Reverb service port |
OCTANE_SERVICE_ENABLED | - | Enable Laravel Octane (FrankenPHP) |
OCTANE_WORKERS | auto | Number of Octane workers |
OCTANE_MAX_REQUESTS | 500 | Max requests before worker restart |
OCTANE_LOG_LEVEL | info | Octane log level (debug, info, error) |
OCTANE_HTTPS | false | Force Laravel URL generation to HTTPS (Octane config) |
OCTANE_START_HTTPS | unset | Legacy internal TLS switch, mapped to native SSL_MODE |
OCTANE_HTTP_REDIRECT | false | Legacy redirect switch; requires OCTANE_START_HTTPS=true |
LOG_CHANNEL | stderr | Laravel application log channel |
LOG_STDERR_FORMATTER | default | Laravel log format: default or json |
CADDY_LOG_FORMAT | auto | Caddy logging; Octane requires JSON on stderr |
SSL_MODE | mixed | SSL mode: off, mixed, or full |
CADDY_SERVER_ROOT | /var/www/html/public | Document root for FrankenPHP |
HEALTHCHECK_PATH | /healthcheck | Health check endpoint path |
INIT_SCRIPT | true | Enable DEKA app initialization |
DEKA_SKIP_APP_INIT | false | Preserve mounted app code/dependencies during an image-only rollout; service setup still runs |
TRUSTED_PROXY | cloudflare | Upstream proxy profile: cloudflare, sucuri, local, or off |
POST_INSTALL_COMMANDS | - | Shell commands to run after install |
SETUP_COMPOSER_AUTH | true | Enable Composer auth setup |
PERFORM_COMPOSER_INSTALL | true | Enable composer install during init |
COMPOSER_INSTALL_NO_DEV | true | Add --no-dev to composer install (set false to install dev dependencies) |
PERFORM_GIT_OPERATIONS | true | Enable Git cloning/updates |
GIT_HARD_CLEAN_BEFORE_PULL | false | Run git checkout . and git clean -df before pull |
SETUP_SQLITE | true | Enable SQLite setup |
Configure Composer authentication for private repositories:
# HTTP Basic Authentication
COMPOSER_AUTH_CONFIG="packagist.org|username|password"
# GitHub OAuth
COMPOSER_AUTH_CONFIG="github-oauth|github.com|your_token"
# Multiple configurations
COMPOSER_AUTH_CONFIG="github-oauth|github.com|token1;http-basic|packagist.org|user|pass"
All images include MySQL/MariaDB client tools pre-installed. See the MySQL Client column in the Core PHP Versions table above for version details.
Why MariaDB for Alpine? Oracle MySQL binaries require glibc and are incompatible with Alpine's musl libc. MariaDB client is fully compatible with MySQL servers and works seamlessly for all database operations.
Usage Examples:
# mysqldump works out of the box
docker exec my-container mysqldump -h database.example.com -u user -p database > backup.sql
# From PHP/Laravel (Spatie Backup, etc.)
php artisan backup:run # Uses mysqldump internally
# Direct mysql client access
docker exec -it my-container mysql -h localhost -u root -p
# Verify client version
docker run --rm dekadev/php:8.4-amd64 mysql --version
# Output: mysql Ver 9.1.0 for Linux on x86_64 (MySQL Community Server - GPL)
docker run --rm dekadev/php:8.4-alpine mysql --version
# Output: mysql from 11.4.8-MariaDB, client 15.2 for Linux (aarch64)
Database Server Compatibility:
# Horizon mode (recommended)
docker run -e QUEUE_SERVICE_MODE=horizon dekadev/php:8.4-alpine
# Default queue worker with parameters
docker run -e QUEUE_SERVICE_MODE=default \
-e QUEUE_SERVICE_PARAMS="--sleep=3 --tries=3" \
dekadev/php:8.4-alpine
# Disable queue service (recommended for Coolify/Easypanel when not needed)
docker run -e QUEUE_SERVICE_MODE=off dekadev/php:8.4-alpine
Queue service behavior:
QUEUE_SERVICE_MODE=horizon → enable Laravel HorizonQUEUE_SERVICE_MODE=default → enable php artisan queue:workQUEUE_SERVICE_MODE empty / unset / any other value (example: off) → queue service disabled# Enable the Laravel scheduler explicitly
docker run -e SCHEDULE_RUN=true dekadev/php:8.4-alpine
# Enable Reverb service
docker run -e REVERB_SERVICE_ENABLED=true \
-e REVERB_SERVICE_PORT=8100 \
-p 8100:8100 \
dekadev/php:8.4-alpine
Prerequisites: Your Laravel project must have Octane installed:
composer require laravel/octane
php artisan octane:install --server=frankenphp
Important: Runtime install defaults to
composer install --no-dev, solaravel/octaneshould be inrequire(notrequire-dev). If needed, setCOMPOSER_INSTALL_NO_DEV=false.
Usage:
# Run with Octane enabled
docker run -d -p 8080:8080 -p 8443:8443 \
-e OCTANE_SERVICE_ENABLED=true \
dekadev/php:8.3-frankenphp-amd64
# With queue and Reverb support
docker run -d -p 8080:8080 -p 8443:8443 -p 8100:8100 \
-e OCTANE_SERVICE_ENABLED=true \
-e QUEUE_SERVICE_MODE=horizon \
-e REVERB_SERVICE_ENABLED=true \
dekadev/php:8.3-frankenphp-amd64
# Custom SSL mode (off, mixed, full)
docker run -d -p 8080:8080 -p 8443:8443 \
-e OCTANE_SERVICE_ENABLED=true \
-e SSL_MODE=full \
dekadev/php:8.3-frankenphp-amd64
# Behind Cloudflare / reverse proxy (TLS terminated upstream)
# Force Laravel URLs to HTTPS, but keep internal server HTTP
docker run -d -p 8080:8080 \
-e OCTANE_SERVICE_ENABLED=true \
-e OCTANE_HTTPS=true \
-e OCTANE_START_HTTPS=false \
-e SSL_MODE=off \
dekadev/php:8.3-frankenphp-amd64
# Internal TLS mode (FrankenPHP serves HTTPS itself)
docker run -d -p 8080:8080 -p 8443:8443 \
-e OCTANE_SERVICE_ENABLED=true \
-e OCTANE_START_HTTPS=true \
-e OCTANE_HTTP_REDIRECT=true \
dekadev/php:8.3-frankenphp-amd64
Octane HTTPS Modes:
OCTANE_HTTPS=true → forces Laravel-generated URLs to https:// (application-level)OCTANE_START_HTTPS=true → maps to native SSL_MODE=mixed; add OCTANE_HTTP_REDIRECT=true for SSL_MODE=full. Explicit OCTANE_START_HTTPS=false selects SSL_MODE=off.OCTANE_HTTPS=true and keep OCTANE_START_HTTPS=falseDocker Compose with Health Check:
services:
php:
image: dekadev/php:8.3-frankenphp-amd64
ports:
- "8080:8080"
- "8443:8443"
environment:
- OCTANE_SERVICE_ENABLED=true
- OCTANE_WORKERS=4 # Optional: set worker count
- OCTANE_MAX_REQUESTS=1000 # Optional: max requests per worker
- OCTANE_LOG_LEVEL=debug # Optional: debug logging
- SSL_MODE=off # Recommended behind proxy
# Healthcheck is built-in, no need to define manually
Tuning Octane Performance:
# Production settings
OCTANE_WORKERS=4 # Number of worker processes
OCTANE_MAX_REQUESTS=1000 # Requests before worker restart
OCTANE_LOG_LEVEL=error # Minimal logging
# Development settings
OCTANE_WORKERS=1 # Single worker for debugging
OCTANE_MAX_REQUESTS=1 # Reload on every request
OCTANE_LOG_LEVEL=debug # Verbose logging
Log Formatting:
Caddy uses the upstream auto format on stderr. With Octane enabled, server logs must be JSON because Octane parses them. Laravel application logging remains controlled by the application's logging configuration.
CADDY_LOG_FORMAT=auto
LOG_CHANNEL=stderr
Both regular FrankenPHP and Octane use /etc/frankenphp/Caddyfile, including the upstream v5 Octane and trusted-proxy fragments. The old Caddyfile.octane override is removed. Use OCTANE_WORKERS for the worker count; the old FRANKENPHP_WORKER_OPTIONS="num ..." setting is no longer consumed. Octane owns the native worker directive, preventing duplicate definitions.
Note:
- FrankenPHP uses Caddy as web server with HTTP/2 and HTTP/3 support
- Port 8080 is HTTP, port 8443 is HTTPS
- Built-in healthcheck automatically monitors Octane status
- When
OCTANE_SERVICE_ENABLEDis not set totrue, default FrankenPHP/Caddy server will run- FrankenPHP entrypoint runs init scripts first (git operations, composer install, post-install commands)
- If
OCTANE_SERVICE_ENABLED=truebut Octane cannot boot, startup fails visibly; correct the application before retryingOCTANE_HTTPS(Laravel URL scheme) is separate fromOCTANE_START_HTTPS(FrankenPHP internal TLS)- For production behind proxy (Cloudflare, Nginx), set
SSL_MODE=off
OPcache remains opt-in with PHP_OPCACHE_ENABLE=1. Upstream v5 defaults to 256 MB, 32 MB interned strings, 32,531 cached files, and timestamp validation disabled when enabled.
PHP_OPCACHE_VALIDATE_TIMESTAMPS=0 and restart the container after every code/dependency change.PHP_OPCACHE_VALIDATE_TIMESTAMPS=1. Octane and queue workers still need restarting or reloading after code changes.Do not enable preload globally for applications whose code arrives after the image is built. Size OPcache and worker counts using application memory measurements.
Copy-paste this configuration for a standard Laravel deployment on Coolify.
# Application
APP_ENV=production
APP_DEBUG=false
APP_URL=https://$COOLIFY_FQDN
ASSET_URL=https://$COOLIFY_FQDN
# Git Repository
REPO_URL=https://username:[email protected]/your-org/your-repo.git
REPO_BRANCH=main
# Database & Redis
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=secret
REDIS_HOST=redis
REDIS_PASSWORD=secret
QUEUE_CONNECTION=redis
# PHP Tuning (Inherited from Base Image)
PHP_MEMORY_LIMIT=512M
PHP_MAX_EXECUTION_TIME=300
PHP_POST_MAX_SIZE=100M
PHP_UPLOAD_MAX_FILE_SIZE=100M
PHP_DATE_TIMEZONE=Asia/Jakarta
# Startup Automation (Inherited from Base Image)
AUTORUN_ENABLED=true
AUTORUN_LARAVEL_MIGRATION=true # Run 'php artisan migrate --force' on startup
AUTORUN_LARAVEL_CONFIG_CACHE=true # Run 'php artisan config:cache' on startup
AUTORUN_LARAVEL_ROUTE_CACHE=true # Run 'php artisan route:cache' on startup
AUTORUN_LARAVEL_VIEW_CACHE=true # Run 'php artisan view:cache' on startup
AUTORUN_LARAVEL_STORAGE_LINK=true # Run 'php artisan storage:link' on startup
# Service Configuration (Our Image Features)
QUEUE_SERVICE_MODE=default # Options: default (standard worker) or horizon (Laravel Horizon)
REVERB_SERVICE_ENABLED=false # Set true to enable Laravel Reverb WebSocket
OCTANE_SERVICE_ENABLED=false # Set true to enable Octane (FrankenPHP images only)
These images inherit powerful configuration options from the serversideup/php base image.
| Variable | Default | Description |
|---|---|---|
AUTORUN_ENABLED | false | Master switch to enable startup scripts |
AUTORUN_LARAVEL_MIGRATION | false | Run php artisan migrate --force |
AUTORUN_LARAVEL_STORAGE_LINK | false | Run php artisan storage:link |
PHP_MEMORY_LIMIT | 256M | PHP memory limit (e.g. 512M, 1G) |
PHP_MAX_EXECUTION_TIME | 60 | Max execution time in seconds |
PHP_POST_MAX_SIZE | 100M | Max POST size |
PHP_UPLOAD_MAX_FILE_SIZE | 100M | Max file upload size |
PHP_DATE_TIMEZONE | UTC | PHP Timezone (e.g. Asia/Jakarta) |
Real Client IP and HTTPS Behind a Proxy
Use the upstream TRUSTED_PROXY profiles consistently for Nginx and FrankenPHP:
cloudflare (default): Cloudflare plus local Docker ranges; uses CF-Connecting-IP (FrankenPHP can fall back to X-Forwarded-For).local: Your reverse proxy on a private Docker network, using X-Forwarded-For.sucuri: Sucuri plus local Docker ranges.off: Ignore client-IP forwarding headers at the web server.FrankenPHP resolves forwarded chains right to left and passes the validated client address as PHP REMOTE_ADDR. Configure Laravel's trusted proxy middleware for your actual proxy path and forwarded HTTPS headers. Do not overwrite REMOTE_ADDR directly from a client-supplied header. If Cloudflare terminates TLS, restrict the origin proxy to the intended ingress so clients cannot bypass it with forged headers.
See the versioned upstream proxy guide and Laravel trusted proxy documentation.
PHP File Access
Storage PHP files, including mixed-case extensions and PATH_INFO suffixes such as /storage/upload.php/test, must be denied. DEKA also restricts direct PHP execution outside approved front controllers. Benign storage assets remain accessible. Run the runtime integration test against the exact candidate image before deployment.
Permission Errors
# Fix file ownership
docker exec -it container_name chown -R www-data:www-data /var/www/html
Composer Installation Issues
# Check Composer authentication
docker exec -it container_name composer config --list --global
Queue Workers Not Starting
# Check queue service configuration
docker logs container_name | grep queue
# Verify queue service mode is set
docker exec -it container_name env | grep QUEUE_SERVICE_MODE
# Test queue worker manually
docker exec -it container_name php artisan queue:work --once
Git Clone Fails on Non-Empty Persistent Volume
If startup is interrupted and /var/www/html becomes partially populated without .git, the init script now auto-cleans the directory before cloning again.
For hardened deployments, enable this to clean local tracked/untracked changes before pull:
GIT_HARD_CLEAN_BEFORE_PULL=true
This runs before git pull:
git checkout .git clean -dfUse this mode when you want every restart to converge to remote branch state and discard local modifications.
Enable verbose initialization logging:
docker run -e INIT_SCRIPT=true \
-e SETUP_COMPOSER_AUTH=true \
dekadev/php:8.4-alpine
This project is licensed under the MIT License. See the LICENSE file for details.
Images are published to Docker Hub under the dekadev namespace:
For issues and questions:
Built with ❤️ for DEKA TEAM
Content type
Image
Digest
sha256:385444ed7…
Size
299.7 MB
Last updated
about 17 hours ago
docker pull dekadev/php:8.3-amd64-python