Skip to content

Settings migrations

turnout stores its catalogs as JSON in the data directory, and meta.json records which shape they are in:

{ "schema_version": 2 }

When a release changes that shape, the first command you run afterwards upgrades the directory in place and says so:

Migrating settings from schema 2 to 3.
A copy of the old files is in ~/.local/share/lacodda/turnout/settings-backup-v2
<what the step changed>

Then the command you actually typed carries on. There is no separate migrate command to remember: a tool that refuses to start until you type a magic word is just a worse error message.

v0.24.0 lets a target carry a health check and an address of its own. Both are optional, so nothing in the directory is rewritten and no copy is set aside - the step says what changed and moves the version on.

What changes is who may read the directory. A turnout older than v0.24 would read a target with a health check, drop the check without a word, and deploy unchecked - no check, no rollback. After the bump it refuses the directory instead, the way it refuses anything a newer turnout wrote.

v0.20.0 gave every job turnout runs a record in the job registry, and the gateway became the first of them. Its record moves out of state.json into jobs/gateway.json.

A gateway that is running through the upgrade keeps running and is known by the new version - but only if it still is one: its process alive and its first port answering. A record left behind by a gateway that died long ago may name a pid the system has handed to another program since, and carrying that over would hand the program to the next gateway stop. Such a record is dropped with a note, and the next gateway start begins clean.

v0.18.0 made the gateway port turnout’s to hand out rather than yours to type, so an app without one is no longer a state you can reach by pressing enter. Migrating to schema 4 gives every app that has none a port from 7100-7199.

An app that already has a port keeps exactly that number, including one from outside the range. It is already written into the app’s dotenv file, and probably into a copy someone made from it; renumbering would break a working setup while claiming to upgrade it. The migration only fills the gaps, and names the apps it filled.

v0.11.0 pulled the app-to-path map out of the server and into its own entity, the target. A schema-2 directory has that map spread across every server as server.deploy[app] = path; migrating to schema 3 walks each one and creates a target named {app}-{server} for every entry, carrying over the server’s own credential.

A deploy entry on a server that has no credential set converts to nothing - there is no login to give the target - and the migration says so by name, so the route can be re-created once a credential exists.

Unlike schema 1 to 2, this step runs automatically rather than refusing: schema 1 to 2 needed a user@host split into pieces you would have to name yourself, and turnout has no way to guess names you will recognize. A target’s name, by contrast, is joined entirely from names you already chose - the app’s and the server’s - so {app}-{server} is a name you would likely have picked anyway, not an invention.

turnout export and turnout import moved to format 3 alongside it. Importing a format-2 file converts its routes into targets the same way, at import time.

Sometimes the new shape needs information the old one does not contain, and inventing it would be worse than stopping. That is what happened in v0.9.0: a server used to carry one user@host and an inline directory per app, and those became credentials and paths - named entities. turnout has no way to pick names you will still recognize six months from now.

So a schema-1 directory is refused rather than converted. The refusal names what changed, sets a readable copy of the old catalogs aside in settings-backup-v1, and lists the commands to re-enter them with. Your original files are untouched, so installing v0.8 again puts you exactly back. The walkthrough is in Upgrading to 0.9.

Before anything is rewritten, every JSON file turnout owns is copied to settings-backup-v<N> inside the data directory - next to the data, not in a temp folder, so it is where you would look for it. Journals and caches are left alone; they are not what a migration touches.

Nothing deletes that folder. If a migration ever gets something wrong, copying the files back and installing the previous release puts you exactly where you were. A second migration of the same directory writes settings-backup-v1-2 rather than overwriting the first safety net.

If the directory was written by a newer release than the one you are running, turnout stops:

error: this data directory was written by a newer turnout (schema 3, this build reads 2).
Update turnout with `turnout self-update`, or point TURNOUT_DATA_DIR at a different directory.

This is the case worth being strict about. The files would very likely still parse - and mean something different. Reading half of a shape you do not understand is how settings get silently corrupted, so an old binary refuses rather than guesses. It happens naturally when two machines share a synced folder, or after rolling a release back.

Nothing is rewritten on the way out, including the version marker: the directory is exactly as it was when the newer turnout last touched it.

Migrations are single-version hops applied in order, so a directory several releases behind is upgraded one step at a time and each step only has to know the shape immediately before it. A version with no path to the present is an error rather than a silent no-op, with the honest way out - back up the directory and run turnout setup fresh.

Moving settings deliberately between machines is a different job, and turnout export does it.