A cmdz.toml, if you wrote one
Explicit beats implicit, always. Anything you set in cmdz.toml — runtime version, install command, build command, start command, extra Nix packages, cache paths — overrides everything below it.
How it works
You never write a Dockerfile, a manifest or a pipeline unless you want to. The platform reads your source, works out what it is, builds it, and puts it in its own virtual machine behind HTTPS.
The chain
We read your repository: lockfiles, manifests, framework config. That produces a build plan — runtime version, package manager, install command, build command, start command — which you can see and override in a cmdz.toml if the detection got something wrong.
A sandboxed builder turns the plan into an image, with a warm dependency cache from your previous build. Services your app needs — Postgres, Valkey, object storage — are provisioned in parallel, because that work does not depend on the image.
The image starts in a Kata micro-VM with its own kernel. A startup probe polls until the app answers, and only then does traffic move over. If it never answers, the old version keeps serving and the deploy is marked failed.
$ cmdz deploy
queue accepted 0.4s
detect next@15 · pnpm@9 · node 22 · prisma → postgres
from package.json, pnpm-lock.yaml, next.config.mjs
build nixpacks plan → layers → image 38.2s
cache hit: node_modules (412 MB)
scan no critical CVEs 2.1s
provision postgres-16 small · nl-rtm-1 14.0s (parallel)
release kata micro-VM · 2 replicas · rolling 21.6s
health startupProbe ok after 3.0s
✓ live https://linktree.cmdz.app 92.1s
DATABASE_URL injected · rollback: cmdz rollback --to 41
Ninety seconds is the target for a first deploy of a small Next.js app. A second deploy of the same app is usually well under thirty, because the dependency cache is already warm.
Detection
Detection runs on your source, in this order. The first thing that matches wins, and you can always override it.
Explicit beats implicit, always. Anything you set in cmdz.toml — runtime version, install command, build command, start command, extra Nix packages, cache paths — overrides everything below it.
If your repository has a Dockerfile we use it and stay out of the way. This is the escape hatch for anything unusual, and it is a first-class path rather than a fallback.
We recognise Next.js, Nuxt, SvelteKit, Astro, Remix, Laravel, Symfony, Django, FastAPI, Flask, Rails, Express, NestJS, Go, Rust and more, along with the package manager and the runtime version you pinned.
The build fails with a detect_failed error that names what it looked for and what it found, plus the three-line cmdz.toml that would fix it. It does not guess and it does not silently deploy something wrong.
Isolation
Most platforms in this category run customer code in containers that share one kernel. That is efficient and it is fine right up until a kernel escape, at which point "fine" stops being the word.
We run every workload in a Kata Containers micro-VM: a real virtual machine with its own kernel, started by the same Kubernetes machinery as an ordinary pod. The isolation boundary is the hypervisor, not a namespace.
This matters more than it used to, because a growing share of the code running on platforms like ours was written by a model rather than reviewed by a person. Fly.io reaches the same isolation quality with Firecracker; we are not claiming to be alone in this. What we combine it with — a hard limit and a full agent API — is the part that is ours.
your app
└─ own kernel ← the boundary
└─ kata micro-VM
└─ hypervisor
└─ node (Rocky Linux 10, LUKS at rest)
└─ our rack
elsewhere:
your app
└─ namespace + cgroup
└─ shared kernel ← the boundary
└─ node, with other customers
Around the deploy
Ninety days of deploy history. A rollback re-releases a previous image that already built and already passed its health check, so it is a cutover rather than a rebuild — seconds, not minutes.
Every pull request gets its own URL with its own environment. They go to sleep on their own when nobody visits them, so having many of them costs very little, and they are cleaned up when the branch is.
Rolling releases with a startup probe, so traffic only moves once the new version answers. If it never answers, the old one keeps serving and you get told why.
Adding Postgres, Valkey, object storage or a vector index injects the connection variables into your app. You do not copy a connection string anywhere, and rotating a credential does not require a redeploy.
Scheduled tasks and queue workers are first-class workload types with their own logs and their own metrics, priced the same way as everything else.
A workload that nobody is using can sleep and wake on the first request. For production apps you can set a schedule instead, so the first visitor of the morning never pays the cold start.
The first deploy tells you more than this page can, and it comes out of the € 10 of credit you start with.