All documentation
  1. Home
  2. Docs
  3. KLYRN to KLYRN

KLYRN to KLYRN: the Move

Two servers that both run KLYRN do not need a root SSH login between them. The source mints a grant for one account, the destination redeems it over the pinned panel port, and what the source knows about each site travels instead of being re-detected.

What has and has not been proven

A move has never crossed a real link. It has been run between two full KLYRN installs in two containers on one machine, each with its own configuration, database, panel certificate and port, over a virtual link through the host. That proves the protocol, the fidelity, the refusals and the grant's destination binding. It proves nothing about latency, loss or throughput, and there are no published numbers for a move for that reason.

  • rsync over a move is not built. A move uses the manifest strategy, which carries the same delta, the same path confinement and the same ownership rules. What it loses is resume within one large file: a stream that breaks at 90% of a 4 GB video re-sends that video. The engine records which strategy ran.
  • There is no panel screen for a move. CLI and API only.
  • Resume after a break has not been demonstrated live. A deliberate six-second link cut was survived by TCP retransmits, so the pass never failed and move resume correctly reported nothing to resume.
  • The destination cannot suspend the source. A grant is read-only by construction, so the move prints the command and records that it was not run. That is deliberate, and it is also what makes rollback easy.

What you need before you start

Both servers running KLYRN
The source's panel port, 7443 by default, must be reachable from the destination. SSH on the source does not have to be open at all.
Administrative access to both
The grant is minted on the source and redeemed on the destination, and the two halves are run on different machines by somebody who has to keep straight which is which.
Three strings from the source
The panel address, the certificate fingerprint and the token. They are printed together when the grant is minted, and the token is printed once.
Room on the destination
The preflight measures it and refuses below the size of the move plus a 15% margin.

On the source:

klyrn move grant --account shop --to <destination install id> --hours 24 --note "new box"
klyrn move grants
klyrn move revoke 4

On the destination:

export KLYRN_MOVE_TOKEN=klyrnmv_12.…
klyrn move preflight --from panel.old.example.com:7443 --fingerprint SHA256:…
klyrn move start     --from panel.old.example.com:7443 --fingerprint SHA256:…
klyrn move status 1
klyrn move sync 1        # the final pass. This is the downtime.
klyrn move abort 1       # roll back what landed here

Prefer the environment variable to --token. A move grant authorises pulling an entire account's files and databases off another server. Passed as a flag it lands in /proc/<pid>/cmdline, which is world-readable on a stock Linux and therefore readable by the customers who have shell accounts on the box. /proc/<pid>/environ is not. --token still works, and the grant is single-subject, time-boxed, bound to one destination and revocable, which is why this is advice rather than a refusal.

The grant, and why it is not an SSH login

The alternative this replaces is handing the destination a root SSH login on the source: the authority to read every tenant, forever, for a job that needs one tenant, once. A grant is that job and nothing else:

One subject
An account, or a single site. Nothing else on the source is readable through it.
One taker
Bound to the destination's install id. Minted without --to, any server holding the token can take the subject until it expires, and the source says so when it prints it.
Bounded
24 hours by default, a week at most.
Revocable
Every stream re-checks it, so klyrn move revoke stops a move that is already running.
Hashed
Only the SHA-256 of the secret is stored. The token is shown once and cannot be recovered; mint another if it is lost.
Read-only
It cannot write anything, cannot reach another account, and cannot enumerate. The source answers by running exactly one command from a fixed, closed list: the caller names a stream and a subject, never a command.

The channel is TLS to the source's panel with the certificate pinned to the fingerprint carried in the grant, exactly as an SSH host key is pinned, so a self-signed panel certificate is fine and an intercepted one is not. The fingerprint is SHA256: followed by the base64 SHA-256 of the panel certificate’s public key information rather than of the whole certificate, so a renewal that keeps the same key does not invalidate a grant half way through a move. Plain http is refused rather than upgraded: a move carries a tenant's whole account and a typo must not downgrade it. A grant records its first use, its last use and how many times it has been used, and klyrn move grants puts that count on the source's own screen, so you can see how much has been done with it before you revoke it.

What migrates

The destination asks the source for a configuration document and applies it, rather than sniffing the document root and guessing. That is the difference between this route and every other one on these pages.

Files
The whole document root, with the same ownership rules as every other migration: owned by the destination account, setuid stripped, symlinks not followed.
Databases and their users
Re-prefixed to the destination account. Passwords are regenerated and written into wp-config.php and .env, the way the inbound path already does.
Domains, aliases and redirects
Including www mode.
PHP version and per-site pool limits
What the source has set, not what a detector would infer.
Cron
Account-level and per-site, each entry carrying the site it belongs to, applied idempotently.
The resource policy
Subject to the destination's own entitlement limits, and only when a package of that name exists there.
nginx rules and redirects
As configured, not as re-derived.
Custom certificates
Key and chain. Certificates KLYRN issued itself are not moved. See below.
Container applications
Their definitions, routes and volumes. The images are not sent as layers: the destination pulls the exact digest from the registry and refuses if it gets different bytes, so the registry has to be reachable from there.

What arrived is proved against the source, not asserted. Before the final pass can be called finished, both servers list the tree and the listings are compared on path, size, mtime to the second, the owner's permission bits and symlink target. Owner, group bits and directory times are excluded because a move changes all three on purpose. Content is not read, and the method is recorded, so the report never implies a stronger check than the one that ran.

What does not migrate

Named on the preflight and again in the move's own status, rather than left to be discovered:

  • Backup history. It stays on the source; the destination takes its own first backup after cut-over.
  • Logs, metrics and resource history. Stated, not silently dropped.
  • Mail. KLYRN does not host mail, so there is none to move and no MX record is touched.
  • Let's Encrypt certificates. Re-issued on the destination once DNS points there.
  • Site secrets. They are sealed with the source's own key; the configuration document says how many were left behind.
  • Application deployment history. Only the release that is serving travels.
  • The basic-auth password file for a site that has one. The preflight warns; set the password again on the destination.
  • A Node application's code. Its release tree lives outside the document root a grant may read, so the site is refused rather than half-created. See the refusals below.

What downtime to expect

The same two passes as every other two-phase migration. klyrn move start runs the pre-copy while the source keeps serving; klyrn move sync is the final pass, and its wall time is the downtime. Both are measured and printed: the status prints the final sync's seconds, bytes and file count once it has finished.

What decides it:

  • The delta. A move uses the manifest strategy, so only files whose size or mtime differ travel. On an account of many small files that is the whole story.
  • The databases, in full. A fresh snapshot at the final pass, as everywhere else, because InnoDB has no safe incremental without binlog access.
  • An account of very few very large files, over a link that drops. This is where the missing rsync transport costs something: a broken stream re-sends the file it was on, not the whole account, but it does re-send that file.

An account's sites move one at a time, each resumable on its own, and the account-level settings are applied once at the start so the sites land into a correctly shaped account.

The preflight, and what each refusal means

klyrn move preflight answers “would this work?” and creates nothing at all. klyrn move start runs the same checks again and refuses on the same evidence, so a refusal never arrives half way through.

“example.com already exists on this server, under account other”
Writing into it would be writing into somebody else's website. Remove or rename it here first, or leave that site out with --site. An alias or redirect name that is already claimed refuses the same way.
“runs a Node application, whose code lives outside the document root and cannot travel over a move grant”
Deliberate. Creating the site here would give an nginx proxy to a port with nothing behind it: a 502 that looks like a finished move. The refusal prints the start command, the Node version, the port, the memory limit and the health path, so nothing is lost: create the site here, deploy it from git, and move the rest of the account with --site for each remaining site.
“2 database(s) of that name already exist here”
Move into a differently named account with --account, or remove them here first.
The licence limit
Counted for the whole move, not per site. A free edition that allows five sites and already has four cannot take three, and finding that out after two of them have landed is exactly the half-move this refuses. The count is asked again as each site is created, because a move can sit for hours between the check and the site.
“there is not enough room on this server”
Measured against the move's size plus 15%. A document root that exactly fills the filesystem is a broken server, not a finished move.
“this grant does not reach a site called …”
Named with what the grant does cover. A grant for one site reaches exactly that site; a grant for an account reaches every site of that account and none of another's.
“there is nothing to move”
No sites selected and no applications to carry.
A container runtime that cannot take the applications
Not installed, not reachable, or speaking an API version KLYRN does not pin: each refused by name, rather than discovered one container at a time during the cut-over.

Three things are warnings rather than refusals, and each says what will happen instead:

  • An account of that name already exists here: the moved sites are added to it, and nothing already in it is touched.
  • A system user of that name exists and is not managed by KLYRN: the account is given a different name and the database names are re-prefixed to match.
  • A PHP version the source uses is not installed here: the site runs on this server's default, and the preflight lists what is installed.

The cut-over

  1. Pre-copyklyrn move start. The source keeps serving; each site becomes its own resumable migration and klyrn move status shows where each one is.
  2. Look at the sites hereThey are live on the destination with nothing pointing at them.
  3. Lower the TTLHours before, not at, the switch.
  4. Final syncklyrn move sync <id>. This pass is the downtime, and it ends with the cross-server comparison; a listing that does not match is printed, file by file, rather than summarised away.
  5. Suspend the old copy yourselfA grant is read-only, so the destination cannot do it. The move prints the exact command (klyrn account suspend <name> or klyrn site suspend <domain>) to run on the source once you are satisfied. Nothing then runs twice and nobody writes to the old copy.
  6. Point DNS at the destinationAnd let the certificates be issued there: KLYRN's own certificates are not moved, they are re-issued once the name resolves.
  7. Revoke the grantklyrn move revoke <id> on the source, when the move is finished. The list shows how many times it was used.

Mail is not part of this. Neither server hosts mail, so there is no mailbox to move and no MX record to change. Whatever answers for your mail today keeps answering.

How to roll back

The source is never written to. A grant is read-only by construction (the source answers a short, fixed list of stream requests and nothing else), so the old copy is exactly as it was, and it is still serving unless you suspended it yourself. That is what makes rollback a small operation.

  1. Roll back the destinationklyrn move abort <id> removes only what the move created here: the sites it made, with their files, and the databases it created. Anything that was on this server before is untouched. It refuses while a job for the move is still running, and says which sites it could not remove if any.
  2. Or, if you had already suspended the sourceUnsuspend it on the source and point DNS back. That is the whole of the rollback, and it is why the move asks you to suspend the old copy rather than delete it.
  3. Revoke the grantRevoking stops a move in flight at its next stream, so it is also the emergency stop.

Troubleshooting

“the grant is no longer held (klyrn-core restarted); pass --token to resume or sync”
A move holds a grant, not a login, and it is held in memory exactly as an SSH credential is. Give the token again on resume or sync, or set KLYRN_MOVE_TOKEN.
“that is not a move token”
The token looks like klyrnmv_12.<secret> and is shown once, when the grant is minted on the source.
“a move will not run over plain http”
Use the https address the grant printed. The certificate is pinned, so a self-signed one is fine.
The move stops after the grant was revoked
Every stream re-checks the grant, so revoking is immediate rather than advisory. Mint a new grant and klyrn move resume.
“DIFFERS” in the verified column
The cross-server comparison found entries missing here, extra here, or differing. klyrn move status prints them individually. Resume rather than starting again: nothing already here is sent twice.
“a job for this move is still running; wait for it to stop before rolling back”
Rolling back under a running pass would race it. Wait, then abort.
A site arrives suspended
It was suspended on the source. The preflight says so per site.
The resource policy did not come across
There is no package of that name on the destination, so the account is on the destination's default and the preflight prints what the source's package was. Nothing is invented, because inventing it would mean quietly granting somebody unlimited memory.

Where a move cannot reach (a source whose panel port is closed to the destination), the SSH route still works and is still a pull.