Migrate from Coolify, Dokploy, or Dokku
Move an existing self-hosted server onto Synq without redeploying from scratch — Synq scans your running Docker containers and adopts them in place, with a zero-downtime path and server-to-server moves.
Already running apps on Coolify, Dokploy, or Dokku? You don't rebuild them on Synq. Synq scans the Docker containers already running on your server and adopts them in place — reusing the exact image that's live right now, with no rebuild and, on the same server, no data move. It's a guided, one-click import, and your workloads keep serving while it happens.
The one idea that makes this different
Every other platform here only deploys new workloads. Synq can point at a server's existing
running containers, read their domains and volumes, and take them over — including taking over the
:80/:443 reverse proxy from Coolify/Dokploy/Dokku, rollback-safe.
How the import works
Synq models the move as a short, reversible pipeline:
- Scan (read-only — nothing is changed) — over SSH, Synq enumerates all containers, volumes, and networks on the server (not just ones it owns), reads their compose config, and joins your existing proxy's vhosts to each container by published port to recover its current domains + SSL.
- Adopt (records only) — it creates a
servicesproject mirroring the running containers. Each adopted service carries the container's current image and no build context, so Synq re-runs exactly what's live and never rebuilds. Services come in unexposed, so routing is untouched at import time. - Take over the edge (later, opt-in, rollback-safe) — when you add a domain, Synq journals your old proxy's state, disables it, installs its OpenResty edge, and re-registers your vhosts + certs. If anything fails — or the box reboots mid-way — it rolls back automatically.
- Cut over (opt-in, confirmed) — only when you say so does it stop and remove the old containers. Your source volumes are never touched.
Before you start
Keep your old proxy running for the scan
Don't stop Coolify/Dokploy/Dokku's proxy before scanning — Synq reads your live domains and certificates from it. You'll retire it later, and Synq does that step for you (and can roll it back).
- Migration is self-hosted only and needs
server:writeon the source (and target, for server-to-server). - Connect the source server to Synq over SSH first (Servers → Add server). Your apps and their proxy stay running throughout.
- You can safely stop the old platform's control-plane app at any time — your running app containers and their proxy don't depend on it. Leave the proxy up until the takeover step.
Migrate a server (step by step)
Connect the server and scan
Add the server over SSH, then start the import. Synq scans it read-only and lists what it found — each container with its recovered domain(s), ports, and volumes. (On a large or slow server, use the streaming scan so the client doesn't time out.)
Pick workloads (3-step wizard)
The wizard walks select → link source → domains. Your reverse proxy (Traefik / nginx / Caddy / …) is automatically excluded from the workload set and left running untouched — Synq never adopts or blind-stops it during import.
Review the detected proxy and discovery notices before proceeding. Traefik routes are matched by the container's private network address as well as published ports, so services do not need a host port to keep their domains. Imported environment values are saved in each service's Environment tab. With reuse in place, edits are saved for the next environment reapply or redeploy; the existing container keeps running with its current values.
Choose a volume strategy — this decides downtime
This is the important choice:
- Reuse in place (same server, default) → attach-live: the running container is adopted as-is, its records rebuilt from the live container, the current image reused. No move, no recreation. → zero downtime.
- Copy, or any server-to-server move → deploy path: originals are stopped for a consistent volume copy, then a fresh deploy is verified to "ready." → a real downtime window (polled up to ~20 min).
Trust the strategy, not the banner
The preview may conservatively warn about "downtime" even for the zero-downtime reuse case. The reuse in-place strategy is genuinely zero-downtime for the workload — the container is never stopped.
Take over the domains + edge
When the migration includes domains, Synq prepares the target edge during migration. It shows which
existing sites belong to the selected project and services, then asks for confirmation before replacing
the proxy. Choosing Migrate sites & take over journals the old configuration, installs OpenResty on
:80/:443, and imports the existing sites and certificates, including sites outside the selected project.
If takeover fails, Synq attempts to restore the previous proxy and reports the recovery outcome.
Selected domains are attached to their migrated services and re-applied from the live containers. Existing certificates are reused and pending verification is retried immediately. Routes that cannot be reproduced are reported in discovery notices; review those before taking over. If routing or TLS still needs attention, the migration keeps a warning and does not automatically remove the old deploy-set containers.
The only brief interruption
With reuse in place, your containers keep running. Requests can be interrupted briefly while :80/:443
moves from the old proxy to OpenResty. Copy and cross-server migrations also have the downtime described above.
Verify, then cut over and remove the old platform
Confirm everything serves through Synq. Then run cutover (explicit, token-confirmed): it stops and removes only the old deploy-set containers — never your source volumes. Once you're happy, remove Coolify/Dokploy/Dokku entirely; Synq now owns the box.
Verify before you cut over
Cutover stops and removes the old workload, so don't run it until the Synq-managed app is fully healthy:
it loads and serves traffic, your data is intact (sign in, open a record, check volumes/uploads and any
database), and every service is RUNNING. Cutover leaves your source volumes untouched, so the old
deploy-set stays as a fallback until you're certain. If anything's off — missing data, a service that won't
start, routing/TLS wrong — don't cut over; leave the old containers running and
check the troubleshooting guide.
What an adopted service keeps from its container
A later redeploy recreates each container under a Synq name (synq-<project>-<service>). So that the
containers still find each other and the host the same way, adoption reads the running container and
stores how it is wired. The next deploy recreates exactly that:
| Wiring | What is kept |
|---|---|
| External networks | A network created outside the compose project (docker network create app_net, declared external: true) is joined by name, not flattened. On it the container keeps its old container name, its compose service name and every alias it had, spelled exactly as before (underscores included). Synq never creates, changes or removes an external network; a deploy fails before touching the running container if the network is missing. |
| Project networks | The compose project's own networks (<project>_default and other networks it created) become the one Synq project network. There each service answers to its service name and, exactly as before, to its old container name (for example a compose container_name). |
| Extra hosts | extra_hosts entries, including host.docker.internal:host-gateway. |
| Host ports | A publish pinned to one interface, such as 127.0.0.1:6379:6379, is kept exactly, so host processes keep reaching the app. Publishes on all interfaces (5432:5432, 0.0.0.0:5432:5432) are dropped to the container port, as are 80/443 and ports in Synq's reserved ranges; the migration log names each dropped publish. |
| Stop grace period | The container's StopTimeout (compose stop_grace_period). A redeploy stops the old container with it and waits for it before starting the new one. |
| Volumes | Named volumes are reused by their exact names (not renamed per project) with reuse in place. |
| Environment | Values the operator set, saved in the service's Environment tab. |
Every service is one container; a compose profile such as worker becomes its own service with one replica.
Open a service's Settings → Networking → Host networking to check the external networks, aliases, extra
hosts, stop grace period and volume naming before the first redeploy. The migration run log lists the same
per service. To change them, send advanced.externalNetworks, advanced.networkAliases,
advanced.extraHosts or advanced.stopGracePeriod to the service update API.
Server-to-server migration
Moving a whole stack from one host to another is the same flow, with the data streamed across:
- Named volumes and movable app-data mounts stream A → B directly (same daemon: a
tar → tarcopy; across daemons: a gzip stream). System paths (/proc,/sys,/var/lib/docker, sockets, …) are never copied. - Synq refuses to overwrite a non-empty same-named volume on the target, so you can't clobber data.
- External networks exist only on the source, so on the target those services join the project network instead, keeping the names they had there, with a warning naming each network. Anything else on that network (a host process on its gateway, containers outside the migration) is not reachable that way: create the network on the target and add it to the service.
- Server-to-server always uses the deploy path (a downtime window), and it can't move locally-built images — those have no registry to pull from on B, so take them over in place on their current server instead.
Why the architecture is different
One thing is worth understanding, because it's where Synq genuinely diverges from the others:
- A programmable OpenResty edge. Routing runs on OpenResty with a database-driven, reload-free per-route rules engine — per-route rate limiting, IP/CIDR + country + user-agent bans, method/CIDR/country allow-lists, and hotlink protection — pushed live into a shared dictionary with no nginx reload and no Redis, plus built-in request/bandwidth analytics and a live log stream. Coolify and Dokploy expose Traefik middlewares and Dokku exposes nginx templates that can approximate these rules; Synq's difference is that it's a managed, reload-free, per-route surface — not that it does more at the protocol level (see the honest caveats below).
Honest comparison
All four are open-source, Docker-based, self-hosted PaaS tools, and they share most of the baseline. We're not going to pretend otherwise — migrate for the differences, not the essentials.
Shared essentials (all four do these): git-push / webhook auto-deploy · Dockerfile builds · automatic Let's Encrypt TLS with auto-renew · one-click databases · scheduled S3 backups · multi-server over SSH · REST API + scoped tokens · a CLI · rollbacks · per-event notifications · and an official MCP server (Coolify's is read-only, Dokploy's covers the full API, Synq's is write-capable). "Has an MCP server" is not a reason to switch.
The full picture — differentiators in bold; ✓ built-in · ~ partial / manual / paid · ✗ not
available. Where a competitor leads (Coolify's 280+ catalog, Dokploy's HTTP/3, Dokku's zero-downtime default,
buildpack breadth), it's marked honestly.
Deploys & builds
| Coolify | Dokploy | Dokku | Synq | |
|---|---|---|---|---|
| Git push / webhook auto-deploy | ✓ | ✓ | ✓ | ✓ |
| Dockerfile & prebuilt-image deploy | ✓ | ✓ | ✓ | ✓ |
| Docker Compose / multi-service | ✓ | ✓ | ✗ | ✓ |
| Buildpacks (Nixpacks / Heroku) | ✓ | ✓ | ✓ | ~ auto stack-detect |
| Rollback to a previous release | ✓ | ✓ | ✓ | ✓ |
| Per-PR preview environments | ✓ | ✓ | ~ | ~ |
| Zero-downtime new deploys | ~ health-gated | ~ health-gated | ✓ default | ~ health-gated |
Servers & migration
| Coolify | Dokploy | Dokku | Synq | |
|---|---|---|---|---|
| Multi-server over SSH | ✓ | ✓ | ~ K3s | ✓ |
| Server health / resource monitoring | ~ | ✓ | ~ plugin | ✓ |
| Fine-grained server access grants | ✗ | ~ ent. | ✗ | ✓ |
| Adopt existing running containers | ✗ | ✗ | ✗ | ✓ |
| Server-to-server move (with data) | ✗ | ✗ | ✗ | ✓ |
| Leave management, keep apps + data running (re-importable) | ✗ delete prunes managed containers | ✗ delete tears down | ✗ apps:destroy removes | ✓ record-only, re-adoptable |
| Take over an existing reverse proxy | ✗ | ✗ | ✗ | ✓ |
Networking & edge
| Coolify | Dokploy | Dokku | Synq | |
|---|---|---|---|---|
| Automatic Let's Encrypt TLS | ✓ | ✓ | ✓ | ✓ |
| Unlimited custom domains + wildcards | ✓ | ✓ | ✓ | ✓ |
| HTTP/3 (QUIC) | ✗ | ✓ | ✗ | ✗ |
| CDN / response caching | ✗ | ✗ | ✗ | ✗ |
| Per-route rate limiting | ~ Traefik mw | ~ Traefik mw | ~ nginx | ✓ UI, reload-free |
| IP / CIDR ban + allow-lists | ~ mw | ~ mw | ~ nginx | ✓ UI |
| Country (geo) blocking | ✗ | ✗ | ~ GeoIP module | **✓ built-in ** |
| Bad-user-agent + hotlink protection | ~ mw | ~ mw | ~ nginx | ✓ UI |
| Built-in request analytics (traffic, status codes, top paths, geo) | ✗ add-on | ✗ add-on | ✗ | ✓ |
| Live per-route request-log stream | ~ proxy logs | ~ proxy logs | ~ files | ✓ |
| Resource metrics (CPU / memory / bandwidth) | ~ basic | ✓ | ~ plugin | ✓ |
The edge is where the gap is widest: every route gets per-route rate limiting, IP/CIDR + country + user-agent bans, and hotlink protection from a UI (pushed live, no nginx reload), plus built-in traffic analytics — requests, status codes, top paths, and geo/country breakdowns — and a live per-route request-log stream. Coolify and Dokploy give you Traefik middlewares and bolt-on Grafana/Plausible; native country-level blocking and built-in request analytics aren't there. (Honestly: it's not a CDN or WAF — no HTTP/3, caching, or OWASP rules.)
Data, team & platform
| Coolify | Dokploy | Dokku | Synq | |
|---|---|---|---|---|
| One-click databases | ✓ 8 | ✓ 6 | ✓ 16 | ✓ catalog |
| Scheduled S3 backups + restore | ✓ | ✓ | ~ per-DB | ✓ |
| Volume backups | ~ | ✓ | ~ | ✓ |
| One-click app catalog | ✓ 280+ | ✓ | ~ community | ✓ curated |
| Teams / organizations | ✓ | ✓ | ~ Pro | ✓ |
| Fine-grained RBAC + audit log | ~ coarse | ~ ent. | ✗ | ✓ |
| Scoped API tokens (PAT) + CLI | ✓ | ✓ | ~ Pro | ✓ |
| Scheduled jobs / cron | ✓ | ✓ | ~ plugin | ✓ |
| Web dashboard | ✓ | ✓ | ✗ Pro | ✓ |
| MCP for AI agents | ✓ read-only | ✓ full-API | ~ community | ✓ write + re-auth |
| Built-in full mail server | ✗ | ✗ | ~ community | ✓ |
The defensible reasons to move are the bold rows: adopting what's already running, moving stacks between servers with their data, leaving without lock-in (a record-only removal drops just Synq's record and leaves your apps + data running on your box, re-importable later), taking over your existing proxy, the programmable edge, and the built-in mail server. None of the other three can adopt a running stack, move one with its data, or hand it back to you still running — every "migration" is a manual rsync-and-redeploy or a community script.
Honest caveats
- Zero-downtime is narrow, not blanket. Only a same-server "reuse in place" import is truly
zero-downtime. Same-server "copy" and every server-to-server move stop the originals for a consistent
copy and verify a fresh deploy (up to ~20 min) — a real window. The edge takeover briefly interrupts
:80/:443while OpenResty installs. - The edge is programmable access rules + analytics — not a CDN and not a WAF. There's no HTTP/3, no brotli, and no response caching layer; the rules are a built-in ruleset (bans / allow-deny / hotlink / method / rate-limit), not ModSecurity/OWASP CRS.
- The mail server is a real mail server. It's a genuine Postfix + Dovecot stack — so DNS/DKIM/SPF setup, deliverability, and ongoing mail ops are yours to run. The others sidestep this by not offering mail at all.
- Catalog breadth isn't the pitch. Coolify's 280+ one-click services is the largest catalog here; Synq leads on the migration and edge story, not template count.
Next steps
Self-hosted GitHub Apps
Create and manage workspace-owned GitHub Apps for private repositories, direct webhooks, and short-lived clone tokens.
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.