I get the Kamal vs Coolify question because both solve the same problem: “I want to deploy to my own server without building a mini Heroku.”

I use Kamal for my SaaS apps because I want deployment to be a command in the repo, not another app I have to run.

Coolify is still good. I just want different tradeoffs.

What Kamal gives me

Kamal is a CLI deploy tool. It reads config/deploy.yml, builds a Docker image, pushes it to a registry, SSHs into the server, starts the new container, waits for health checks, and swaps traffic through kamal-proxy.

My day-to-day command is still:

kamal deploy

Most of my workflow now runs through coding agents. I can tell an agent:

Run tests, deploy with Kamal, then check the GitHub Pages or app health endpoint.

The deploy path, config, and error output are all text. The agent can read them without a special integration.

Kamal fits my solo SaaS codebases because it uses the tools I already use: git, Docker, SSH, tmux, scripts, and AGENTS.md.

What Coolify gives you

Coolify is closer to a self-hosted PaaS.

You get:

  • a web dashboard
  • app creation flows
  • environment variable management
  • service templates
  • Git-based deploys
  • logs in the UI
  • databases and supporting services
  • an API for automation

That is useful if you want a platform experience on your own VPS.

It is also useful if you have people deploying who are not comfortable editing YAML and running CLI commands. A dashboard is easier to teach.

The tradeoff: Coolify itself becomes part of the system. You run your app, your database, and the deployment platform.

That can be fine. For me, it is one more moving part than I want.

Where deployment state belongs

With Kamal, the important deployment decisions are in the repo:

service: myapp
image: ghcr.io/me/myapp

servers:
  web:
    hosts:
      - 123.45.67.89

proxy:
  ssl: true
  host: myapp.com
  app_port: 3000
  healthcheck:
    path: /health

accessories:
  db:
    image: postgres:16
    host: 123.45.67.89
    directories:
      - data:/var/lib/postgresql/data

That file can be reviewed, copied, diffed, and changed by an agent.

With Coolify, more of the deployment state is in the platform. PaaS-style tools work that way. You get a nicer UI, but the deploy system is less like the rest of my codebase.

For a team, that might be worth it.

I want that config in git.

What the agent can inspect

This is the part that matters once coding agents are in the loop.

When a Kamal deploy fails, the agent can inspect the same things I inspect:

git diff config/deploy.yml
kamal details
kamal app logs --lines 200
kamal proxy logs --lines 200
kamal accessory logs db --lines 200

The agent can compare the deploy config with the Dockerfile, check whether the health endpoint exists, see whether a secret is missing, and make a normal code change.

That gives me a clean feedback loop. The deployment tool writes output to the terminal, the agent reads it, and the fix usually lives in the repo.

With a dashboard tool, some of the state is still inspectable through logs, config files, and APIs, but the platform becomes part of the process. If the fix is “change this setting in Coolify”, I need that setting to be scriptable or I have to do it myself. I avoid that when I can.

The common failure modes are boring

Most deploy problems I hit are not exotic.

They are things like:

  • the Docker image builds, but the app exits because an env var is missing
  • the health check path returns 404
  • the app listens on localhost instead of 0.0.0.0
  • the database accessory is up, but the app has the wrong DATABASE_URL
  • a Vite build-time env var was only provided at runtime
  • the server is out of disk space because old images were not pruned

Kamal does not make these disappear. It just keeps the problem close to the code.

For example, if the health check fails, I can point an agent at the output:

Kamal deploy failed on the health check. Check the app port, health route,
and container logs. Keep the fix scoped.

The prompt gives the agent enough context for a useful debugging pass. It can read config/deploy.yml, find the Fastify route, check the Dockerfile, and run the app locally if needed.

The same thing works for secrets. If a deploy fails because STRIPE_WEBHOOK_SECRET is missing, the agent can update the deploy config or tell me the secret needs to be added. It should not print the secret, and my AGENTS.md says that.

Kamal works well with repo instructions here. The deploy tool stays simple, and the guardrails are in text.

Why I picked Kamal for Stacknaut

Stacknaut is a one-server SaaS starter kit. It ships with Vue, Fastify, PostgreSQL, Docker, Terraform, and Kamal.

I care that the deployment flow is part of the starter kit and part of the agent workflow.

A coding agent can:

  • edit app code
  • update shared types
  • run checks
  • inspect config/deploy.yml
  • deploy with Kamal
  • read deploy errors
  • fix the problem
  • deploy again

The agent does not need browser automation or instructions like “go to this page and press the deploy button.”

That is the main reason I prefer Kamal.

When I would choose Coolify

I would choose Coolify if:

  • I wanted a self-hosted dashboard for multiple small apps
  • I was deploying projects that do not share a common repo pattern
  • I wanted templates for databases and services
  • I had non-CLI users deploying apps
  • I wanted Git push deploys managed by the platform
  • I wanted a deployment API around the platform

Coolify is a better fit when you want the dashboard to be the thing you run.

When I would choose Kamal

I choose Kamal when:

  • the app already has a Dockerfile
  • the team is comfortable with CLI deploys
  • deployment config should stay in git
  • I want a coding agent to reason through deploy failures
  • I want the server to stay simple
  • I do not need a platform UI

For solo SaaS work, this is my default.

My rule

If I want a self-hosted Heroku-like dashboard, I would use Coolify.

If I want a boring deploy command that fits a codebase and an agent workflow, I use Kamal.

Stacknaut uses Kamal for this reason.

What I would still borrow from Coolify

Coolify has nice ideas. I would not mind better visibility around running services, env vars, and logs.

I handle that with smaller tools:

  • Better Stack for logs
  • uptime checks for public health endpoints
  • Telegram notifications for server jobs
  • tmux windows for deploys I want to watch
  • Kamal commands for app and accessory status

It is less polished than a dashboard. Fair enough. But I can swap any piece out without changing the deploy model.

The missing bit is usually a nice status page for everything on the server. I get close enough with commands:

kamal details
kamal accessory details
docker ps
df -h

Those are not pretty, but they are scriptable. An agent can run them, paste the important lines into the session, and decide whether the problem is app health, disk space, a dead accessory, or something else.

Coolify bundles more of that into one interface. I see the appeal. If I were running a small internal platform for other people, I would take that more seriously.

For my own projects, the command-line version wins because it is easier to automate. I can put the exact commands in AGENTS.md, a skill, or a script. The workflow survives if I change terminals, editors, or coding agents.

It also keeps onboarding simple. A new project has the same deploy file layout, the same commands, and the same failure paths as the last project. I do not need to remember which dashboard setting I clicked six months ago, or explain it again to an agent.

For my own apps, that is the tradeoff I want.