15 KiB
FrankenPHP Docker Environment
A modern, lightweight Docker environment for PHP applications using FrankenPHP, featuring HTTP/2, HTTP/3, automatic HTTPS, and built-in Node.js support for multiple PHP frameworks.
Features
- 🚀 FrankenPHP Server: Modern PHP server with HTTP/2, HTTP/3, and automatic HTTPS via Caddy
- 🗄️ MariaDB Database: Persistent relational database for your applications
- 📦 Node.js Integration: Full Node.js runtime (v20.x) for frontend tooling and build processes
- 🎯 Multi-Framework Support: Optimized build targets for Laravel, Symfony, Magento2, WordPress, and OpenCart
- 🧰 CLI Tools Included: Symfony CLI is available for Symfony projects and WP-CLI is included for WordPress workflows
- 🗄️ Redis Support: A dedicated Redis container is included with health checks and PHP connectivity for supported stacks
- 📝 PHP 8.0 - 8.5: Support for multiple PHP versions with configurable extensions
- 🔧 Flexible Configuration: Local, proxy, and production modes
- 🔒 Automatic HTTPS: Let's Encrypt SSL certificates in production
- 🎨 Extension-Rich: Pre-installed essential PHP extensions (PDO/MySQL, Redis, APCu, and more)
- ⚡ Alpine-Based: Lightweight, fast, and efficient container images
- 🔄 Hot Reload: Automatic code reloading during development
Recently Added
- Symfony CLI pre-installed for Symfony-based applications
- WP-CLI pre-installed for WordPress administration and site management
- Redis container support with health checks and framework-ready PHP integration
Requirements
- Docker Desktop (or Docker Engine + Docker Compose)
- 2GB+ RAM available
- Terminal/Command-line access
Quick Start
1. Clone the Repository
git clone <repository-url>
cd franken
2. Configure Environment
cp .env.example .env
Edit the .env file with your preferred settings:
# Core Configuration
PHP_VERSION=8.4 # PHP version (8.0-8.5)
FRAMEWORK=opencart # Framework: laravel, symfony, magento2, wordpress, opencart
MODE=proxy # Mode: local, prod, proxy
PORT=80 # Local port
PROJECT_NAME=opencart # Project name
# Database Configuration
MYSQL_ROOT_PASSWORD=root
MYSQL_DATABASE=database
MYSQL_USER_DATABASE=app
MYSQL_PASSWORD_DATABASE=secret
3. Start Services
docker-compose up -d
4. Access Your Application
- Local/Proxy Mode: http://localhost:{PORT}
- Default: http://localhost:80
Configuration Guide
Environment Variables
| Variable | Description | Options |
|---|---|---|
PHP_VERSION |
PHP version | 8.0, 8.1, 8.2, 8.3, 8.4, 8.5 |
FRAMEWORK |
Target framework | laravel, symfony, magento2, wordpress, opencart |
MODE |
Run mode | local (dev), proxy (behind reverse proxy), prod (production) |
PORT |
HTTP port | Any available port (default: 80) |
PROJECT_NAME |
Project identifier | Any alphanumeric name |
MYSQL_ROOT_PASSWORD |
Database root password | Your secure password |
MYSQL_DATABASE |
Database name | database |
MYSQL_USER_DATABASE |
Database user | app |
MYSQL_PASSWORD_DATABASE |
Database user password | Your secure password |
SERVER_NAME |
Domain (prod mode) | Your domain (e.g., example.com) |
Framework-Specific Setup
Laravel
-
Update
.env:FRAMEWORK=laravel -
Uncomment Laravel volume in
docker-compose.yml:volumes: - ./www:/app # Instead of /app/public -
Start services:
docker-compose up -d --build
Symfony
-
Update
.env:FRAMEWORK=symfony -
Uncomment Symfony volume in
docker-compose.yml:volumes: - ./www:/app # Instead of /app/public -
Build and start:
docker-compose up -d --build
WordPress
-
Update
.env:FRAMEWORK=wordpress -
The default volume mount
/app/publicworks for WordPress -
Start services:
docker-compose up -d --build
OpenCart
-
Update
.env:FRAMEWORK=opencart -
Default configuration uses
/app/public -
Start services:
docker-compose up -d --build
Magento 2
-
Update
.env:FRAMEWORK=magento2 -
Start services:
docker-compose up -d --build
Modes Explained
- local: Development mode with internal TLS disabled, suitable for local development
- proxy: Designed for use behind a reverse proxy or load balancer
- prod: Production mode with HTTPS enabled, automatic SSL certificates, and optimized PHP settings
Production Deployment
-
Update
.env:MODE=prod SERVER_NAME=yourdomain.com PHP_VERSION=8.4 -
Enable HTTPS in
docker-compose.yml:ports: - "443:443" # HTTPS - "443:443/udp" # HTTP/3 -
Start services:
docker-compose up -d --build -
FrankenPHP automatically obtains SSL certificates from Let's Encrypt
How to Run
Start Services
# Start in background
docker-compose up -d
# Start with output
docker-compose up
# Start and rebuild images
docker-compose up -d --build
Stop Services
docker-compose down
View Logs
# View all logs
docker-compose logs -f
# View specific service logs
docker-compose logs -f php
docker-compose logs -f db
Access Container Shell
# PHP container
docker-compose exec php sh
# Database container
docker-compose exec db mysql -u app -p
Development Workflow
Running Commands Inside Container
# Composer commands
docker-compose exec php composer install
docker-compose exec php composer require package/name
# NPM/Node commands
docker-compose exec php npm install
docker-compose exec php npm run dev
docker-compose exec php npm run build
# PHP CLI
docker-compose exec php php -v
docker-compose exec php php artisan migrate # Laravel
docker-compose exec php bin/console doctrine:schema # Symfony
# Database management
docker-compose exec db mysql -u app -p database
File Structure
franken/
├── docker-compose.yml # Docker services configuration
├── Dockerfile # Multi-stage PHP container build
├── .env # Environment configuration
├── php.ini # Custom PHP settings
├── www/ # Web application root
│ └── index.php # Entry point
└── README.md # This file
PHP Configuration
Edit php.ini to customize PHP settings:
; Example configurations
display_errors = On
memory_limit = 512M
upload_max_filesize = 100M
post_max_size = 100M
Changes take effect after container restart:
docker-compose restart php
Redis Caching
Setup
Redis is included and pre-configured in docker-compose.yml. It runs in a separate container with:
- Image:
redis:alpine(lightweight, fast) - Persistent Storage: Redis data is stored in
redis_datavolume - Health Check: Built-in health check to verify Redis is running
- Networking: Connected to
app-networkfor internal PHP communication
Redis Service Configuration
Redis is automatically started with docker-compose up. To verify it's running:
# Check Redis container status
docker-compose ps redis
# Connect to Redis CLI
docker-compose exec redis redis-cli
# Test Redis connection from PHP container
docker-compose exec php php -r "
\$redis = new Redis();
\$redis->connect('redis', 6379);
echo 'Redis connected: ' . (\$redis->ping() ? 'OK' : 'FAILED');
\$redis->close();
"
Using Redis in Your Application
Laravel Example
Update your .env:
CACHE_DRIVER=redis
SESSION_DRIVER=redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=null
Install/configure in your Laravel app:
# Inside container
docker-compose exec php composer require predis/predis
# Or use PHP's native Redis extension (recommended)
PHP Native Example
<?php
$redis = new Redis();
$redis->connect('redis', 6379);
// Set a cache value
$redis->set('user:1:name', 'John Doe', 3600); // 1 hour TTL
// Get a value
$name = $redis->get('user:1:name');
// Check if key exists
if ($redis->exists('user:1:name')) {
echo "Key exists";
}
// Delete a key
$redis->del('user:1:name');
// Increment counter
$redis->incr('page_views');
$redis->close();
?>
Symfony Example
Configure in config/packages/cache.yaml:
framework:
cache:
default_redis_provider: 'redis://redis:6379'
pools:
cache.app:
adapter: cache.adapter.redis
provider: 'redis://redis:6379'
WordPress Example
Use a Redis plugin like "Redis Object Cache" and configure:
define('WP_REDIS_HOST', 'redis');
define('WP_REDIS_PORT', 6379);
Common Redis Operations
# Connect to Redis CLI
docker-compose exec redis redis-cli
# Inside redis-cli commands:
PING # Check connection
SET mykey "Hello" # Set a key
GET mykey # Get a value
DEL mykey # Delete a key
KEYS * # List all keys
FLUSHDB # Clear all keys
INFO # Show Redis info
MONITOR # Monitor commands in real-time
Redis Persistence
- Data persists in the
redis_datavolume - If you need to reset Redis data:
docker volume rm gold_redis_data - Configure persistence in
docker-compose.ymlif needed:
redis:
command: redis-server --appendonly yes # Enable AOF persistence
Troubleshooting Redis
# Check if Redis is running
docker-compose logs redis
# Restart Redis
docker-compose restart redis
# Clear Redis cache
docker-compose exec redis redis-cli FLUSHDB
# Monitor Redis activity
docker-compose exec redis redis-cli MONITOR
Docker Architecture
Multi-Stage Build
The Dockerfile uses multi-stage builds to optimize for different frameworks:
- base: Foundation with core PHP extensions
- node: Adds Node.js runtime
- laravel: Laravel-specific extensions (Redis, APCu)
- symfony: Symfony-specific extensions (APCu, Sodium)
- wordpress: WordPress-specific configuration
- opencart: OpenCart-specific configuration
- magento2: Magento 2-specific configuration
Services
PHP Service (FrankenPHP)
- Image:
dunglas/frankenphp:php{PHP_VERSION}-alpine - Includes:
- PHP 8.0-8.5
- Node.js v20.x
- Caddy web server
- Framework-specific extensions
Database Service (MariaDB)
- Image:
mariadb:latest - Volume:
db_data(persistent) - Port:
3306(internal)
Redis Service (Cache)
- Image:
redis:alpine(lightweight) - Volume:
redis_data(persistent) - Port:
6379(internal) - Health Check: Built-in Redis ping monitoring
- Available in all frameworks: Laravel, Symfony, WordPress, OpenCart, Magento2
Network & Volumes
networks:
app-network: # Internal communication
driver: bridge
volumes:
caddy_data: # Caddy cache and data
caddy_config: # Caddy configuration
db_data: # Database persistence
PHP Extensions
Core Extensions
- Database:
pdo_mysql,mysqli - String:
mbstring,ctype,iconv - Data:
xml,dom,simplexml,fileinfo - Security:
openssl,tokenizer - Utilities:
curl,zip,intl,opcache
Framework-Specific Extensions
All Frameworks (Laravel, Symfony, WordPress, OpenCart, Magento2):
redis: Redis cache and session support
Laravel/Magento2:
- Additional caching optimizations
Symfony:
apcu: APCu cachingsodium: Modern cryptography
WordPress:
- Additional compatibility features
Troubleshooting
Port Already in Use
# Change PORT in .env and restart
PORT=8080 docker-compose up -d
Database Connection Issues
Verify credentials in .env:
docker-compose exec php php -r "
\$db = new mysqli('db', 'app', 'secret', 'database');
if (\$db->connect_error) exit('Connection failed: ' . \$db->connect_error);
echo 'Connected successfully!';
"
Permission Issues
# Fix permissions in www directory
chmod -R 755 www/
Rebuild Everything
docker-compose down -v
docker-compose up --build -d
Check Container Status
docker-compose ps
docker-compose logs -f
Useful Commands
# List running containers
docker-compose ps
# Restart a service
docker-compose restart php
# Rebuild images
docker-compose build --no-cache
# Execute command in container
docker-compose exec php <command>
# Copy files from container
docker cp <container>:/path/to/file ./local/path
# View resource usage
docker stats
Performance Optimization
For Production
- Set
MODE=prodin.env - Enable opcache in
php.ini:opcache.enable=1 opcache.memory_consumption=256 - Configure Redis for caching:
docker-compose exec php redis-cli
Database Optimization
- Set appropriate
MYSQL_ROOT_PASSWORD - Monitor with:
docker-compose exec db mysql -u root -p -e "SHOW PROCESSLIST;"
Security Best Practices
- ✅ Change default database credentials in
.env - ✅ Use strong passwords for
MYSQL_ROOT_PASSWORDandMYSQL_PASSWORD_DATABASE - ✅ Set
MODE=prodfor production deployments - ✅ Keep
php.inilocked to read-only (default) - ✅ Use environment variables for sensitive data
- ✅ Keep Docker images updated
Contributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/improvement) - Make your changes
- Test thoroughly
- Commit with clear messages (
git commit -am 'Add improvement') - Push to your fork (
git push origin feature/improvement) - Open a Pull Request
License
[Specify your license here]
Support & Resources
- FrankenPHP Docs: https://frankenphp.dev/
- Caddy Docs: https://caddyserver.com/docs/
- Docker Docs: https://docs.docker.com/
- Compose Docs: https://docs.docker.com/compose/
Changelog
v1.1.0 (Current)
- Multi-stage Docker build for framework-specific optimization
- Node.js v20.x integration
- Support for 5+ PHP frameworks with dedicated configurations
- Improved documentation and examples
- Production mode with automatic HTTPS
v1.0.0
- Initial release with FrankenPHP and MariaDB
- Basic framework support
- Development and production modes
Last Updated: March 2026
Maintainer: Development Team