# 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 ```bash git clone cd franken ``` ### 2. Configure Environment ```bash cp .env.example .env ``` Edit the `.env` file with your preferred settings: ```bash # 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 ```bash 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 1. Update `.env`: ``` FRAMEWORK=laravel ``` 2. Uncomment Laravel volume in `docker-compose.yml`: ```yaml volumes: - ./www:/app # Instead of /app/public ``` 3. Start services: ```bash docker-compose up -d --build ``` #### Symfony 1. Update `.env`: ``` FRAMEWORK=symfony ``` 2. Uncomment Symfony volume in `docker-compose.yml`: ```yaml volumes: - ./www:/app # Instead of /app/public ``` 3. Build and start: ```bash docker-compose up -d --build ``` #### WordPress 1. Update `.env`: ``` FRAMEWORK=wordpress ``` 2. The default volume mount `/app/public` works for WordPress 3. Start services: ```bash docker-compose up -d --build ``` #### OpenCart 1. Update `.env`: ``` FRAMEWORK=opencart ``` 2. Default configuration uses `/app/public` 3. Start services: ```bash docker-compose up -d --build ``` #### Magento 2 1. Update `.env`: ``` FRAMEWORK=magento2 ``` 2. Start services: ```bash 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 1. Update `.env`: ``` MODE=prod SERVER_NAME=yourdomain.com PHP_VERSION=8.4 ``` 2. Enable HTTPS in `docker-compose.yml`: ```yaml ports: - "443:443" # HTTPS - "443:443/udp" # HTTP/3 ``` 3. Start services: ```bash docker-compose up -d --build ``` 4. FrankenPHP automatically obtains SSL certificates from Let's Encrypt ## How to Run ### Start Services ```bash # Start in background docker-compose up -d # Start with output docker-compose up # Start and rebuild images docker-compose up -d --build ``` ### Stop Services ```bash docker-compose down ``` ### View Logs ```bash # View all logs docker-compose logs -f # View specific service logs docker-compose logs -f php docker-compose logs -f db ``` ### Access Container Shell ```bash # PHP container docker-compose exec php sh # Database container docker-compose exec db mysql -u app -p ``` ## Development Workflow ### Running Commands Inside Container ```bash # 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: ```ini ; Example configurations display_errors = On memory_limit = 512M upload_max_filesize = 100M post_max_size = 100M ``` Changes take effect after container restart: ```bash 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_data` volume - **Health Check**: Built-in health check to verify Redis is running - **Networking**: Connected to `app-network` for internal PHP communication ### Redis Service Configuration Redis is automatically started with `docker-compose up`. To verify it's running: ```bash # 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`: ```env CACHE_DRIVER=redis SESSION_DRIVER=redis REDIS_HOST=redis REDIS_PORT=6379 REDIS_PASSWORD=null ``` Install/configure in your Laravel app: ```bash # Inside container docker-compose exec php composer require predis/predis # Or use PHP's native Redis extension (recommended) ``` #### PHP Native Example ```php 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`: ```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: ```php define('WP_REDIS_HOST', 'redis'); define('WP_REDIS_PORT', 6379); ``` ### Common Redis Operations ```bash # 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_data` volume - If you need to reset Redis data: `docker volume rm gold_redis_data` - Configure persistence in `docker-compose.yml` if needed: ```yaml redis: command: redis-server --appendonly yes # Enable AOF persistence ``` ### Troubleshooting Redis ```bash # 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 ```yaml 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 caching - `sodium`: Modern cryptography **WordPress**: - Additional compatibility features ## Troubleshooting ### Port Already in Use ```bash # Change PORT in .env and restart PORT=8080 docker-compose up -d ``` ### Database Connection Issues Verify credentials in `.env`: ```bash 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 ```bash # Fix permissions in www directory chmod -R 755 www/ ``` ### Rebuild Everything ```bash docker-compose down -v docker-compose up --build -d ``` ### Check Container Status ```bash docker-compose ps docker-compose logs -f ``` ## Useful Commands ```bash # 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 # Copy files from container docker cp :/path/to/file ./local/path # View resource usage docker stats ``` ## Performance Optimization ### For Production 1. Set `MODE=prod` in `.env` 2. Enable opcache in `php.ini`: ```ini opcache.enable=1 opcache.memory_consumption=256 ``` 3. Configure Redis for caching: ```bash docker-compose exec php redis-cli ``` ### Database Optimization 1. Set appropriate `MYSQL_ROOT_PASSWORD` 2. Monitor with: ```bash 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_PASSWORD` and `MYSQL_PASSWORD_DATABASE` - ✅ Set `MODE=prod` for production deployments - ✅ Keep `php.ini` locked to read-only (default) - ✅ Use environment variables for sensitive data - ✅ Keep Docker images updated ## Contributing 1. Fork the repository 2. Create a feature branch (`git checkout -b feature/improvement`) 3. Make your changes 4. Test thoroughly 5. Commit with clear messages (`git commit -am 'Add improvement'`) 6. Push to your fork (`git push origin feature/improvement`) 7. 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