asamoahboateng/frankenphp-deploy
| Install | |
|---|---|
composer require asamoahboateng/frankenphp-deploy |
|
| Latest Version: | v1.3.1 |
| PHP: | ^8.2 |
| License: | MIT |
| Last Updated: | Mar 11, 2026 |
| Links: | GitHub · Packagist |
FrankenPHP Deploy
FrankenPHP + Traefik Docker deployment scaffolding for Laravel Octane projects.
Requirements
- PHP 8.2+
- Laravel 11.x or 12.x
- Laravel Octane
- Docker & Docker Compose
Installation
composer require asamoahboateng/frankenphp-deploy --dev
Quick Start
# Install the scaffolding (interactive prompts for domain and project name)
php artisan frankenphp:install
# Or with options
php artisan frankenphp:install --domain=myapp.test --project=myapp
# With pgvector support (vector similarity search)
php artisan frankenphp:install --domain=myapp.test --project=myapp --pgvector
This will create:
frankenphp_server/directory with Docker configuration filesphaCLI script in your project root
SSL Certificates
Local Development
cd frankenphp_server
./setup-ssl.sh
This uses mkcert to generate trusted local SSL certificates into certs/local/.
Production / Multi-Domain
When using Traefik, each project manages its own SSL certificate. Place your cert files in the project's frankenphp_server/certs/ directory:
frankenphp_server/certs/
├── local/ ← local dev certs (generated by setup-ssl.sh)
│ ├── cert.pem
│ └── key.pem
└── prod/ ← production certs (you provide these)
├── cert.pem
└── key.pem
Set APP_ENV=local or APP_ENV=production in your .env to control which certs are loaded.
When ./pha start runs, a cert_loader service copies each project's certs into shared Docker volumes (traefik_certs, traefik_dynamic). Traefik watches these volumes and automatically picks up certificates for all projects — so each domain gets its own SSL without any Traefik restart.
Starting Your Application
# Start with Traefik reverse proxy (recommended for multiple projects)
./pha start
# Or start standalone (FrankenPHP handles SSL directly)
./pha standalone
Pha CLI Commands
Lifecycle
| Command | Description |
|---|---|
./pha start |
Start with Traefik reverse proxy |
./pha standalone |
Start without Traefik (FrankenPHP handles SSL) |
./pha stop |
Stop all containers |
./pha reboot |
Restart all services |
./pha reload |
Reload Octane workers (zero downtime) |
./pha fresh |
Wipe and rebuild (local only) |
./pha delete |
Remove all containers, images, volumes |
Scaling
| Command | Description |
|---|---|
./pha scale [n] |
Scale to n replicas (e.g., ./pha scale 4) |
Development
| Command | Description |
|---|---|
./pha art [cmd] |
Run artisan command |
./pha composer |
Run composer command |
./pha npm |
Run npm command |
./pha tinker |
Open Laravel tinker |
./pha ssh |
Shell into container |
Monitoring
| Command | Description |
|---|---|
./pha ls |
List running containers |
./pha logs |
Tail FrankenPHP logs |
./pha status |
Show detailed status |
Configuration
After installation, edit frankenphp_server/.env to customize:
APP_URL=https://myapp.test/
APP_DOMAIN=myapp.test
APP_PORT=8000
APP_ENV=local
COMPOSE_PROJECT_NAME=myapp
TRAEFIK_HOST=traefik.myapp.test
ADMINER_DOMAIN=adminer.myapp.test
Architecture
Standalone vs Traefik
Standalone (./pha standalone) |
Traefik (./pha start) |
|
|---|---|---|
| SSL | FrankenPHP/Caddy handles SSL directly | Traefik terminates SSL, proxies to FrankenPHP |
| Ports | Exposes 80, 443, 443/udp (HTTP/3) | No ports exposed — Traefik routes traffic |
| Multi-project | One app per server | Multiple apps on the same server |
| Scaling | Single container | Horizontal scaling via replicas |
| Cert management | Caddy auto-manages or uses local certs | cert_loader copies certs to shared Traefik volumes |
| Adminer | Exposed on port 8080 | Routed via adminer.yourdomain.com |
Use standalone for a single app on a server (simpler, fewer moving parts). Use Traefik when you need multiple apps sharing ports 80/443 or horizontal scaling.
With Traefik (Multi-Project Setup)
┌─────────────┐
│ Traefik │ ← SSL termination, routing
│ (port 443) │
└──────┬──────┘
│
┌──────┴──────┐
│ FrankenPHP │ ← Laravel Octane
└──────┬──────┘
│
┌──────┴──────┐
│ Init │ ← composer install, migrations
└──────┬──────┘
│
┌──────┴──────┐
│ Services │ ← PostgreSQL, Redis, Queue Worker, DB Backup
└─────────────┘
Standalone (Single Project)
┌─────────────┐
│ FrankenPHP │ ← SSL via Caddy + Laravel Octane
│ (port 443) │
└──────┬──────┘
│
┌──────┴──────┐
│ Init │ ← composer install, migrations
└──────┬──────┘
│
┌──────┴──────┐
│ Services │ ← PostgreSQL, Redis, Queue Worker, DB Backup
└─────────────┘
Services Included
- Init - Runs before the app starts to install Composer dependencies and execute database migrations
- FrankenPHP - High-performance PHP application server with Caddy
- PostgreSQL 16 - Database (optionally with pgvector for vector similarity search)
- Redis - Cache and session storage
- Queue Worker - Laravel queue processing
- DB Backup - Weekly PostgreSQL backup with automatic old backup cleanup
- Adminer - Database management UI
- Typesense - Search engine (optional)
Init Service
The init service is a one-shot container that runs before the main FrankenPHP application starts. It ensures your environment is ready by performing:
- Composer install - installs/updates PHP dependencies (
composer install --optimize-autoloader) - Database migrations - runs
php artisan migrate --force - Storage link - creates the public storage symlink
The startup order is: db/redis → init → frankenphp → worker
The init container uses the same Docker image as the app, exits after completing its tasks, and will not restart. If it fails (e.g., a migration error), the main app will not start — allowing you to fix the issue before the app serves traffic.
To customize what the init service runs, edit frankenphp_server/init.sh.
Database Backups
The db_backup service runs alongside your app and performs a weekly pg_dump of the PostgreSQL database. Each time it runs, it deletes the previous backup and creates a fresh compressed dump.
Backups are stored in frankenphp_server/db-backups/ on the host:
frankenphp_server/db-backups/
└── backup_20260222_030000.sql.gz
To restore from a backup:
gunzip -c frankenphp_server/db-backups/backup_*.sql.gz | docker exec -i <db_container> psql -U laravelUser -d laravel
pgvector (Vector Search)
To enable vector similarity search, use the --pgvector flag during installation:
php artisan frankenphp:install --pgvector
This switches the database image from postgres:16-alpine to pgvector/pgvector:pg16. After starting the containers, enable the extension in your database:
CREATE EXTENSION vector;
This is useful for AI/ML applications that need to store and query vector embeddings (e.g., with Laravel Scout or custom similarity search).
Optional Services
Typesense (Search Engine)
Typesense is included by default but is optional. It's useful for Laravel Scout integration.
To use Typesense:
-
Set your API key in
frankenphp_server/.env:TYPESENSE_API_KEY=your-secure-api-key -
Add to your Laravel
.env:SCOUT_DRIVER=typesense TYPESENSE_HOST=typesense TYPESENSE_PORT=8108 TYPESENSE_PROTOCOL=http TYPESENSE_API_KEY=your-secure-api-key -
Install Laravel Scout with Typesense:
composer require laravel/scout typesense/typesense-php
To remove Typesense:
If you don't need search functionality, comment out or delete the Typesense service in your docker-compose files:
In frankenphp_server/docker-compose-traefik.yml and docker-compose-standalone.yml:
# Comment out or delete this entire block:
# typesense:
# image: typesense/typesense:27.1
# container_name: myapp_typesense_franken
# ...
Also remove the Typesense environment variables from the frankenphp service:
# Remove these lines:
# - TYPESENSE_HOST=typesense
# - TYPESENSE_PORT=8108
# - TYPESENSE_PROTOCOL=http
# - TYPESENSE_API_KEY=${TYPESENSE_API_KEY:-xyz123}
Publishing Config
To customize the package configuration:
php artisan vendor:publish --tag=frankenphp-config
This publishes config/frankenphp.php where you can set default values for Traefik network names and other settings.
Force Overwrite
To reinstall and overwrite existing files:
php artisan frankenphp:install --force
License
MIT