I moved my whole server off Easypanel with one command, and five things broke A developer moved a production Hetzner server running 14 projects and 57 services from Easypanel to Tugport, a self-hosted deploy panel they built, using a single `tugport migrate easypanel` command. The migration completed with 54 services running and surfaced five bugs, including unexpanded Easypanel environment variables, imported wildcard health checks, and database health checks that timed out under load; the first three were fixed and released the next day. The developer also pinned all database images after a floating `mysql:9` tag silently upgraded MySQL and took a blog down for three minutes. On October 24 I moved my main server from Easypanel to Tugport https://tugport.com , the deploy panel I've been building. It's the Hetzner box that runs most of what I make: Telegram bots, my Forgejo and its CI runner, two Ghost blogs including this one , a game server, and the Postgres, MySQL and Mongo databases behind all of them. That's 14 projects and 57 services, and the move was one command. Five things broke along the way, and I'll go through each of them below. Tugport is a single Rust binary that runs next to Docker on a server you own. It builds every git push, switches traffic once the new version is healthy, serves it over HTTPS, and keeps logs, charts and backups. AI agents get their own scoped API keys instead of root. It's MIT-licensed, and moving off Easypanel or Coolify is built in, which is the part this post is about. First the dry run, which changes nothing and never prints secrets: tugport migrate easypanel --dry-run It prints every service, how it maps, what needs attention and what won't move. Then the real thing: tugport migrate easypanel Here's what happens under the hood, in order: Every step goes into a journal, so a rerun resumes where the last one stopped, and tugport migrate rollback undoes it in reverse. Volumes and bind mounts stay where they are, so no data is copied, and certificates are imported rather than re-issued. When it finished, 54 services were running on Tugport. The other three had already been scaled to zero in Easypanel, and they came over stopped. This was the first production server the migrator moved, and it found five bugs: $ PROJECT NAME and $ PRIMARY DOMAIN in their environment, and Tugport passed them through as text. I set them by hand that night, and the migrator now fills them in. .easypanel.host address. Tugport imported those too, so a bot with no web server got a health check waiting on port 80 forever. The migrator skips them now. The first three were fixed and released the next day. The next night the panel lit up with "unhealthy" databases. Load on the server hit 99: four CI jobs were running at once, and Voicy was doing its first build on Tugport, which took about 20 minutes. The database health checks ran every 5 seconds with a 5-second timeout, so they timed out under that load. Nothing restarted, because health checks only mark a service, but I didn't love seeing a wall of red. Databases are now checked every 30 seconds with a 15-second timeout. Then I made my own mistake. Redeploying the databases to pick up the new health checks pulled the floating mysql:9 tag, and this blog's MySQL quietly went from 9.7.0 to 9.7.4. The blog was down for about three minutes while it upgraded itself. It upgraded cleanly, but now every database image on the server is pinned to an exact version, and the migrator pins moved databases to the exact image they ran under Easypanel. Pin your database images. Once everything had run on Tugport for a day, I removed Easypanel for good: 63 swarm services, its networks and its stopped containers. Pruning unused images and build cache freed 21 GB of disk. Easypanel's backup schedules don't move, so I set up new ones in Tugport: every night, restic backs up 12 databases, 15 volumes and a handful of host folders to S3 Glacier Deep Archive, Amazon's cheapest storage class. The first runs all passed, and so did the test restores. Deploys now come straight from my Forgejo. Tugport added signed webhooks to 24 repositories, and it honors watch paths, so a service in a monorepo only rebuilds when its own files change. Putting skip deploy in a commit message skips the deploy. The migrator doesn't move everything, and the dry run lists what it leaves behind: backup schedules, users, notification channels, Easypanel's Box and WordPress services, wildcard domains, and hand-written Traefik routes. If your setup leans on those, plan for them. Tugport is also young. It has a handful of GitHub stars, and most of the bugs I've fixed so far were found on my own server. Since the move I've shipped twenty releases, and many of them fix things that only a real server full of services could find. That's the honest state of it. If you run Easypanel or Coolify, install Tugport and run tugport migrate easypanel --dry-run or tugport migrate coolify --dry-run . It reads the old panel and prints the plan without changing anything. The migration guide https://tugport.com/docs/migration/ covers what maps to what, and if you'd rather hand the whole move to an AI agent, point it at tugport.com/agents.md https://tugport.com/agents.md . The source is on GitHub https://github.com/backmeupplz/tugport .