Deployment hooks
Orbit runs on your machine over SSH: sync source, then run ops/* on the remote. Hooks live in your repository under ops/.
your machine remote host ($SLOT_SRC)orbit deploy rsync work tree → src /bin/bash ops/deployorbit status SSH /bin/bash ops/status (optional)orbit remove SSH /bin/bash ops/remove (optional)Sync finishes before the hook runs. Hooks execute from $SLOT_SRC with SLOT_* exported. Orbit does not read hook contents.
Lifecycle
orbit init— writesops/from a recipe. Local only. No remote hook.orbit create— slot directories and optional expiry timer. No hook.orbit env— writes$SLOT_ENV. No hook.orbit deploy— synchronizes$SLOT_SRC, then runsops/deploy.orbit status— runsops/statuswhen present.orbit remove— runsops/removewhen present, then deletessrcandrun.--purgealso deletesdataandenv.
ops/deploy is required. Add ops/status and ops/remove when the app runs as a long-lived process.
Normal removal keeps data and env. Recreate the same slot name to reuse them. orbit remove --purge and automatic expiry delete the whole slot.
Hook files
ops/deploy required — orbit deployops/status optional — orbit statusops/remove optional — orbit removeThe file name is the command name. Orbit runs /bin/bash -- ops/<name>. The files do not need a .sh suffix or the execute bit. A #!/bin/bash line at the top is for humans.
ops/remove stops the app. Orbit then deletes src and run.
Hooks run from $SLOT_SRC, take no arguments, and cannot prompt. Orbit streams output to your terminal.
Writing hooks
#!/bin/bashset -euo pipefail # Build or install from "$SLOT_SRC".# Start the new process or container.# Check that it is healthy.# Publish it or reload the service.Idempotent. Exit 0 on success, non-zero on failure.
Docker — build and run containers, health-check a release socket, switch a stable socket, keep $SLOT_DATA and $SLOT_ENV outside the image.
systemd — install or compile under $SLOT_SRC or $SLOT_RUN, unit with EnvironmentFile=$SLOT_ENV, restart, check socket or port, reload Caddy when needed. ops/remove stops the unit; ops/status checks the unit or socket.
See Podman. Other runtimes: name releases with $SLOT_DEPLOY_ID, keep data in $SLOT_DATA, publish under $SLOT_RUN, and make ops/remove find the app by slot label — not a remembered deploy ID.
Slot layout
/srv/orbit/ Caddyfile slots/<slot>/ src/ synchronized repository data/ durable application data env application environment, mode 0600 run/ sockets and generated runtime configurationThe deploy user owns src, data, and env. Caddy can use run/ but cannot read src, data, or env.
Synchronization
Orbit selects with git ls-files. Default deploy requires a clean work tree, so the upload matches HEAD. --force also includes:
tracked files with local modifications
untracked files Git does not ignore
It mirrors that tree into $SLOT_SRC with rsync. Files absent from the selection are deleted on the remote. The transfer is not atomic; do not run concurrent deploy or remove on the same slot.
Keep secrets, durable data, and runtime files outside $SLOT_SRC.
The deploy hook receives slot paths, $SLOT_DEPLOY_ID, and $SLOT_GIT_REVISION. Use data, env, and run to see what is already running.
Cutover — stop/start, blue/green, migrations — is the hook's job. Orbit does not choose a strategy.
Variables from Orbit
Exported before each hook:
ORBIT_ROOT /srv/orbitORBIT_CADDY /srv/orbit/Caddyfile SLOT_NAME concrete slot name (after {} expansion)SLOT_ROOT /srv/orbit/slots/<slot>SLOT_SRC synchronized repositorySLOT_DATA durable application dataSLOT_ENV application environment fileSLOT_RUN sockets and generated runtime filesSLOT_TTL configured lifetime in seconds, or 0ops/deploy also receives:
SLOT_DEPLOY_ID unique 12-character ID for this deploymentSLOT_GIT_REVISION local Git HEAD revisionUse $SLOT_DEPLOY_ID to name releases, images, containers, or sockets. $SLOT_GIT_REVISION is for labels. With --force, Orbit also uploads local changes and non-ignored untracked files.
ops/remove receives ORBIT_PURGE (1 = delete data and env, 0 = keep them).
Environment file
orbit env writes $SLOT_ENV (mode 0600, atomic replace). Orbit does not source it into the hook. Pass it to the app with --env-file, EnvironmentFile=, or equivalent.
Caddy
Host setup prepares system Caddy. Hooks write per-slot routes and reload after the app socket is ready:
Listen on a Unix socket under
$SLOT_RUN.Write
$SLOT_RUN/Caddyfile.Reload after the socket is ready.
Main file imports slot routes:
{ email you@example.com}import /srv/orbit/slots/*/run/Caddyfileops/remove
Stops application-owned processes and removes runtime files. Non-zero exit aborts removal. When missing, Orbit still deletes src and run and warns that processes or routes may remain.
ops/status
Print one short line:
exit
0— healthyexit
1— stopped or unhealthyexit
2+— check failed
If ops/status is missing, Orbit prints unknown and exits 0. If src/ is missing, it prints missing and exits 1.