Overview
What Whoosh is, how a deploy works, and what the single binary does when you run it.
Whoosh is a single-binary deployment tool.
It connects to your hosts over SSH (or locally), builds a timestamped release from git, links shared files and
directories into it, atomically swaps a current symlink, and supports rollback - all driven by a Deployfile.yml
plus per-stage deploy/<stage>.yml files. (Same directory structure as in Capistrano)
Capistrano?
For those who have used Capistrano, this utility may seem familiar.
Indeed, this utility was originally written as a replacement for Capistrano and therefore inherited familiar behavior.
The command is whoosh, and every invocation has the same shape:
whoosh <stage> <action>
Why it exists
- No agent on the targets. There is no daemon, agent, or runtime to install.
A host only needs
git,tar, a POSIX shell (/bin/sh) and your scripts dependencies. Whoosh talks SSH directly, so the operator machine doesn’t even need the OpenSSH client. - One binary to ship. Build it once, drop it on your
PATHor in a CI image, and run it anywhere. - Batteries included.
Releases,
shared/files, thecurrentsymlink, rollback, roles, and lifecycle hooks all come built in.
What it does when you run a deploy
whoosh <stage> deploy runs a fixed sequence of phases, each as a barrier across all the stage’s hosts, with your
before/after hooks wrapped around it:
deploy:starting ---> deploy:check ---> deploy:init ---> deploy:started
----------------------------------+------------------------------------
|
v
deploy:updating ---> deploy:symlink ---> deploy:updated ---> deploy:publishing
--------------------------------------+---------------------------------------
|
v
deploy:published ---> deploy:finishing ---> deploy:finished
------------------------------------------------------------ Lock & check.
Take a deploy lock on the primary host, ensure the directory tree exists, and verify every
linked_filesentry is present inshared/. - Build the release.
Update the git mirror, create
releases/<timestamp>/, and record the deployedREVISION/REVISION_TIME. - Link & publish. Symlink the shared files/dirs into the new release, then atomically repoint
currentat it. - Finish. Append to
revisions.logand prune releases beyondkeep_releases.
The marker phases (deploy:init, deploy:started, deploy:updated, deploy:published, deploy:finished) run no
built-in command - they are just hook anchors for assign your tasks if needed.
See Usage -> Deploy lifecycle for what each phase does,
and Configuration -> Hooks & phases for attaching
your own tasks.
On-target layout
A deploy produces this tree under each host’s deploy_to:
<deploy_to>/
repo/ # bare git mirror cache
releases/<timestamp>/ # one dir per release
REVISION # deployed git SHA
REVISION_TIME # deploy time (RFC3339)
shared/ # linked_files / linked_dirs persist here
current -> releases/<timestamp>
revisions.log # one line appended per deploy
What you configure
Everything lives in two kinds of YAML file:
Deployfile.yml- shared config: the app, defaults, tasks, hooks, plugins.deploy/<stage>.yml- one file per stage, holding that stage’s hosts and any overrides (the stage wins - see merge rules).
A first config is two short files:
# Deployfile.yml
version: "1"
app:
name: myapp
repo: [email protected]:org/myapp.git
branch: main
deploy_to: /var/www/myapp
keep_releases: 5
# deploy/production.yml
hosts:
- address: web1.example.com
roles: [ app, web ]
- address: db1.example.com
roles: [ db ]
Who it’s for
Teams who want multi-host, role-aware deploys.
Next steps
- Installation - build the binary and run it from CI.
- Configuration - write your
Deployfile.yml. - Usage - the commands, flags, lifecycle, and rollback.
- Examples - runnable, self-contained configs.