https://github.com/phlisg/frank
A (almost) dependency-free modern Laravel install with FrankenPHP!
https://github.com/phlisg/frank
cli docker fpm frankenphp laravel php8 sail
Last synced: about 1 month ago
JSON representation
A (almost) dependency-free modern Laravel install with FrankenPHP!
- Host: GitHub
- URL: https://github.com/phlisg/frank
- Owner: phlisg
- Created: 2025-09-08T16:47:06.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2026-04-27T18:07:18.000Z (about 1 month ago)
- Last Synced: 2026-04-27T19:29:06.812Z (about 1 month ago)
- Topics: cli, docker, fpm, frankenphp, laravel, php8, sail
- Language: Go
- Homepage:
- Size: 379 KB
- Stars: 2
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: docs/contributing.md
- Agents: AGENTS.md
Awesome Lists containing this project
README

Frank
> 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`.
### Features
**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 new` scaffolds a project (interactive or flag-driven)
- `frank install` bootstraps Laravel inside the container - Shell aliases (`artisan`, `composer`, `php`, `psql`, …) auto-activate on `cd`
- **Custom project aliases** in `frank.yaml` (container or host-side)
- Shell completion for zsh / bash / fish / powershell
**Automatically reloading workers**
- Declared `schedule:work` + `queue:work` pools in `frank.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 ` to install tools on existing projects - `frank generate` reconciles tools for new devs cloning the repo$
**Interop**
- Import existing Laravel Sail projects (`frank import`)
- Hand off to Sail anytime (`frank eject`)
- Single static Go binary — no runtime dependencies
---
## Contents
- [Install](#install)
- [Getting Started](#getting-started)
- [frank.yaml](#frankyaml)
- [Supported Services](#supported-services)
- [CLI Commands](#cli-commands)
- [Further Reading](#further-reading)
---
## Install
### Homebrew (macOS & Linux)
```bash
brew install phlisg/tap/frank
```
No Go required. Updates via `brew upgrade frank`.
### Go install
If you have Go 1.26+ installed:
```bash
go install github.com/phlisg/frank@latest
```
> **Tip**: Don't have Go locally installed and the setup scares you? Try [Proto](https://moonrepo.dev/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".
---
## Getting Started
```bash
frank new my-app
cd my-app
```
That's it. Frank scaffolds the project, installs Laravel, starts containers, and runs migrations. Visit [http://localhost](http://localhost).
No local PHP, Composer, or Node required.
### What just happened?
`frank new` walked you through PHP version, runtime, and services — or skip the wizard entirely:
```bash
frank new --php 8.4 --runtime frankenphp --with="pgsql,redis,mailpit" --schedule --queue-count 2 my-app
```
Frank generated `frank.yaml`, Dockerfile, compose, Caddy/nginx config, `.env` — then built and started everything.
### Shell aliases
```bash
echo 'eval "$(frank config shell setup)"' >> ~/.zshrc # or ~/.bashrc
source ~/.zshrc
```
> **Note:** this line must appear **after** your Go bin path (`export PATH=$PATH:$HOME/go/bin` or equivalent) in your shell config — otherwise `frank` won'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:
```bash
artisan make:controller Api/PostController --resource
php vendor/bin/pint
composer require filament/filament
frank test
npm run dev
```
### Onboard a teammate
```bash
git clone … && cd my-app
frank generate
frank up -d
```
`frank generate` rebuilds the Docker files from `frank.yaml`. Then `frank up` starts containers, runs migrations — same environment, every machine.
---
## frank.yaml
`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.
```yaml
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..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`](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`](docs/tools.md) |
| `server.https` | boolean | `true` | Serve over HTTPS with locally-trusted mkcert certificates — see [`docs/https.md`](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.` | 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`.
---
## Supported Services
| 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..port`:
```yaml
config:
pgsql:
port: 5433
redis:
port: 6380
```
---
## CLI Commands
| Command | Description |
| ------- | ----------- |
| `frank new ` | 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 ` | 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 ` | Add a service to `frank.yaml` and regenerate |
| `frank remove ` | Remove a service from `frank.yaml` and regenerate |
| `frank up [-d] [--quick] [-- ]` | 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 [-- ]` | Run tests inside the app container (`php artisan test`). Pest parallel works out of the box — see [`docs/testing.md`](docs/testing.md) |
| `frank exec [args...]` | Run a command inside the app container as sail (e.g. `frank exec bash`, `frank exec php vendor/bin/pint`) |
| `frank compose [--] ` | Pass-through to `docker compose` (e.g. `frank compose ps`, `frank compose logs`) |
| `frank worker queue [--count N] [--queue …] [--tries …] [-- ]` | 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 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`](docs/config.md) |
| `frank config edit` | Open frank.yaml in your editor — see [`docs/config.md`](docs/config.md) |
| `frank config set ` | Set a config value (e.g. `frank config set php.version 8.4`) — see [`docs/config.md`](docs/config.md) |
| `frank config shell ...` | Shell integration (aliases, hooks, completion) — see [`docs/shell.md`](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` |
---
## Further Reading
- HTTPS (local TLS) — [`docs/https.md`](docs/https.md)
- Testing — [`docs/testing.md`](docs/testing.md)
- Dev tools — [`docs/tools.md`](docs/tools.md)
- Workers & code reload — [`docs/workers.md`](docs/workers.md)
- Project and PHP tools — [`docs/tools.md`](docs/tools.md)
- PHP runtimes — [`docs/runtimes.md`](docs/runtimes.md)
- Shell aliases — [`docs/shell.md`](docs/shell.md)
- Sail interop — [`docs/sail-interop.md`](docs/sail-interop.md)
- Contributing — [`docs/contributing.md`](docs/contributing.md)