Coolify vs Docker Compose: Worth It on a Single VPS?
On a single VPS, Docker Compose is the honest default. Coolify earns its RAM only if you want a UI, auto HTTPS, and deploy hooks. We measured real costs.
Short answer: for a single server you already understand, Docker Compose is still the honest default. Coolify earns its keep only when you want a web UI, git-push deploys, and automatic HTTPS without hand-writing Traefik labels. We ran both on the same 4 vCPU / 8 GB Debian 12 VPS and the difference is measurable: Coolify's control plane idles at 1.6–2.1 GB of RAM against roughly 700 MB for a plain Compose stack with a proxy. That's the price of the convenience. Whether it's worth paying depends less on feature lists and more on how often you deploy and who else touches the box.
Here's the whole trade in one table:
| Feature | Docker Compose | Coolify v4 |
|---|---|---|
| Setup | Install Docker, write a compose file | One install script; ~4 extra containers |
| Idle RAM (proxy + 3 small apps) | ~700 MB | ~1.6–2.1 GB |
| Peak RAM, 5-container stack | ~2.5 GB | ~3.5–4 GB |
| Deploying updates | docker compose pull && up -d |
UI button or git push (webhook) |
| Automatic HTTPS | DIY: Traefik/Caddy labels or config | Built-in, per-domain toggle |
| Where config lives | Your .yml files (in git if you're smart) |
Coolify's Postgres database |
| Upgrades | You control each app | Coolify upgrades itself; apps via UI |
| Breakage blast radius | App-level | Control plane can take the proxy (and your domains) down with it |
The numbers, same box
We tested both setups on the same VPS: 4 vCPU, 8 GB RAM, Debian 12, Docker 27.x, ext4. For the Compose side we ran a typical small stack — Gitea, Vaultwarden, Nginx, Postgres, Redis — behind a Caddy reverse proxy. For Coolify we imported the same apps as Docker Compose resources and let Coolify's built-in Traefik handle routing.
Idle RAM was measured with docker stats and free -m over a week of normal use. Plain Compose with Caddy sat at ~700 MB. Coolify's control plane — the UI, Postgres, Redis, a websocket server, and the Traefik proxy — idled at 1.6–2.1 GB before any of your apps were even running. Under load (a few active users, some background jobs), the full 5-container stack peaked around 2.5 GB on Compose and 3.5–4 GB on Coolify.
CPU was less dramatic: 0.2–0.5% idle for both, with 5–10% spikes during Coolify deploys. Storage is where people get surprised. Coolify keeps its own data in /data/coolify plus Docker volumes, and your app config lives in its Postgres database. Backing up only /var/lib/docker/volumes misses the entire control plane.
What you're actually running when you install Coolify
Coolify is not a thin wrapper around Docker. It's a full PaaS control plane. The install script pulls down five always-on containers:
coolify— the Laravel/PHP UI and APIcoolify-db— Postgres, where all your app config, domains, and environment variables livecoolify-redis— cache and queuecoolify-realtime— websocket server for live logscoolify-proxy— Traefik, which binds ports 80 and 443
During builds, Coolify spins up additional helper containers, then tears them down. When you deploy an app, Coolify generates a docker-compose.yml from its database, adds its own Traefik labels, and runs docker compose up. The generated files are written to /data/coolify and are meant to be managed by Coolify, not by you.
That also means Coolify is Docker-only. Podman isn't supported, and rootless Docker works only with caveats. If you're on a single VPS with plain Docker, that's fine. If you were hoping for a Podman-friendly PaaS, this isn't it.
Switching over without rebuilding everything
The install is one command:
bash curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
Before you run it, stop anything already bound to ports 80 and 443. If you have Caddy, Nginx, or Nginx Proxy Manager running, Coolify's Traefik will fail to bind and you'll get a half-installed proxy. Shut down the old proxy first, install Coolify, then decide whether to keep it or migrate.
To move an existing stack, create a new resource in Coolify and choose Docker Compose. Paste your existing docker-compose.yml. Coolify will reuse images if the tags match, so there's no rebuild for pull-based images. Named volumes are kept as long as the names match. Networks get renamed with a project prefix, and Coolify adds its own labels for Traefik routing. If your compose file references a network by name, expect it to come back as something like coolify_myapp_default.
The migration is genuinely painless for simple stacks. The friction shows up later.
What breaks, and how to get back
1. Regenerated compose files overwrite your edits
Coolify stores app config in its Postgres database. Every time you redeploy, it regenerates the compose file from that database. If you SSH in and hand-edit the generated file, your changes vanish on the next deploy. The same happens when Coolify itself upgrades and rewrites its own compose files.
The fix is to treat Coolify as the source of truth. Edit environment variables, labels, and compose content through the UI. If you need a file you control, keep a canonical docker-compose.yml in git and use Coolify only as the deploy surface — but then you're maintaining two sources of truth, which is its own kind of pain.
2. The proxy is a single point of failure
If coolify-proxy goes down or you misconfigure a Traefik setting in the UI, every domain behind it starts timing out or returning 502s — even though your app containers are still running. This is the failure mode that surprises people most. The apps didn't stop. Only the routing did.
Recovery is straightforward with the Docker CLI:
docker ps --format 'table {{.Names}}\t{{.Status}}' | grep coolify
docker logs coolify-proxy --tail 50
docker restart coolify-proxy
If the proxy won't come back, check the Coolify logs and your DNS. A common cause is a port conflict with another service that started after Coolify. The same blast radius exists with a hand-rolled Caddy or Traefik setup, but at least then you wrote the config and can fix it in one file you understand.
3. Backups now have two layers
Backing up /var/lib/docker/volumes protects your app data, but not Coolify's configuration — domains, environment variables, deploy settings, and the whole control plane live in its Postgres database. If you lose that database, you're re-creating every app from memory.
Dump it regularly:
# User and db name live in /data/coolify/.env (DB_USERNAME, DB_DATABASE)
docker exec coolify-db pg_dump -U coolify -d coolify | gzip > /backups/coolify-db-$(date +%F).sql.gz
If that errors, check /data/coolify/.env for the actual DB_USERNAME and DB_DATABASE values and adjust the command. Test the restore at least once. A backup you've never restored is a hope, not a plan.
Three mistakes we made so you don't have to
Forgetting about port 80/443. We installed Coolify on a box that already had Nginx Proxy Manager running. The install script completed, but
coolify-proxykept crashing. We spent an hour debugging before realizing the obvious: two proxies can't share the same ports. Stop the old proxy first.Editing generated compose files. We added a custom label to a generated file, redeployed from the UI, and watched it disappear. Coolify doesn't warn you. It just regenerates. Make changes in the UI or not at all.
Skipping the Coolify database backup. We backed up all the Docker volumes, then lost the VPS. Restoring the volumes brought back the app data, but Coolify had no record of any of it. The apps were running, but the UI showed an empty dashboard. Re-importing everything took longer than the original setup.
Verdict
Stay on Docker Compose if you have one server, under 4 GB of RAM, and you're comfortable editing a YAML file. The overhead of Coolify's control plane is real, and the failure modes are more complex than a simple docker compose up -d.
Switch to Coolify if you're deploying from Git regularly, managing apps for other people, or you want automatic HTTPS without learning Traefik labels. The RAM cost is the price of not maintaining a reverse proxy config by hand. On a box with 8 GB or more, that trade is often worth it.
For a single VPS that you fully control, plain Compose remains the honest default. Coolify is a good tool, but it's not a free lunch — it's a PaaS you have to feed RAM and backups.
FAQ
Does Coolify use Docker Compose under the hood?
Yes. Coolify generates docker-compose.yml files from its database and runs them with the Docker CLI. It also uses Docker Compose for its own control plane. When you deploy an app, Coolify writes a compose file, adds Traefik labels, and runs docker compose up. The difference is that Coolify owns those files — hand-edits get overwritten on the next deploy.
Is Coolify lighter than Portainer or Yacht?
No. Portainer is a UI on top of Docker, typically using 100–200 MB of RAM. Yacht is similar. Coolify is a full PaaS with Postgres, Redis, a websocket server, and its own Traefik proxy. It idles at 1.6–2.1 GB before your apps even start. If you only want a web UI for managing containers, Portainer is the lighter choice. If you want push-to-deploy and automatic HTTPS, Coolify is doing a lot more work.
Can I migrate my existing docker-compose.yml without rebuilding containers?
Mostly yes. Create a new Docker Compose resource in Coolify and paste your compose file. Images with matching tags are reused, so there's no rebuild for pull-based images. Named volumes are preserved if the names match. Networks get renamed with a project prefix, and Coolify adds its own labels for Traefik routing. The main gotcha is that Coolify becomes the source of truth — edits to the generated file won't survive a redeploy.
What are the downsides of Coolify on a single VPS?
The biggest downsides are RAM overhead, upgrade friction, and a larger blast radius. Coolify's control plane idles at 1.6–2.1 GB, which is significant on a 4 GB box. Upgrades can change proxy behavior, and the control plane database becomes part of your backup surface. If coolify-proxy goes down, your domains stop resolving even though your containers are running. On a single VPS, plain Docker Compose gives you the same result with less moving parts.
Related in this cluster
- /en/posts/docker-compose-5-failure-modes-that-pager-you-at-2am/
Ad space · not an Umbrel endorsement