Run the registry server
This page is for whoever runs the registry server. It covers the jobs you will actually do: start it, back it up, rotate tokens, kill a remote, recover from a halted rollout and upgrade it. The server is one option. The static registry needs no server at all.
Before you start
You need Node 22.13 or newer, or Docker. The server uses node:sqlite, which is unflagged from Node 22.13. Node prints an "experimental" warning for it. That is expected.
You also need a place to serve build files from, such as a bucket behind a CDN. The server never stores builds. It reads them from there, checks them and moves the pointer.
Start it
The quickest way to see it work is the compose example. It starts the server and MinIO for artifacts:
You should see both containers running with docker compose ps. The server answers on 127.0.0.1:8787.
Without Docker, install the package and set the two addresses:
You should see a log line ending in listening on http://127.0.0.1:8787. Check it with curl http://127.0.0.1:8787/healthz, which answers {"ok":true}.
Put TLS in front of it before anyone outside your machine uses it. The server binds to loopback unless you set MFEFLOW_REGISTRY_BIND. Every setting is listed in the package README.
Make the first token
Create a token on the server itself. This is the only time its value is shown:
You should see the token on stdout and Token <id> created on stderr. Store it as MFEFLOW_REGISTRY_TOKEN in your CI secrets. Under Docker, run the same command with docker compose exec registry in front.
Give each token the fewest actions and the narrowest env it needs. A CI job that only deploys to staging gets deploy on staging.
Back up the SQLite file
Everything the server knows is in one file: pointers, version records, tokens and the audit log. Lose it and you lose the pointers.
The database runs in WAL mode, so do not copy the file while the server runs. Ask SQLite for a consistent copy instead:
You should see /backups/registry.db appear, as one file with no -wal beside it. Run this on a schedule and copy the result off the machine.
To restore, stop the server, remove the old -wal and -shm files, put the backup in place and start it again. The server republishes the fallback index for every env at start, so shells that fall back get the restored state.
Builds are not in this file. Back up the artifact host the way you back up any bucket.
Rotate a token
Rotate on a schedule, and at once when one may have leaked. Make the new token first, so nothing breaks:
- Create a token with the same scope. See Make the first token.
- Put it in the CI secret and run one deploy or
mfeflow statusto prove it works. - List the tokens and find the old id:
You should see one line per token, with its env, remote and actions.
- Revoke the old one:
You should see Revoked <id>. The old token gets a 401 on its next request. The audit log keeps its id, never its value.
Kill a remote
Use this when a remote is hurting people right now and you cannot wait for a fix. It removes the remote from the served index:
You should see checkout is killed in prod: it is out of the index. The next index request leaves it out, and the static fallback is rewritten without it. The shell shows its fallback for that route.
Two limits are worth knowing. Browsers keep the index for up to 5 seconds, so allow that. And a page that already loaded the remote keeps running it until the person reloads.
To bring it back:
You should see checkout is live again in prod. A restore is refused if another remote took one of its routes meanwhile. The error is E502.
Recover from a halted rollout
The server halts a rollout when the candidate's error rate is clearly worse than stable's. A halt is an abort: everyone is back on the stable version. Nothing needs undoing.
To see why it halted, read the audit log for the remote:
You should see a rollout.halt entry whose detail has the reason and the counts for both sides. Then:
- Check that
mfeflow status --env prodshows the stable version and no rollout. - Fix the cause. A version is immutable, so the fix needs a new version number.
- Deploy it again with
mfeflow deploy checkout --env prod --percent 10 --steps 50,100.
While a rollout runs, mfeflow rollout status checkout --env prod shows the counts and what the gate is waiting for. If the gate never reaches a verdict on a quiet remote, it needs more traffic or lower minimums. See the gate settings in the README.
To end a rollout yourself, use mfeflow rollout abort. To finish it, use mfeflow rollout complete.
Upgrade the server
Migrations run when the server starts and only go forward. An older server cannot read a newer file, so take a backup first.
- Back up the file. See Back up the SQLite file.
- Pull the new image, or install the new package version.
- Restart. Under compose that is
docker compose pull && docker compose up -d. - Check
/healthzand runmfeflow status --env prod.
To go back, stop the server, restore the backup and start the old version. Anything written after the backup is lost, so keep that window short.
The CLI and the server are versioned together. Upgrade both in the same change when you can.
Troubleshoot
For the other E5xx codes, see Error codes. For the numbers behind the timings above, see Registry server SLOs.