Guides

Server-to-server migration

Move a running project and its data from one of your connected servers to another — no rebuild, volumes streamed host-to-host, with a DNS trick to keep downtime to seconds.

Outgrew a VPS, or consolidating boxes? Openship moves a running project — its containers and its volume data — from one connected server to another. It reuses the image that's already live (no rebuild) and streams the data directly between hosts. Both servers just need to be connected to Openship over SSH.

This is the same engine as the platform import

Moving between your own servers and importing from Coolify / Dokploy / Dokku run through the same migration flow — you just pick a different target server. That page has the deeper architecture notes; this one is the focused server-to-server path.

Before you start

  • Both servers connected to Openship (Servers → Add server), and you need server:write on both the source and the target.
  • Locally-built images can't cross hosts. A service whose image was built on the source (no registry to pull from on the target) is blocked — take those over in place on their current server instead. Services running a published image (postgres, your app's pushed image, catalog apps) move fine.
  • Cross-server is never zero-downtime. Unlike a same-server takeover, moving to another host stops the originals to copy the data consistently, then verifies a fresh deploy on the target — a real (usually short) downtime window. The DNS step below is what keeps it to seconds rather than minutes.

Lower your DNS TTL first — do this a day ahead

The migration re-points your app to a new server IP. For a custom domain, that means updating its A record — and the old record is cached for however long its TTL says. Set the domain's TTL to 60 seconds (1 minute) at your DNS provider a few hours (ideally a day) before you migrate. Then, when you flip the A record to the new server after cutover, it propagates in about a minute instead of hours — that's the difference between seconds of downtime and a long tail. Raise the TTL back up once you've confirmed the move. (Free .opsh.io domains are routed for you — no A record to touch.)

Move a project (step by step)

Start the migration and pick the source

Open the migration wizard and choose the source server (server A). Openship scans it read-only and lists what's running — each project/service with its ports and volumes.

Screenshot

The migration wizard's source-server scan, listing the running projects and their volumes. (screenshot pending)

Select the workloads to move

Pick the project(s) or services you want on the new box. A reverse proxy on the source is excluded automatically — you're moving your apps, not the edge.

Choose the target server

Set the target server (server B) — a different connected server. This is what makes it a server-to-server move rather than an in-place adopt. Named volumes and movable app-data mounts are streamed A → B directly (same Docker daemon: a tar → tar copy; across daemons: a gzip stream); system paths (/proc, /sys, /var/lib/docker, sockets) are never copied.

Your data is safe on the source

Openship refuses to overwrite a non-empty same-named volume on the target, so a re-run can't clobber data, and it never deletes the source volumes during the move.

Migrate

Openship stops the originals on A for a consistent copy, streams the volumes to B, brings the workload up there on the same image, and polls it to ready (up to ~20 minutes for a large first move). Watch the progress live.

Screenshot

Volume transfer A → B with per-service progress, ending in a healthy deploy on the target. (screenshot pending)

Re-point DNS, verify, then cut over

The workload is now live on server B and Openship has re-registered its routing + certificate there. For a custom domain, update its A record to server B's IP at your DNS provider (this is where the low TTL pays off — it goes live in about a minute). Confirm the app serves through B, then run cutover to stop and remove the leftover containers on A. Your source volumes are never touched by cutover, so you keep a fallback until you're sure; raise the DNS TTL back up afterward.

What moves, and what doesn't

  • Moves: the running containers (via their current image), named volumes, and movable app-data mounts — streamed host-to-host; routing + Let's Encrypt are re-established on the target.
  • Doesn't move: locally-built images (no registry on the target — take those over in place), and system paths / sockets (never copied).
  • Downtime: a short window while volumes copy and the target verifies. A low DNS TTL set ahead of time is what shrinks the DNS half of that to seconds.

Next steps

On this page