Skip to content

Databases & services

Beyond serving PHP sites, Butler manages the backing services your projects need — databases, caches, a mail catcher, search and object storage. A fresh install ships none of these; you add exactly the ones you want.

Butler draws a small but useful distinction:

  • A component is an installed binary — for example the MySQL engine or the Redis server. Installing a component downloads and verifies the binary but doesn’t run anything.
  • A service is a running instance of a component — an actual MySQL server listening on a port, with its own data directory. You can run more than one instance of the same component (say, two MySQL versions) as separate services.

The menu bar app mirrors this: a Components section for what’s installed and a Services section for what’s running.

Terminal window
butler install mysql
butler install redis
butler install mailpit

See what you have installed:

Terminal window
butler components

Butler’s catalog includes databases (MySQL, PostgreSQL, MariaDB, MongoDB), caches (Redis, Valkey), mail (Mailpit), search (Typesense) and S3-compatible storage — installed on demand as verified static binaries.

Once a component is installed, manage instances of it with butler service:

Terminal window
butler service add mysql # create + start an instance (on a unix socket)
butler service add mysql --tcp # …or listen on 127.0.0.1:3306 instead
butler service status mysql
butler service stop mysql
butler service restart mysql

By default an instance listens on a unix socket; pass --tcp (or --port) to bind a loopback TCP port instead. See Connecting to databases for the details.

For everyday start/stop across everything, the top-level commands are quickest:

Terminal window
butler start # start Butler's services
butler stop # stop them
butler restart mysql # restart one by name

Run butler start with no arguments in a terminal and it confirms before starting everything.

Butler treats a minor/patch upgrade and a major upgrade very differently, because a database’s on-disk format only stays compatible within a major version.

Staying on the same major version — say MySQL 8.4.98.4.10 — is an in-place upgrade. Butler downloads the new component, snapshots the data directory, repoints the instance and restarts it, rolling back automatically if the new version fails to start:

Terminal window
butler service upgrade mysql-default # latest compatible minor/patch
butler service upgrade mysql-default @8.4.10 # a specific version

In the menu bar app, the same upgrade appears as an Update available card on the service’s detail panel.

Crossing a major — for example MySQL 8.49.x — is not an in-place upgrade. The newer engine would run a one-way conversion of your data directory on first start, with no path back, so Butler refuses it and asks you to migrate instead.

The safe path is a logical migration: dump from the old instance and load into a fresh instance on the new version, side by side, then cut over.

Terminal window
# 1. Install the new major and add a fresh instance for it
butler install [email protected]
butler service add mysql --name v97 # → instance "mysql-v97"
# 2. Dump from the old instance, load into the new one.
# Each instance's socket lives under Butler's Run directory.
RUN="$HOME/Library/Application Support/Butler/Run"
BIN="$HOME/Library/Application Support/Butler/Components/mysql"
"$BIN"/8.4.*/bin/mysqldump --socket="$RUN/mysql-default.sock" -u root \
--all-databases --routines --events --single-transaction > /tmp/mysql-8.4.sql
"$BIN"/9.7.*/bin/mysql --socket="$RUN/mysql-v97.sock" -u root < /tmp/mysql-8.4.sql
# 3. Point your apps at mysql-v97, verify, then retire the old instance.
# Removing an instance keeps its data directory as a safety net.
butler service remove mysql-default

A logical dump/restore leaves the old data directory untouched, so if anything looks wrong you can point your app back at the original instance.

The equivalent flow for PostgreSQL uses pg_dumpall/psql, and MongoDB uses mongodump/mongorestore — the shape is the same: dump from the old instance’s socket, restore into a fresh instance on the new version.

Install a database engine, add a service instance, and connect your app to it on 127.0.0.1 with the default port for that engine — see Connecting to databases for the exact hosts, ports and credentials. You can also have Butler create a database and wire it up when you link a project — set it in the project’s butler.yml:

database:
type: mysql
name: my_app_local
createOnLink: true

Install Mailpit to catch outgoing mail locally. Point your app’s SMTP settings at Mailpit’s local port and every message your app sends lands in Mailpit’s inbox instead of a real recipient — ideal for testing password resets, receipts and the like.

One Mailpit serves every project: it accepts any SMTP credentials and tags each message with the username it was sent with, so setting MAIL_USERNAME per app (MAIL_USERNAME="${APP_NAME}" in Laravel) makes the inbox filterable by app.

Every service writes to a log you can tail:

Terminal window
butler log mysql

The app’s Logs section shows the same output live for any process.

Terminal window
butler service remove mysql # remove a running instance
butler component remove mysql # remove the installed binary

Butler guards against removing a component that a service still depends on, so you won’t accidentally pull the binary out from under a running instance.