yes, another self-hosted PaaS. hear us out.
push your
compose.yaml, get a URL back. every branch gets a preview that borrows or clones your database instead of copying it. bring your compose.yaml
built on Compose, not against it.
you already wrote a compose.yaml. you run it every day. it'd be rude to make you write another one. chaalaa reads it as-is and adds a tiny chaalaa.yaml for the stuff Compose never thought about: environments, previews, borrowing, domains and health checks.
# compose.yaml — the one you already haveservices: api: build: ./services/api expose: ["8080"] environment: DATABASE_URL: postgres://app:${DB_PASSWORD}@db/app depends_on: [db] web: build: ./apps/web expose: ["3000"] db: image: postgres:17 volumes: [data:/var/lib/postgresql/data]volumes: data: {}# chaalaa.yaml — the bits Compose never thought ofversion: 2environments: production: { branch: main, domain: example.com } integration: branch: develop shareable: { db: [borrow, clone] } preview: branch: "*" ephemeral: true base: integration borrow: db: { mode: clone }services: web: { domain: www } api: { health: /healthz } bind mounts, debug ports and hot reload stay in compose.override.yaml — Docker Compose loads it on your laptop, chaalaa pretends it doesn't exist. secrets reach ${VARS} through ssh chaalaa.dev config:set, because git is not a password manager. (we've all seen that commit. we don't talk about that commit.)
previews
every branch gets a stack. your database stays put.
previews usually mean one of two things: a full copy of everything (hello, cloud bill), or pointing at staging and praying nobody runs a migration. chaalaa lets a preview borrow a service from its base, or clone its data in under a second.
clone: copy-on-write snapshot — the preview's mess stays in the previewborrow: one firewall rule and a DNS entry, nothing copied
borrow
the preview talks to the base's running service through one firewall rule and a DNS entry. costs nothing. copies nothing.
clone
the preview gets its own copy, booted off an instant copy-on-write snapshot. drop tables, seed garbage, go wild — the base never finds out.
both sides have to agree
the base marks a service shareable, per mode. so no, a preview can't borrow from production by accident. we checked.
migrations know the difference
release commands run against clones and get skipped against shared services unless you say otherwise. the push output tells you either way.
cleanup
done with a branch? delete it.
one git push tears the whole preview down. the base keeps running like nothing happened.
- one command, whole stacktasks, cloned volumes, DNS, routes and addresses. all of it, gone.
- the base never noticesborrowed services keep running. only the preview's firewall rule and DNS entry go.
- forgot? it expirespreviews clean themselves up 7 days after the last push. tune it per environment.
- production is protecteddeleting a branch that maps to a real environment gets rejected at push time.
built for AI agents
agents push branches. chaalaa gives each one a stack to break.
point your coding agent at ssh chaalaa.dev mcp. it gets a preview per task, real logs and a database it can't hurt. production still waits for you.
- MCP over plain SSHno tokens, no new ports. same keys, same roles, same audit log.
- its own identitysponsored by a human, boxed into agent/* branches, revoked in one command.
- break things safelyevery task gets a preview with a cloned database. reset it between test runs with one call.
- you still ship productionagents can propose a deploy to main. only a human can approve it.
{ "mcpServers": { "chaalaa": { "command": "ssh", "args": ["chaalaa.dev", "mcp"] } }}okay, but why?
owns the layer Docker would've owned.
chaalaa talks to containerd directly and owns everything underneath your services — networking, isolation, DNS and release switches — so they're designed in from day one, not duct-taped on later.
no Docker daemon
chaalaa drives containerd and BuildKit directly. the host gets one agent binary next to containerd and runc. that's it. that's the install.
containerd ✓buildkit ✓nftables ✓dockerd
atomic switches
every service keeps a stable virtual IP. a release repoints every changed service in one kernel transaction, and open connections drain on the old one like nothing happened.
api · 10.201.0.7release r7release r8
its own networking
a bridge and subnet per environment, isolated by default, with egress you can actually reason about.
DNS per environment
db means your environment's db. same name, different answer. no more 2am "wait, which db is this?"
production ❯ db → 10.201.0.4
brave-otter ❯ db → 10.201.3.4
builds and secrets stay on the server
no Docker or credentials on anyone's laptop. config is encrypted at rest and never, ever lives in git.
your compose.yaml, as-is
the file you run locally is the file chaalaa deploys. your local-only hacks stay in compose.override.yaml, and chaalaa politely looks away.
teams, roles and an audit log
owners, developers and viewers per team, team-owned domains, and an audit log for when someone inevitably asks "who changed that?" all over plain SSH.
monorepos
one push. only what changed. all at once.
chaalaa figures out which services a push actually touched, builds shared images once, and switches every changed service together — or none of them. half-deployed is not a state we do.
- deploys what changedbuild contexts and watch globs decide. touch apps/web, rebuild web. your database doesn't need to hear about your CSS.
- targets straight from gitgit push -o deploy=web, or deploy=all when you're feeling dramatic. no extra CLI to install.
- builds onceservices that share a build context and Dockerfile share one image and just run different commands.
- all or nothingevery changed service starts and passes its health check before any of them switch. one fails, nobody moves.
under the hood
one binary on the host. protobuf at every boundary.
install the agent next to containerd. it runs everything else — chaalaa's own services, Traefik and BuildKit — as containers pinned by digest, talking binary protobuf over unix sockets. upgrade the agent and the whole platform follows. yes, protobuf. no, not JSON. we've been over this.
agent
the one thing you install. runs containers on containerd, owns bridges, nftables, DNS and volumes.
gateway
the SSH server, git receive-pack, hooks, every command, and a terminal dashboard.
deployer
queue, change detection, plans — and deploys run step by step, so a crash resumes instead of panicking.
apps
apps, environments, previews and releases. keeper of the manifest.
builder
Dockerfile builds on BuildKit's containerd worker, straight into the image store. no registry round trip.
secrets
encrypted config with scopes. previews inherit from their base, like a trust fund.
ingress
hostnames, team domains and certificates, served through Traefik.
identity
users, SSH keys, teams, roles, and an audit log that remembers everything.
cluster
nodes, address pools and placement. one node today, more when it earns them.
honest fit
is chaalaa for you?
there are good tools out there, and pretending otherwise would be weird. here's where chaalaa shines, and where you'll be happier somewhere else.
chaalaa's your thing if
- you run a handful of services and at least one expensive database or search cluster.
- you want a preview per branch without paying for a whole stack per branch.
- your code lives in a monorepo and you're tired of rebuilding the universe for a typo.
- you'd rather git push and ssh than click around a dashboard.
look elsewhere if
- you need lots of machines today — Kamal or Uncloud are built for that.
- you want a web dashboard to click through — Coolify or Dokploy will make you happier.
- you need something battle-tested right now — Dokku has a decade of plugins (and scars).
- you want automatic scheduling and failover — that's Kubernetes. go with god.
roadmap
where it stands
chaalaa is being rewritten from a 2021 proof of concept that was, let's say, a learning experience. the design's done; the build starts at the foundation and works up from the agent.
M0
foundation
monorepo, protobuf contracts, tooling, CI, the release pipeline. the boring bit that makes everything else less boring.
M1–M5
the agent
containerd runtime, networking, DNS views, atomic switches, isolation, borrow and clone. the fun bit.
M6
push to deploy
SSH, git, change detection, builds and deploys across the platform services.
M7
previews and HTTPS
preview lifecycle, rollback, config changes, team domains and certificates.
M8
v1
install and upgrade, commands and dashboard, docs, end-to-end and chaos tests. then, finally: ship it.
later
more nodes
workers that run only the agent, routed over WireGuard. a Kubernetes backend after that, if it earns one.
follow along.
watch the repo to see the agent come together, or open an issue if previews that borrow or clone your databases would fix a problem you have today. complaints also accepted.