Deploying

Hooks

Executable files in .dash/hooks that dash runs before and after each stage of a deploy.

How hooks run#

A hook is an executable file named after the event, in .dash/hooks (or hooks_path). dash runs it locally on the machine running the command, with the event's details in the environment, and treats a non-zero exit as a failure — a failing pre-* hook aborts the command. The exceptions are pre-app-stop / post-app-stop: by then the new container is already live, so a failure there is reported and the old container is stopped anyway.

dash init writes commented sample hooks for the common events. Pass --skip-hooks to any command to run it without them.

The hooks#

HookFiresCommandsSecrets
pre-connectbefore the first SSH connection of any command that talks to the serversanyyes
pre-buildbefore the image is builtbuild push / deliver, deploy, setup
pre-deploybefore the deploy starts, after the lock is takendeploy, redeploy, rollback, setupyes
post-deployafter a successful deploy, with the elapsed timedeploy, redeploy, rollback, setupyes
pre-app-bootbefore each boot group of hosts starts its new containersapp boot, deploy, redeploy, rollback
post-app-bootafter that boot group's new containers are liveapp boot, deploy, redeploy, rollback
pre-proxy-deploybefore a proxied role's new container is registered with dash-proxy on a hostapp boot, deploy, redeploy, rollbackdash-only
post-proxy-deployafter dash-proxy reports that container healthy and switches to itapp boot, deploy, redeploy, rollbackdash-only
pre-app-stopbefore the previous version's container is stopped on a hostapp boot, deploy, redeploy, rollback
post-app-stopafter it is stoppedapp boot, deploy, redeploy, rollback
pre-proxy-rebootbefore the proxy container is replaced on a hostproxy reboot, proxy boot (config drift), upgrade
post-proxy-rebootafter the new proxy container is upproxy reboot, proxy boot (config drift), upgrade
pre-loadbalancer-rebootbefore the load balancer container is replacedproxy reboot, proxy boot (config drift)dash-only
post-loadbalancer-rebootafter the new load balancer container is upproxy reboot, proxy boot (config drift)dash-only
docker-setupafter Docker is confirmed installed on every hostserver bootstrap, setup

pre-proxy-deploy / post-proxy-deploy fire once per host and role, around the exact moment dash-proxy switches traffic — the place to warm a cache or notify a tracker with the host that just went live. The loadbalancer hooks wrap the load balancer container's replacement, which happens separately from the per-host proxies. Neither exists in upstream kamal.

pre-connect runs before the first SSH connection of a command, so it fires for dash app logs as much as for dash deploy; use DASH_COMMAND to scope it. pre-traefik-reboot / post-traefik-reboot from kamal 1 are rejected at config time — rename them to the proxy hooks.

Environment#

Every hook gets these variables. Each is emitted twice — as DASH_* and, until dash 5.0, as its KAMAL_* twin with the same value, so hooks written for kamal keep working.

VariableValue
DASH_RECORDED_ATUTC timestamp of the event
DASH_PERFORMERthe git email of whoever is running dash, or the local username
DASH_SERVICEthe service from deploy.yml
DASH_SERVICE_VERSIONservice@abbreviated version
DASH_VERSIONthe full version being deployed
DASH_DESTINATIONthe -d destination, when one is set
DASH_HOSTScomma-separated hosts the event concerns (the boot group, the single host, or the whole target)
DASH_ROLEScomma-separated roles, when the command was narrowed with --roles
DASH_ROLEthe one role, on the proxy-deploy hooks
DASH_COMMAND / DASH_SUBCOMMANDthe command being run, e.g. deploy, or app / boot
DASH_LOCKtrue when the command holds the deploy lock
DASH_RUNTIMEseconds elapsed, on post-deploy

post-deploy also gets what the deploy report measured, so a hook can page on a build that doubled without parsing anything:

VariableValue
DASH_BUILD_RUNTIMEseconds the build phase took — absent when nothing was built
DASH_BOOT_RUNTIMEseconds the boot phase took — absent when nothing was booted
DASH_ADVICE_COUNThow many pieces of advice the report printed
DASH_ADVICE_WARNINGShow many of those were warnings rather than notes
DASH_REPORT_PATHthe JSON report this deploy saved, when report: history: is not 0

Under dash setup the hook fires from the deploy it wraps, before the outer report is finalised, so DASH_REPORT_PATH is absent from that run.

Hooks marked Secrets in the table also receive every entry of .dash/secrets as environment variables, so a pre-deploy hook can talk to the same services the deploy does.

Configuration#

Two keys in deploy.yml, both documented in the deploy.yml reference:

  • hooks_path — where the hook files live. Defaults to .dash/hooks (.kamal/hooks is still read when only that exists).
  • hooks_outputquiet or verbose, globally or per hook, to control whether a hook's stdout is shown. -v / -q on the command line override it, and a failed hook always shows its output.
config/deploy.yml
hooks_output:
  pre-build: quiet
  post-deploy: verbose

A pre-deploy hook#

The classic: refuse to deploy from a dirty tree, and record the deploy somewhere. Make the file executable (chmod +x).

.dash/hooks/pre-deploy
#!/bin/sh
set -e

if [ -n "$(git status --porcelain)" ]; then
  echo "Refusing to deploy with uncommitted changes" >&2
  exit 1
fi

echo "$DASH_PERFORMER is deploying $DASH_SERVICE_VERSION to $DASH_HOSTS"