asamoahboateng/frankenphp-deploy

FrankenPHP + Traefik Docker deployment scaffolding for Laravel Octane projects.
39
Install
composer require asamoahboateng/frankenphp-deploy
Latest Version:v1.3.1
PHP:^8.2
Maintainer: asamoahboateng

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 files
  • pha CLI 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:

  1. Composer install - installs/updates PHP dependencies (composer install --optimize-autoloader)
  2. Database migrations - runs php artisan migrate --force
  3. 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:

  1. Set your API key in frankenphp_server/.env:

    TYPESENSE_API_KEY=your-secure-api-key
    
  2. Add to your Laravel .env:

    SCOUT_DRIVER=typesense
    TYPESENSE_HOST=typesense
    TYPESENSE_PORT=8108
    TYPESENSE_PROTOCOL=http
    TYPESENSE_API_KEY=your-secure-api-key
    
  3. 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