nkapp migrate
Migrate an entire environment from the deploy target it currently runs on to a different deploy target — moving services, stateful data, TLS certificates, and DNS — with a readiness‑gated cutover and the source kept as a rollback fallback.
nkapp migrate -e <env> --to <deployTarget> [options]migrate is the one command that performs a complete host‑to‑host move (e.g. kama-vps → nk-prod-vps). It re‑points the environment, provisions the destination, copies the data, carries the gateway’s TLS certs, flips DNS, and verifies — all without touching the source until you explicitly decommission it.
Options
| Flag | Type | Default | Description |
|---|---|---|---|
-e, --env <env> | string | — | Required. Environment to migrate. |
--to <deployTarget> | string | — | Required. Destination deploy target — must be a key under deploy: in nanokit.yml. |
--from <name> | string | current | Source deploy target. Defaults to the environment’s currently‑resolved target. |
--dry-run | boolean | false | Print the plan and change nothing — fully side‑effect‑free. |
-y, --yes | boolean | false | Skip confirmation prompts (CI). |
--no-data | boolean | — | Provision + re‑point only; skip stateful data movement. |
--data-only | boolean | false | Move only the stateful data (assumes the destination is already provisioned). |
--no-certs | boolean | — | Skip carrying the gateway TLS certificates to the destination. |
--no-dns | boolean | — | Skip the DNS cutover (no live traffic move). |
--force | boolean | false | Overwrite non‑empty destination volumes during the data copy (clean‑replace). Required on any re‑run/cutover where the destination already holds data. |
--decommission | boolean | false | Tear down the source after a verified cutover. Off by default — the source is kept as a rollback fallback. |
--services <list> | string | all | Limit volume migration to specific services (comma‑separated). |
--keep-source | boolean | true | Keep the source after cutover (the default; --decommission is the opt‑in to remove it). |
What it does
The migration is a deterministic, idempotent pipeline. Each phase is re‑runnable.
1. Resolve & validate
Resolves the from (current) and to targets, validates that --to exists under deploy:, and rejects from === to. Validates SSH reachability.
2. Plan & confirm
Renders a structured plan — Infrastructure · Data · DNS · TLS Certs · Config · Decommission — and asks for confirmation (unless -y). --dry-run stops here.
3. Re‑point config
Flips environments.<env>.deploy in nanokit.yml to the destination target (preserving the string‑shorthand vs object form). This is the authoritative binding that deploy/up use thereafter.
4. Provision destination
Runs host setup + deploy against the destination to bootstrap the host and stand up the services and gateway. DNS sync is suppressed during this step — only the dedicated cutover phase changes DNS.
5. Migrate data
Copies every stateful asset byte‑for‑byte (clean‑replace) from source → destination: all environment databases and all service named volumes. The source is never modified.
6. Restart destination services
Restarts the destination services that received migrated data so they reload with the real config/data (a freshly‑provisioned stateful service boots empty before the data lands).
7. Carry TLS certificates
Copies the gateway’s already‑issued public certificates for the environment’s grey (non‑proxied) hosts from the source gateway to the destination’s — so those hosts serve valid HTTPS immediately at cutover, with no ACME race.
8. Readiness gate
Confirms the destination is actually serving the expected service containers. DNS is never changed unless this passes — production traffic is never pointed at an unprepared host.
9. DNS cutover
Re‑points the environment’s hostnames to the destination IP via the configured DNS provider (e.g. Cloudflare), preserving each service’s proxied flag. Skipped with --no-dns.
10. Verify
Confirms the destination passed the deploy health gate.
11. Decommission (opt‑in)
With --decommission, tears down the source last, after verify. Default keeps the source as a rollback fallback.
What gets migrated (scoping)
| Asset | Scope |
|---|---|
| Services | The environment’s effective services (its services: block — same scoping deploy uses). |
| Databases | All databases the environment provisions (parity with the reconciler), copied as their data volumes. |
| Volumes | Every named volume declared by an in‑scope service. Bind mounts (host paths) are skipped. |
| TLS certificates | Public certs for the environment’s grey (non‑proxied) hosts. Proxied hosts use tls internal behind the CDN and need no carry. |
| DNS | All project‑ and service‑level hosts of the environment, re‑pointed to the destination IP. |
Cutover & TLS
Two kinds of host behave differently at the DNS flip:
- Proxied hosts (e.g.
nanokit.devbehind Cloudflare) serve immediately — the CDN terminates TLS and accepts the origin’stls internalcertificate. - Grey / non‑proxied hosts (e.g.
mail.nanokit.dev,storage.nanokit.dev) are hit directly and need a valid public certificate on the destination. Because HTTP‑01/TLS‑ALPN‑01 ACME validates against the live A record, a certificate cannot be freshly issued during the flip (the record still resolves to the source).migratesolves this by carrying the existing certificates from the source gateway to the destination (phase 7), so grey hosts serve valid HTTPS the moment DNS changes.
Recommended workflow
For a live environment, run it staged so you can verify before moving traffic.
# 1. Preview — changes nothing
nkapp migrate -e production --to nk-prod-vps --dry-run
# 2. Provision + data + certs, WITHOUT touching DNS (traffic stays on the source)
nkapp migrate -e production --to nk-prod-vps --no-dns
# 3. Verify the destination directly (bypass DNS via the Host header)
curl -sk --resolve nanokit.dev:443:<dest-ip> https://nanokit.dev/ -o /dev/null -w '%{http_code}\n'
# 4. Cutover — re-sync the latest data, carry certs, then flip DNS
nkapp migrate -e production --to nk-prod-vps --force -y
# 5. Once fully confident, decommission the source
nkapp migrate -e production --to nk-prod-vps --no-data --no-dns --decommissionOr, in a single maintenance window:
nkapp migrate -e production --to nk-prod-vps --force -y # re-point → provision → data → certs → DNS → verifySafety & rollback
- Plan‑first + confirm.
--dry-runis fully side‑effect‑free; a real run prints the plan and asks before acting. - Source kept by default. The cutover is reversible — flip the binding back and re‑point DNS (the source still holds its data) until you run
--decommission. - Readiness gate. DNS is never changed unless the destination is confirmed serving — production traffic is never pointed at an unprepared host.
- Source is never modified by the data/cert copy (read‑only on the source; the destination is the only thing written).
Limitations
- SSH / Docker targets. Data movement, cert carry, and DNS cutover apply to
type: ssh(Docker‑over‑SSH) targets. Cloud targets (type: cloud) are guarded — their managed stores/DNS need provider‑native snapshot/restore, not yet automated. - Stateful‑service downtime window. Copying a database or volume briefly quiesces the owning source container for a consistent copy — a short interruption on the source during the data phase.
- Complex mail servers. A self‑hosted mail server (Stalwart) carries both a config volume and a database; ensure the data phase + service restart complete before cutting over, and verify SMTP/IMAP on the destination, not just the web UI.
See also
nkapp deploy— the underlying VPS deploy thatmigratedrives to provision the destination.nkapp host— VPS bootstrap and certificate trust.nkapp destroy— teardown used by--decommission.