Installing Sailfish
Grant the database user what it needs, require the package, migrate and narrow the gate. The first snapshot is taken within five minutes.
Sailfish is a Laravel package with its own routes, views and assets. Installing it takes a Composer registry, a migration and one service provider, and the scheduler you already run does the rest. It reads performance_schema over one of your application's database connections and stores its history in tables next to your own.
Requirements#
- PHP 8.2 or newer
- Laravel 12 or 13
- MySQL 8.0
- The Laravel scheduler running on at least one server (
php artisan schedule:runevery minute) - A database user that can
SELECTfromperformance_schema
Database grants#
Sailfish uses one connection, both to read performance_schema and to store its sailfish_* tables. Its
user needs SELECT on performance_schema. An application user granted only its own schema
does not have that, and sailfish:collect names the grant it is missing rather than recording nothing.
GRANT SELECT ON performance_schema.* TO 'app'@'%';
-- Optional: index sizes
GRANT SELECT ON mysql.innodb_index_stats TO 'app'@'%';
-- Optional: InnoDB's history list length
GRANT PROCESS ON *.* TO 'app'@'%';
The two optional grants each add one figure. Without mysql.innodb_index_stats the dashboard shows no
index sizes, and without PROCESS the history list chart and its
event stay empty. A user with
ALL on *.* already has all three.
If your application's user should not have these, point SAILFISH_DB_CONNECTION at a second connection
to the same server whose user does. See configuration.
1. Authenticate Composer#
Sailfish is distributed through a private Composer registry. Your license key is issued through Anystack at checkout. Run this once on every machine that installs the package, including CI and build servers:
composer config --global --auth http-basic.sailfish.composer.sh \
you@example.com \
YOUR-LICENSE-KEY
The username is the email address the license belongs to and the key is the password. On servers, supply the same
pair through COMPOSER_AUTH instead of committing an auth.json:
export COMPOSER_AUTH='{"http-basic":{"sailfish.composer.sh":{"username":"you@example.com","password":"YOUR-LICENSE-KEY"}}}'
2. Require the package and migrate#
Add the registry to your application's composer.json:
{
"repositories": [
{ "type": "composer", "url": "https://sailfish.composer.sh" }
]
}
Then require Sailfish and run the migrations:
composer require boring-o11y/my-sailfish
php artisan migrate
Package discovery registers Sailfish's own service provider, which adds the commands, the schedule and the
dashboard routes. The migrations create the sailfish_* tables described in
what gets recorded.
3. Let the scheduler run#
Sailfish puts its commands on Laravel's scheduler for you:
| Command | Scheduled | What it does |
|---|---|---|
sailfish:collect |
Every five minutes, withoutOverlapping and onOneServer |
Reads performance_schema, stores what moved since the last run, then runs sailfish:detect |
sailfish:daily-digest |
Daily at 09:00 | Sends the events that go out in the daily digest |
sailfish:rollup |
Daily at 03:00 | Merges old snapshots into hourly and daily ones (see retention) |
The first collection only records a baseline, because counters running since the server booted describe no
particular interval. Samples start with the second one, five minutes later. Set
SAILFISH_SCHEDULE_ENABLED=false to register the commands yourself.
4. Decide who sees the dashboard#
The dashboard is served at /sailfish. In the local environment anyone can open it.
Everywhere else nobody gets in until you say who can. Publish the provider:
php artisan vendor:publish --tag=sailfish-provider
Register it in bootstrap/providers.php, then narrow its gates:
// app/Providers/SailfishServiceProvider.php
protected function gate(): void
{
Gate::define('viewSailfish', fn ($user) => $user->isAdmin());
Gate::define('viewSailfishQueryText', fn ($user) => $user->isDba());
}
viewSailfish opens the dashboard. viewSailfishQueryText decides who sees running
statements on the Activity page as they were sent, with their values. Everyone else sees them with each literal
replaced by ?, because those values can be email addresses and tokens.
5. Check it#
php artisan about gets a Sailfish section with the connection, the schemas watched, the last snapshot
and the retention. To take a snapshot straight away rather than wait for the scheduler:
php artisan sailfish:collect
Sailfish sends nothing until you give it somewhere to send. Set up notifications to hear about the events it finds.