Hooks

Orbit handles the common path to the remote machine. Repository-owned shell hooks handle the steps that are specific to the application.

The hooks are the boundary between Orbit and the project. Orbit calls them at known points in the slot lifecycle. The project decides what each call must do for this application. A recipe is one complete implementation of this contract.

The split is:

text
Orbit                         Your repository
select and synchronize  -->   ops/deploy.sh builds and starts
run a status check       -->   ops/status.sh checks
remove a slot            -->   ops/down.sh stops and cleans up

Orbit does not inspect these scripts or infer steps from the application language. A Python service, a Go binary, and a static site use the same hook contract.

Lifecycle

Each Orbit command either does the outer work itself or calls one hook. The table is the complete map:

  • orbit create makes the slot directories and the optional expiry timer. It does not run a hook.

  • orbit env writes $SLOT_ENV. It does not run a hook.

  • orbit deploy synchronizes $SLOT_SRC, then runs ops/deploy.sh.

  • orbit status runs ops/status.sh when that file exists.

  • orbit remove runs ops/down.sh when that file exists, then deletes src and run. --purge also deletes data and env.

ops/deploy.sh is required. The other two hooks are optional. A recipe that starts a long-lived process should still provide them so status and removal stay accurate.

The rest of this page is the contract those hooks must follow.

Hook files

Hooks live under ops/ in the repository:

text
ops/deploy.sh     required and executable
ops/down.sh       optional
ops/status.sh     optional

They run from $SLOT_SRC, receive no arguments, and cannot prompt for input. Orbit streams their output to the local terminal.

Slot layout

Each slot separates synchronized source from state that must survive a deploy:

text
/srv/orbit/
  Caddyfile
  slots/
    <slot>/
      src/      synchronized repository
      data/     durable application data
      env       application environment, mode 0600
      run/      sockets and generated runtime configuration

Two slots can contain the same application source and hooks while using different env, data, and run contents. This is how one project can run as production, staging, and a temporary preview without separate deployment definitions.

The deploy user owns source, data, and environment values. Caddy can traverse the slot and access the shared run/ tree, but it cannot read the source, durable data, or environment file.

orbit remove keeps data and env. orbit remove --purge deletes the complete slot.

Synchronization

Orbit uses git ls-files to select:

  • tracked files

  • tracked files with local modifications

  • untracked files that Git does not ignore

It builds an exact local staging tree and mirrors it into $SLOT_SRC with rsync. Files that are not in the local Git selection are deleted from the remote source tree.

The transfer uses delayed updates, but it is not a transactional release swap. An interrupted transfer can leave a mixed source tree. Do not run concurrent deploy or remove operations for the same slot.

Keep persistent data, secrets, and runtime files outside $SLOT_SRC.

Variables from Orbit

Orbit exports a small environment before it runs a hook. These values describe the host layout and the current slot:

text
ORBIT_ROOT    /srv/orbit
ORBIT_CADDY   /srv/orbit/Caddyfile
 
SLOT_NAME     concrete slot name
SLOT_ROOT     /srv/orbit/slots/<slot>
SLOT_SRC      synchronized repository
SLOT_DATA     durable application data
SLOT_ENV      application environment file
SLOT_RUN      sockets and generated runtime files
SLOT_TTL      configured lifetime in seconds, or 0

All three hooks receive these values. SLOT_NAME is the concrete slot name after {} expansion.

Deploy values

ops/deploy.sh also receives:

text
SLOT_DEPLOY_ID      unique 12-character ID for this deployment attempt
SLOT_GIT_REVISION   local Git HEAD revision

Use SLOT_DEPLOY_ID to name a release, image, container, or socket. Two deployments of the same Git revision still receive different IDs.

SLOT_GIT_REVISION describes the local HEAD commit. It is useful for labels and diagnostics, but it is not a content ID. Orbit also synchronizes local changes and non-ignored untracked files.

Removal value

ops/down.sh receives ORBIT_PURGE. It is 1 when Orbit will delete the complete slot, including durable data and environment values. It is 0 when Orbit will keep them.

ops/status.sh does not receive deploy metadata or ORBIT_PURGE. A status check should use the stable slot paths instead of one specific deployment ID.

Deploy sequence

ops/deploy.sh can use any deployment method. A typical hook performs the steps that apply to the application:

  1. Build or install the new version.

  2. Run migrations when required.

  3. Start the replacement process or container.

  4. Check that it is healthy.

  5. Switch traffic to it.

  6. Stop the previous version.

The exact order is yours. Orbit only runs the script and returns its exit status.

Make deploy.sh idempotent so it can deploy the same source and environment again safely.

Environment values

orbit env stores the slot environment at $SLOT_ENV. The file has mode 0600 and uses an atomic same-directory replacement when it changes.

Orbit does not source this file into the hook environment. The hook decides how to pass the values to the application. For example, a container command can use --env-file "$SLOT_ENV", and a systemd unit can use EnvironmentFile=.

This separates two kinds of values:

  • Orbit-supplied variables describe the deployment and slot. Orbit exports them before each hook.

  • Application variables belong to the project. orbit env stores them in the file at $SLOT_ENV.

The separation keeps application secrets out of the synchronized repository and prevents application settings from becoming part of the Orbit contract.

Anyone who can connect as the deploy user can read the environment file and run the hooks. Keep SSH access to that account restricted.

Caddy integration

Host setup prepares Caddy, but a deployment hook does not have to use it.

For an HTTP application behind the prepared Caddy service, the hook can:

  1. Start the application on a Unix socket under $SLOT_RUN.

  2. Write $SLOT_RUN/Caddyfile with the slot route.

  3. Reload Caddy after the new socket is ready.

The main Orbit Caddyfile imports generated slot configuration:

caddy
{
    email you@example.com
}
import /srv/orbit/slots/*/run/Caddyfile

A Unix socket and Caddy are one deployment pattern. You can instead use systemd, direct processes, another proxy, or a deployment with no HTTP service.

Stop hook

ops/down.sh stops application-owned processes and removes runtime files.

A non-zero exit status stops slot removal. When the hook is missing, Orbit still deletes src and run, but warns that containers, systemd units, or Caddy routes can remain active.

During removal, use $ORBIT_PURGE when the application must clean up durable resources differently for a normal removal and a permanent purge.

Status hook

ops/status.sh prints one short status line:

  • Exit 0 for a healthy application.

  • Exit 1 for a stopped or unhealthy application.

  • Exit 2 or greater when the status check itself fails.

A missing status hook reports unknown.

Recipes

The Recipes page lists runtime implementations. The Podman recipe follows a complete set of hooks. It builds an image, starts a new container, checks it through a Unix socket, switches the stable socket, reloads Caddy, and stops the old container.

The source example defaults to Stario, but you can adapt its container command and health check for another server.

Theme

Made by Adam with Stario