bundant
v0.1.0 · MIT

Ship to the box you already own.

bundant ships your container over plain SSH, gates it on health, and swaps traffic through kamal-proxy. Docker or podman, one CLI, nothing left running on the host. No Kubernetes. No registry required.

Try a command

bundant · demo

type a command, or press Enter to run bundant validate

Type validate, deploy, status or rollback. Press Enter with an empty prompt to run the next command.

output recorded from a real docker host; nothing runs in your browser

$npm i -g bundant
  • docker or podman
  • plain ssh, no agent
  • MIT, self-hosted

Why and how

The platform is the overhead.

A single host already gives you a network, a proxy and a restart policy. bundant adds the deploy discipline without asking you to run a cluster to get it.

  • 01

    No registry required

    Build locally, ship over the connection you already have.

  • 02

    No Kubernetes

    A box, sshd and docker or podman. That is the whole platform.

  • 03

    Zero downtime

    Health-gated swaps through kamal-proxy, not restart roulette.

A deploy, in four steps

  1. 01

    Validate

    One deploy.yml holds the image, hosts, env, health check and proxy host. Validate parses it locally and never contacts a host.

  2. 02

    Deploy

    The new revision starts unpublished, passes a health gate, then kamal-proxy swaps its target set in one call and drains old connections.

  3. 03

    Status

    Expected and live replicas, revision and containers for every unit, straight from the host. --json for scripts.

  4. 04

    Rollback

    The previous revision is re-gated, or the exact one named by --to-revision, then swapped back. The old container was kept for exactly this.

bundant deploy --dry-run
real output
bundant deploy plan (via api)
  hello/web rev=59ceb8d runtime=podman
hosts: replace-me.example.ts.net

  1) [ssh] host prerequisites
  2) [pull] POST /images/create
  3) [network] POST /networks/create
  4) [run] POST /containers/create
  5) [health] probe -> GET /logs
  6) [proxy-ensure] GET /kamal-proxy
  7) [proxy-swap] kamal-proxy deploy
  8) [stop-old] POST /stop?t=10
Every step is printed before it runs. Nothing hidden, nothing implicit.

Features

Everything the CLI does. Nothing it pretends to.

Six capabilities, all present in bundant 0.1.0. If it is not in the CLI today, it is not on this page.

Zero-downtime deploy

The new revision starts unpublished and passes a health gate. One atomic proxy swap moves traffic and drains old connections; the previous revision stays for rollback.

Groups and protected accessories

A group is a namespace: install services in needs: order, aggregate status, roll back to one sha. Stateful accessories keep stable names and named volumes and are never touched by deploy, rollback or prune.

Replicas with manual scale

Run N copies of one service per host and scale up or down on demand. New targets are gated before the proxy swap, and scale-down drains before containers stop.

Preflight validation

validate parses deploy.yml, group.yml or accessory.yml and prints the resolved identity plus the full error list. An invalid config never contacts a host.

Docker and Podman

One typed engine API, two runtimes through their native APIs. Set runtime: podman and every step targets Podman directly — no docker shim, no compatibility layer.

Read-only observability

ps, logs, stats, events, status and a local ui page, all pull-on-demand over the same ssh connection. Nothing is installed on the host and nothing stays resident.

Commands

Every command. Every flag.

23 commands, 34 flags, one binary. Search the list, open a command and read its full usage, every flag and a runnable example.

Full CLI reference

23 of 23 commands

Deploy6

deploydeploy the current revision to every host
scaleadd/remove replicas of the running revision (no drops)
rollbackprevious revision, or the one named by --to-revision
historyrevisions newest-first, with state and the current one
deleteremove the service: proxy route, containers, env file
pruneremove stopped revisions beyond the newest K (--keep, default 3)

Units9

validateparse and validate deploy.yml / group.yml / accessory.yml
startstart a unit (or create an accessory from -f)
stopstop every replica matched by the path
restartrestart a unit (accessory: recreate; service: stop/start)
rmremove matched units (containers; --route/--env/--images/--volumes)
statusunit status by path (no path = every unit; --json for scripts)
execexec in the first running replica (--all for every replica)
inspectinspect the first running replica matched by the path
logslogs by path, each line prefixed with the replica

Groups2

groupnamespace lifecycle: install, uninstall, status, rollback
bootstrapinstall/verify podman and its host prerequisites on every host

Ops6

runtimeper-host engine/API detection: socket, forward, service state
uilocal read-only monitoring page (pull-on-demand)
psevery container on every host, with state and ports
statsone resource snapshot for the matched units
eventsruntime events since a duration (default 2h)
errorsprint the stable error catalog (code + exit status)
Deploybundant deploy

deploy

deploy the current revision to every host

Usage

bundant deploy [flags]

Flags 34

--file, -f string
config path (hosts/runtime; filters read verbs to one unit)
--dry-run
print the plan without executing it
--rev string
override the revision for file-driven deploys
--local-image string
stream a locally built image (name:tag) instead of pulling
--pull string
image pull policy: missing, always or never
--keep string
prune: revisions to keep (default 3)
--replicas string
scale: target replica count
--yes
skip the confirmation prompt
--images
rm/delete: also remove images
--volumes
rm: also remove volumes
--to-revision string
rollback: revision to return to
--skip-smoke
bootstrap: skip the smoke container
--host string
filter to one host
--container string
filter to one container
--tail string
logs: lines to show (default 200)
--follow
logs: keep streaming new lines
--since string
events: window to query (default 2h)
--port string
ui: port to bind (default 8787)
--json
emit machine-readable JSON
--timings
print per-phase timings
--hosts string
filter to a comma-separated host list
--strict
validate: require an explicit version
--allow-case-collision
deploy v2: permit case-only duplicate units
--replica string
flat verbs: select one replica
--revision string
flat verbs: select one revision
--all-revisions
flat verbs: include stopped revisions
--route
rm: also remove the proxy route
--env
rm: also remove the rendered env file
--all
exec: run in every matching replica
--help, -h
Show help information
--version, -v
Show version information
--wizard
Start wizard mode for a command
--completions <bash|zsh|fish|powershell|sh|pwsh>
Print shell completion script (choices: bash, zsh, fish, powershell, sh, pwsh)
--log-level <all|trace|debug|info|warn|warning|error|fatal|none>
Sets the minimum log level (choices: all, trace, debug, info, warn, warning, error, fatal, none)

Example

$ bundant deploy -f deploy.yml

Captured from bundant --help and bundant <command> --help by scripts/capture-commands.mjs. Nothing on this page is written by hand.

FAQ

The questions before the first deploy.

If something is not shipped, it is not promised here. The CLI help is the source of truth.