A config-driven Docker environment for full-fledged Laravel development.
Frank gives you a full Laravel dev environment from a single frank.yaml — no local PHP, Composer, or Node required. Queue workers and the scheduler run as dedicated containers with auto-reload on code change. Onboard a teammate with git clone and frank up.
Single file config
- One-file config (
frank.yaml) → generates Dockerfile, compose, Caddy/nginx
Flexible environments:
- Two runtimes: FrankenPHP (default) or PHP-FPM + Nginx
- Services: Postgres, MySQL, MariaDB, SQLite, Redis, Memcached, Meilisearch, Mailpit, and more
Workflow
frank newscaffolds a project (interactive or flag-driven)frank installbootstraps Laravel inside the container - Shell aliases (artisan,composer,php,psql, …) auto-activate oncd- Custom project aliases in
frank.yaml(container or host-side) - Shell completion for zsh / bash / fish / powershell
Automatically reloading workers
- Declared
schedule:work+queue:workpools infrank.yaml - Ad-hoc workers via
frank worker queue|schedule - Host-side file watcher (
frank watch) reloads workers on code change - Multi-pane CCTV view of every worker:
frank worker top
Dev Tools
- Preconfigured Pint, Larastan, Rector configuration with opinionated Laravel defaults
- Lefthook pre-commit hooks: auto-fix on commit, analyze before push
frank tool add <name>to install tools on existing projects -frank generatereconciles tools for new devs cloning the repo$
Git Worktrees
- Run multiple branches in parallel with isolated containers and ephemeral ports
frank worktree create/remove/list— create, tear down, or browse worktrees- Interactive TUI with keybindings to start, stop, open, or remove worktrees
AI Integration (MCP)
- Built-in MCP server — AI assistants can check status, read config, tail logs, run commands, and manage worktrees without shelling out
- Auto-generated
.mcp.json— Claude Code, Cursor, and Windsurf discover it automatically
Interop
- Import existing Laravel Sail projects (
frank import) - Hand off to Sail anytime (
frank eject) - Single static Go binary — no runtime dependencies
- Install
- Getting Started
- frank.yaml
- Supported Services
- CLI Commands
- Git worktrees
- MCP Integration
- Further Reading
You will need a working installation of Docker and optionally (but recommended) install mkcert for local HTTPS support.
brew install phlisg/tap/frankNo Go required. Updates via brew upgrade frank.
If you have Go 1.26+ installed:
go install github.com/phlisg/frank@latestTip: Don't have Go locally installed and the setup scares you? Try Proto, installs Go in one command :)
Per-OS notes
WSL (Windows) — either method works. Make sure Docker Desktop has the WSL 2 backend enabled (Settings → Resources → WSL Integration).
Tip: for better Docker volume mount performance, enable VirtioFS in Docker Desktop → Settings → General → "Use VirtioFS for file sharing".
frank new my-app
cd my-appThat's it. Frank scaffolds the project, installs Laravel, starts containers, and runs migrations. Visit http://localhost.
No local PHP, Composer, or Node required.
frank new walked you through PHP version, runtime, and services — or skip the wizard entirely:
frank new --php 8.4 --runtime frankenphp --with="pgsql,redis,mailpit" --schedule --queue-count 2 my-appFrank generated frank.yaml, Dockerfile, compose, Caddy/nginx config, .env — then built and started everything.
echo 'eval "$(frank config shell setup)"' >> ~/.zshrc # or ~/.bashrc
source ~/.zshrcNote: this line must appear after your Go bin path (
export PATH=$PATH:$HOME/go/binor equivalent) in your shell config — otherwisefrankwon't be found when the eval runs.
Now artisan, composer, php, npm and any custom alias resolve to the container automatically when you're in a Frank project:
artisan make:controller Api/PostController --resource
php vendor/bin/pint
composer require filament/filament
frank test
npm run devgit clone … && cd my-app
frank up -dfrank up regenerates frank files, starts containers, runs migrations — same environment, every machine.
frank.yaml is the single source of truth for your environment. All Docker files (compose.yaml, Dockerfile, .env, etc.) are generated from it. Commit frank.yaml to git; the generated files can be gitignored or committed alongside — your choice.
version: 1
php:
version: "8.5"
runtime: "frankenphp"
laravel:
version: "latest"
services:
- pgsql
- mailpit| Key | Values | Default | Description |
|---|---|---|---|
php.version |
8.2 8.3 8.4 8.5 |
8.5 |
PHP version |
php.runtime |
frankenphp fpm |
frankenphp |
Web server runtime |
laravel.version |
latest lts 12.* 13.* … |
latest |
Laravel version constraint passed to Composer |
services |
list — see table below | [pgsql, mailpit] |
Services to include |
config.<service>.port |
integer | service default | Override the host-side port mapping |
workers.schedule |
boolean | false |
Run php artisan schedule:work in a dedicated container |
workers.queue |
list — see docs/workers.md |
[] |
Declare long-running queue:work worker pools |
tools |
list — pint larastan rector lefthook |
[] |
Dev tools installed by frank new or frank tool add — see docs/tools.md |
server.https |
boolean | true |
Serve over HTTPS with locally-trusted mkcert certificates — see docs/https.md |
server.port |
integer | 443 (HTTPS) / 80 (HTTP) |
Custom host-side port |
aliases |
map | {} |
Custom shell aliases activated by frank config shell activate |
aliases.<name> |
string or {cmd, host} |
— | String = container command; map with host: true = host-side |
After editing frank.yaml, run frank generate to regenerate Docker files — or simply frank up, which auto-regenerates when frank.yaml is newer than compose.yaml.
| Service | Category | Default port |
|---|---|---|
pgsql |
Database | 5432 |
mysql |
Database | 3306 |
mariadb |
Database | 3306 |
sqlite |
Database | — (file-based) |
redis |
Cache / Queue | 6379 |
meilisearch |
Search | 7700 |
memcached |
Cache | 11211 |
mailpit |
Mail (local SMTP + UI) | 1025 / 8025 |
Only one database can be active at a time. Frank enforces this — frank add mysql will refuse if pgsql is already configured. Use frank remove pgsql first.
Ports are customizable in frank.yaml via config.<service>.port:
config:
pgsql:
port: 5433
redis:
port: 6380| Command | Description |
|---|---|
frank new <project> |
Create a new Laravel project — zero to localhost in one command. Non-interactive by default; use --interactive for wizard. Flags: --php, --laravel, --runtime, --with, --schedule, --queue-count, --http, --no-pint, --no-larastan, --no-rector, --no-lefthook, --no-tools, --no-up, --sail |
frank setup |
Configure Frank in an existing Laravel project (interactive wizard). Supports --sail and --dir |
frank tool add <tool> |
Add a dev tool to frank.yaml and install its config files |
frank generate |
Regenerate Docker files from frank.yaml without prompting |
frank install |
Install Laravel into the project directory |
frank add <service> |
Add a service to frank.yaml and regenerate |
frank remove <service> |
Remove a service from frank.yaml and regenerate |
frank up [-d] [--quick] [-- <compose args>] |
Start containers. Frank owns -d/--detach and --quick; all other docker compose flags must come after a literal -- (e.g. frank up -- --build). Auto-spawns the watcher when workers are declared |
frank down |
Stop containers and the watcher. Use frank down -- -v to also remove volumes |
frank test [-- <artisan/pest flags>] |
Run tests inside the app container (php artisan test). Pest parallel works out of the box — see docs/testing.md |
frank exec <cmd> [args...] |
Run a command inside the app container as sail (e.g. frank exec bash, frank exec php vendor/bin/pint) |
frank compose [--] <args> |
Pass-through to docker compose (e.g. frank compose ps, frank compose logs) |
frank dev [restart|stop|start] |
Attach to the frontend dev server (Vite) running as a compose sidecar; no args tails its logs (Ctrl-C detaches). Disable per-project with dev.enabled: false |
frank worker queue [--count N] [--queue …] [--tries …] [-- <artisan flags>] |
Spawn ad-hoc queue:work workers |
frank worker schedule |
Spawn an ad-hoc schedule:work container |
frank worker ps |
Show declared + ad-hoc worker containers |
frank worker stop [--all] |
Stop ad-hoc workers; --all also stops declared ones |
frank worker logs [name] [-f] |
Tail logs for one or all workers |
frank worker top [--live] [--min-pane-width N] |
Live multi-pane CCTV view of every worker; --live reconciles ad-hoc churn |
frank worktree create <branch> |
Create a new git worktree as a sibling directory — see docs/worktrees.md |
frank worktree remove <path> |
Tear down containers, remove worktree and branch |
frank worktree list |
Interactive TUI for browsing worktrees — see docs/worktrees.md |
frank watch [--status|--stop] |
Run the code-reload watcher in the foreground, or inspect/stop the detached one |
frank config show |
Show resolved configuration — see docs/config.md |
frank config edit |
Open frank.yaml in your editor — see docs/config.md |
frank config set <key> <value> |
Set a config value (e.g. frank config set php.version 8.4) — see docs/config.md |
frank config shell ... |
Shell integration (aliases, hooks, completion) — see docs/shell.md |
frank import [-f path] |
Import from a Sail docker-compose.yml |
frank eject |
Install Laravel Sail into the running containers and hand off to Sail |
frank version [--check|--update] |
Print version and check for updates. --check shows update status; --update self-updates via Homebrew or go install |
Frank detects git worktrees automatically. Each worktree gets isolated containers with ephemeral ports — no conflicts with your main project or other worktrees.
frank worktree create feature/auth # creates ../myapp-feature-auth
cd ../myapp-feature-auth
frank up # containers start with random portsfrank worktree list opens an interactive TUI to browse, start, stop, or remove worktrees. See docs/worktrees.md for details.
Frank ships a built-in MCP server that AI coding assistants can use to interact with your containers directly — no need to shell out to docker compose.
frank generate # creates .mcp.json — IDEs discover it automaticallyAvailable tools: frank_status, frank_config, frank_logs, frank_exec, frank_worktrees. See docs/mcp.md for setup and usage.
- HTTPS (local TLS) —
docs/https.md - Testing —
docs/testing.md - Dev tools —
docs/tools.md - Workers & code reload —
docs/workers.md - Project and PHP tools —
docs/tools.md - PHP runtimes —
docs/runtimes.md - Shell aliases —
docs/shell.md - Worktrees —
docs/worktrees.md - MCP integration —
docs/mcp.md - Sail interop —
docs/sail-interop.md - Contributing —
docs/contributing.md
