Moving from Easypanel or Coolify

Also as Markdown for agents, and on GitHub.

tugport migrate easypanel and tugport migrate coolify move a server's projects, services, domains, certificates, environment, volumes and deploy webhooks into Tugport. The installer runs tugport migrate detect and offers the move when it finds a panel.

Command What it does
tugport migrate detect Prints the panels found here (easypanel 2.38.0).
tugport migrate easypanel --dry-run Prints the plan: every service, what maps, what needs attention, what does not move. Changes nothing and never prints secret values.
tugport migrate easypanel [--yes] Runs the move. Asks first unless --yes.
tugport migrate rollback [--yes] Undoes it: the old panel, its proxy and services run again.

Tugport must be installed and running: its proxy waits for ports 80 and 443 and takes them over at the switch.

Reading the old panel

How things map

Old Tugport
Project (Coolify: project and environment; production keeps the project's name) Project, with project-level env as shared variables
App from GitHub, Git, image or Dockerfile App with the same source. The first deploy runs the image the old service runs, so nothing is rebuilt during the move.
Nixpacks / Railpack build settings Same builder; install, build and start commands become NIXPACKS_* / RAILPACK_* variables
Postgres, MySQL, MariaDB, MongoDB, Redis Database service on the same data directory or volume, same credentials and image (Easypanel: pinned to the exact image it runs, so a redeploy never upgrades it)
Compose stack Compose service; named volumes point at the old stack's volumes
Env vars, file mounts Encrypted env vars and files. Easypanel's $(PROJECT_NAME), $(SERVICE_NAME), $(PRIMARY_DOMAIN), $(EASYPANEL_HOST) and $(EASYPANEL_DOMAIN) are filled in
Volume and bind mounts The same Docker volumes and host paths, so no data is copied
Domains, basic auth, redirects, maintenance page, www redirect Domains with the same options
Let's Encrypt certificates Imported, so nothing is re-issued
Easypanel deploy webhook http://IP:3000/api/deploy/<token> Keeps working: Tugport answers on :3000 after the switch, and GitHub pushes to that URL deploy only the deployed branch
GitHub token (Easypanel) A github_token Git provider
zeroDowntime: false, published ports stop_first rollout
Service names with _ Renamed (cas_ban_bot becomes cas-ban-bot); every old hostname (bots_cas_ban_bot, bots-cas-ban-bot, cas_ban_bot, Coolify's uuids and container names) stays an alias
A service mentioning another project's service by its old name The referenced service also joins the referencing project's network

Not moved, and listed in the plan: Easypanel Box and WordPress services, the *.easypanel.host addresses Easypanel gives every service, wildcard domains, custom Traefik middlewares and hand-written Traefik routes, database admin tools, backup schedules, notification channels, users, Coolify's API-token deploy webhooks, KeyDB, Dragonfly and ClickHouse.

The cutover

Every step is recorded in a journal (the migration state key). A rerun resumes where the last one stopped; rollback undoes the steps in reverse.

  1. Import. Projects, services, env, files, domains, certificates and webhook tokens become Tugport rows. Nothing runs yet. Imported services keep no_new_privileges: false so they behave exactly as before.
  2. Databases, one at a time. The old copy stops (Easypanel: scaled to zero; Coolify: stopped) and is gone before Tugport's starts on the same data, so no data is ever written by two engines. Tugport's copy then joins the old panel's networks under the old names (Easypanel's overlays are attachable), so the old apps keep reaching it. Each database is down for a few seconds.
  3. Apps start next to their old copies, from the image the old copy runs. The old proxy still serves traffic.
  4. Services that must never run twice (stop-first, published ports, compose stacks) move like databases, right before the switch A Compose stack that pins a subnet gets it from the stopped old stack, whose network is removed (and created again on rollback).
  5. Switch. The old proxy stops (easypanel-traefik scaled to zero, or coolify-proxy stopped) and Tugport's proxy binds ports 80 and 443 within half a second. If it does not answer within 30 seconds, the old proxy starts again and the move stops. Then the old apps and the old panel stop, Tugport serves the API on 0.0.0.0:3000 for Easypanel's webhook URLs, and Tugport's copies leave the old networks.

Old containers and swarm services are stopped, never removed, and Tugport's image cleanup leaves their images alone, so rollback can start them again: old apps start next to Tugport's copies, Tugport stops so the old proxy gets ports 80 and 443 back, databases move back one at a time (Tugport's copy first), the old panel starts, and the imported rows are deleted. Volumes are never deleted.