Skip to main content

slip Promises Zero-Downtime Deploys From CI Without Kubernetes: What the Thinnest Deploy Primitive Covers, and What Breaks at Machine #2

11 min readDora NodaDora Noda
Share
On this page

The pitch is one sentence long: zero-downtime container deploys from CI, without Kubernetes. No SSH keys on runners, no PaaS dashboard to babysit, no cluster to feed. slip, a Rust deploy daemon introduced via Show HN in September 2026, is the latest and thinnest entry in the single-box self-hosting wave — and the honest version of its story is more interesting than the pitch. It covers considerably more than "thin" suggests, and what it genuinely can't do all breaks at exactly the same place: the day you need a second machine.

Here is the verdict up front, before a single paragraph of mechanism:

Capabilityslip covers?If not, who owns it?
Zero-downtime container swapYes — blue-green via Caddy route swap, or recreate
Health-check gating before cutoverYes — HTTP path checks, per-container probes
TLS and custom domainsYes — automatic HTTPS via Caddy, multi-route hostnames
Preview environments per PRYes — ephemeral deploys with TTL, subdomain routing, wildcard TLS
Audited rollbackYes — SQLite deploy history plus a rollback API endpoint
Building the imageNo — CI builds and pushes; slip starts at "image exists"Your CI pipeline
A second machineNo — one daemon, one box, no schedulerYou, or a fleet reconciler
Shared or replicated stateNo — host-path bind mounts stay on their boxYou, or a storage layer
Autoscaling and self-healing nodesNo — no MachineHealthCheck equivalentA Cluster API-style control loop

That table is the whole post in miniature. The interesting half is the top: four rows most people assume a tool this thin punts, which it actually covers. The sobering half is the bottom: everything it punts shares one root cause, and no amount of roadmap will fix it without changing what the tool is.

What slip actually is

Strip away the framing and slip is a small, legible machine. A daemon called slipd runs on your server. Your CI pipeline — GitHub Actions in the reference flow — builds and pushes a container image, then fires a signed webhook at the daemon. slipd pulls the image, starts the new container, waits for its health check to pass, swaps the Caddy route to point at it, and stops the old container. The project's own diagram fits on one line:

text
GitHub Actions → signed webhook → slipd → pull → health check → swap route → stop old

Three design choices do most of the work. First, authentication is HMAC-SHA256 with one secret per app, so CI holds a webhook secret instead of SSH keys — there is no inbound shell access for a compromised runner to abuse. Second, all traffic management goes through Caddy's admin API, which means automatic HTTPS, atomic route swaps, and no hand-written reverse-proxy config. Third, the deploy strategies are exactly two: blue-green (start new, swap, drain old) and recreate (stop old, start new) for single-writer workloads that can't run doubled.

Around that core sits more tooling than the "thin primitive" label implies. The codebase is a Cargo workspace with three crates: slipd (the daemon), slip (the CLI), and slip-core (shared config, types, and Docker/Podman/Caddy clients). The runtime backend auto-detects Podman or Docker.

Multi-container pods deploy via podman kube play, worker apps without HTTP endpoints get container-liveness health checks, and one webhook can ship multiple images with ${tag} placeholders. The CLI rounds it out: slip init scaffolds slip.toml, a CI workflow, and an agent contract; slip status and slip logs cover observability; slip doctor runs host diagnostics with prescriptive remedies; and slip apply makes the repo's config the source of truth via validate → diff → push.

A minimal app declaration shows how little ceremony is involved:

toml
[app]
name = "api"
image = "ghcr.io/you/api"
 
[routing]
domain = "api.example.com"
port = 8080
 
[health]
path = "/health"
 
[deploy]
strategy = "blue-green"

Note the premise baked into that file: the image already exists in a registry. slip is a deployment daemon, not a build system — CI owns the Dockerfile, the build cache, and the push. That boundary is deliberate, and it's the first honest "punt" on the list: if your CI is slow or your registry is down, no deploy daemon of any thickness saves you.

Four things the "thin" label undersells

The standard critique of tools like slip writes itself: sure, it restarts a container without dropping connections, but it must punt health gating, previews, TLS, and rollbacks to the operator. I expected to write exactly that critique. The README wouldn't let me — slip covers all four, with concrete mechanisms, not promises.

Health-check gating is structural, not bolted on. Every deploy declares a health probe — an HTTP path for web workloads, container-liveness for workers — and the Caddy route swap doesn't happen until the new container reports healthy. Mixed pods get per-container probes, so an API container and a background worker in the same pod are each gated on their own signal. If the probe never passes, the new container is discarded and the old one keeps serving. That is the entire difference between "restart without downtime" and "restart and hope," and it's in the core loop.

Preview environments exist as a first-class concept: ephemeral deploys with a TTL, subdomain routing, and wildcard TLS. The shape that usually requires a PaaS control plane — or a pile of CI scripting around DNS and certificates — ships in the daemon. Time-bounded, per-PR URLs with valid certificates is genuinely the feature I'd least expect from a single-binary deploy tool, and it's there.

TLS and custom domains fall out of the Caddy integration. slipd manages routes through Caddy's admin API, so automatic HTTPS issuance and renewal come free, and one app can serve multiple hostnames. The project has clearly done real dogfooding here: its issue tracker shows a TLS-default fix (publicly trusted ACME as the default so CI runners can verify the webhook endpoint) that only surfaces once a real deployment exists.

Audited rollback is an API call, not a runbook. Deploy history persists in STRICT SQLite tables under /var/lib/slip, surviving daemon restarts and queryable over the management API, and POST /v1/apps/{name}/rollback reverts to the previous deploy. It isn't a signed, multi-party audit trail — but for a single team on a single box, "every deploy recorded, one call to undo" is the usable 90% of rollback story.

Credit where due: this is a well-scoped core loop, MIT-licensed, with a test suite, smoke tests, and a public roadmap (production hardening like deploy timeouts and image pruning next, then deeper CI integration with reusable workflows and status callbacks). It's early — a small star count and an active SLIP-xxx tracker — but it's real software with real users, including its own author.

The real seam: machine #2

So where does it actually break? Everywhere, all at once, the moment one box isn't enough. Walk through adding a second server concretely and watch each subsystem fail in a different way:

  • The daemon is a singleton. slipd runs on the box and manages that box's containers. There is no leader election, no shared intent, no "desired state" object two daemons could converge on. Two boxes means two daemons, two sources of truth, and you as the consensus protocol.
  • Volumes don't follow. Persistent state is host-path bind mounts that survive redeploys — on the host they're bound to. Move a workload to box 2 and its data stays on box 1. There is no volume driver, no replication, no snapshot story beyond what you'd script around the filesystem.
  • History and secrets are local files. The SQLite deploy log and the per-app secret store live at /var/lib/slip on one machine. Rollback on box 2 can't see box 1's history; secret rotation is a per-box operation with no distribution.
  • Caddy is a single instance. TLS termination, routing, and the atomic swap all assume one reverse proxy. Spreading traffic across two boxes needs a load balancer in front, cross-box health awareness, and certificate coordination — a second layer slip has no opinion about.
  • Nothing watches the machines. No node health checks, no rescheduling, no autoscaling. If the box dies, everything on it is down until a human provisions a replacement and re-runs the setup. The roadmap's production hardening (timeouts, pruning, packaging) makes the single box more robust; it doesn't make it two boxes.

Now map each failure to what a declarative fleet reconciler does instead. Desired state lives in versioned manifests, not a local database, so any controller can converge any machine toward it. Machine lifecycle is itself reconciled — Cluster API's MachineHealthCheck replaces a dead node the way slip replaces a dead container. Storage is a scheduled resource with a CSI driver, not a path on a disk. Rollback is git revert plus reconciliation, with the audit trail in version control rather than a SQLite file on the patient. None of this is magic; it's the same "declare intent, let a loop converge reality" idea slip applies to containers, applied one level up to machines.

The honest summary: slip reconciles containers on a box the way a fleet platform reconciles boxes in a fleet. Adopting it doesn't avoid the reconciliation idea — it just draws the boundary at the machine edge and leaves everything outside that boundary as operator toil. That's a fine trade while the toil is zero machines beyond the first. It becomes a second job the week it isn't.

Where slip sits in the thin-deploy wave

slip didn't appear in a vacuum. The "simpler than a PaaS" space is crowded, and each tool draws the thinness boundary in a different place:

  • Kamal (from 37signals) is the closest philosophical sibling: no Kubernetes, Docker-based, kamal deploy from your machine. But Kamal reaches multiple servers over SSH, manages accessories like Postgres and Redis, and ships kamal rollback — it spends its complexity budget on multi-machine reach while requiring SSH access and (historically) a registry. slip spends its budget in the opposite direction: a daemon so CI needs no SSH keys at all, at the cost of staying single-box.
  • Coolify and Dokploy are full self-hosted PaaSes with dashboards, one-click services (Coolify v4.0 claims 280+ plus an MCP server for agent-driven deploys), database provisioning, and multi-server support. They cover far more of the table above — but you operate a platform: a dashboard to secure and update, idle overhead on the box, and a bigger conceptual surface. slip is for the team that looked at that and said "we just want the deploy loop."
  • Mushak-class tools (zero-config Docker/Compose-to-server deployers from recent Show HNs) share slip's single-box premise with even less machinery — often just an SSH-and-Compose wrapper. slip's daemon model, signed webhooks, and Caddy-native routing put it a rung above the script wrappers in robustness.

slip's actual differentiator, then, isn't thinness per se — it's which complexity it refuses. No SSH keys in CI is a real security posture improvement over SSH-based deployers. No dashboard is a real operational savings over PaaS panels. And the Caddy-native design means TLS and routing are inherited from a mature project rather than reimplemented. The cost is the machine edge: Kamal crosses it with SSH fan-out, Coolify with multi-server orchestration, and slip doesn't cross it at all.

Who should pick it? A solo developer or small team with one server, a container registry, and CI already building images — the shape where every row in the "covers" half of the table is exactly the toil you feel, and the "punts" half is capacity you don't yet need. The failure mode to avoid is adopting it for a fleet-shaped problem: if you already know you'll need two boxes within the quarter, the migration off a singleton daemon is the most expensive part of the tool, and you should price it in on day one.

Thin is a feature until it's a ceiling

The thin-deploy wave keeps producing better single-box tools because the single-box problem is genuinely well-bounded: fixed capacity, local state, one network identity. slip is arguably the cleanest expression of it yet — a signed webhook in, a health-gated blue-green swap out, TLS and previews included, nothing to click. If that sentence describes your whole operations burden, the tool fits like a key.

But notice what the wave never produces: a thin tool that grows into a fleet. Every project either stays single-box or accretes the reconciler, scheduler, and state distribution it was founded to avoid — at which point it's a PaaS with a thinner starting point, not a thin tool anymore. There is no shame in that arc; it's just worth choosing deliberately. Adopt slip for what it covers today, with eyes open about the machine edge, and when box two arrives, reach for the reconciliation loop instead of scripting around the singleton.


Running more than one box already? Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, with declarative reconciliation across the whole fleet instead of per-box daemons. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex