Skip to content

Per-project butler.yml

Drop a butler.yml file at the root of any linked or parked project to override Butler’s defaults for that project alone. Every field is optional — a site without the file inherits your global config and Butler’s auto-detection. A valet.yml file is accepted as a fallback name for projects coming from Valet.

Generate a starter file with butler init in the project directory.

name: my-app # hostname label → my-app.test (see below)
php: "8.3" # runtime version
webserver: frankenphp-octane # caddy | nginx | frankenphp | frankenphp-octane
driver: laravel # overrides auto-detection
publicPath: public # overrides the driver's default docroot
env: # extra environment exposed to PHP
APP_DEBUG: "true"
database: # optional DB hookup for `butler link`
type: mysql
name: my_app_local
createOnLink: true
scripts: # lifecycle hooks (run with cwd = site dir)
postLink: "composer install && php artisan migrate"
preUnlink: "php artisan app:cleanup"
server: # web-server tuning
clientMaxBodySize: "100M"
ssi: true # nginx Server Side Includes (nginx only)
phpSettings:
memory_limit: "1G"
ssl: # bring your own cert instead of Butler's CA
cert: /abs/path/to/site.pem
key: /abs/path/to/site-key.pem
proxy: # reverse-proxy mode (with driver: proxy)
target: "127.0.0.1:3000" # your own dev server (Vite, Next, etc.)
websocket: true # tunnel WebSocket upgrades; default true
headers:
X-Source: butler
octane: # only when webserver is frankenphp-octane
workers: 8
max_requests: 500
watch:
- app
- routes
- config
- .env
  • name — the hostname label the site is served at (<name>.<tld>). Useful when the project folder isn’t the name you want. Precedence at link time: an explicit butler link <name> wins, then this name, then the first .env site URL host that’s under your TLD (APP_URL, then PRIMARY_SITE_URL, then DEFAULT_SITE_URL), then the directory name. It’s read only when linking — changing it means re-running butler link.
  • php — the PHP version for this site, e.g. "8.3". Equivalent to butler isolate but committed with the repo. See PHP versions.
  • webservercaddy, nginx, frankenphp, or frankenphp-octane.
  • driver — force a framework handler instead of auto-detection (laravel, wordpress, symfony, drupal, statamic, craft, proxy, static, custom).
  • publicPath — the web root, relative to the project, when it isn’t the driver default.
  • env — extra environment variables exposed to PHP.

  • database — with createOnLink: true, butler link creates the database named in database.name on a running instance of database.type (mysql, mariadb, or postgres). It connects as the default local superuser (root for MySQL/MariaDB, postgres for PostgreSQL, no password) and is idempotent — an existing database is left alone. Best-effort: if no matching service is running (or it’s unreachable), the link still succeeds and reports why the database wasn’t created. See Databases & services and Connecting to databases.

    database:
    type: mysql
    name: my_app_local
    createOnLink: true

Both run via sh -c with the working directory set to the project root, and with Butler’s active PHP and Composer prepended to PATH — so php, composer and artisan resolve to Butler’s managed binaries without any shell setup. They’re best-effort: a failing hook is reported in the command output but never rolls back the link/unlink. Each is bounded by a 15-minute timeout (enough for a cold composer install).

  • scripts.postLink — run after the site is linked — e.g. install dependencies and run migrations: "composer install && php artisan migrate".
  • scripts.preUnlink — run before the site is unlinked, while the project is still in place — e.g. "php artisan app:cleanup".
  • server.clientMaxBodySize — max request body / upload size, e.g. "100M" or "1G". Applied on both nginx (client_max_body_size) and Caddy/FrankenPHP (request_body max_size). Use whole-number, nginx-style units (100M, 1G); a Caddy-style "100MB" is normalized to "100M" so it works on both, but nginx has no fractional form. This is the web server’s limit — for large uploads also raise PHP’s post_max_size/upload_max_filesize via server.phpSettings.
  • server.ssi — set true to enable nginx Server Side Includes (ssi on;) for the site. nginx only — Caddy and FrankenPHP don’t implement classic SSI, so pair it with webserver: nginx.
  • server.phpSettings — per-site php.ini overrides such as memory_limit or upload_max_filesize. Passed to PHP-FPM as a per-request PHP_VALUE (the FPM pool is shared across all sites on a version), so it only affects settings that are changeable at runtime (PHP_INI_ALL/PHP_INI_PERDIR) — not PHP_INI_SYSTEM ones. Works on both nginx and Caddy/FrankenPHP.
  • ssl.cert / ssl.key — absolute paths to your own certificate and key, used instead of Butler’s CA. See HTTPS & trusted certs.
  • proxy.target — with driver: proxy, forward to a dev server you run yourself (Vite, Next, etc.).
  • proxy.websocket — tunnel WebSocket upgrades (default true).
  • proxy.headers — extra headers to add to proxied requests.

Only applies when webserver is frankenphp-octane:

  • octane.workers — number of worker processes.
  • octane.max_requests — requests a worker handles before recycling.
  • octane.watch — paths that trigger a reload when they change.