Skip to content

Hooks ​

Hooks are shell scripts that run at specific points in the deployment lifecycle — installing dependencies during the build, running migrations before traffic is routed, warming caches after the app goes live. You declare them in the optional <hooks> element.

<hooks> ​

<hooks> contains one or more <hook> elements, at most one per type.

xml
<hooks>
    <hook type="build"><![CDATA[
      composer install --no-dev --optimize-autoloader
    ]]></hook>
    <hook type="deploy"><![CDATA[
      php artisan migrate --force
    ]]></hook>
</hooks>

<hook> ​

The script body is the element's text content. The only attribute is type.

AttributeRequiredDefaultDescription
typeYes—One of build, deploy, post_deploy. Unique per app.

Hook types ​

TypeWhen it runsFilesystemNetworkTraffic
buildDuring the image build.App directory writable.YesNot serving yet.
deployAfter the container starts, before routing.App directory read-only, mounts writable.—Not yet routed — blocks traffic.
post_deployAfter traffic is being served.App directory read-only, mounts writable.—Already live; runs non-blocking.

Choosing a type

  • build — anything that produces build artifacts: composer install, asset compilation. It runs with network access and a writable app directory, and the result is baked into the image.
  • deploy — work that must finish before users hit the new version: database migrations. The app directory is read-only here, so write only to mounts.
  • post_deploy — best-effort work that should not delay going live: cache warming, pinging external services.

CDATA guidance ​

Use CDATA for scripts

Wrap multi-line scripts (and anything containing &, <, >, or quotes) in a <![CDATA[ ... ]]> block so the shell content is not interpreted as XML.

xml
<hook type="deploy"><![CDATA[
  php artisan migrate:status
  php artisan migrate --force
]]></hook>

Worker commands use the same convention for their multi-line form.

How a hook is executed ​

Every hook body becomes a real /bin/sh script with set -e, not a chain of &&-joined commands. Comments, if/for/case and multi-line quoting all work as written. A build hook is copied into the image as /usr/local/bin/tragwerk-build-hook and run in a single RUN layer; deploy and post_deploy hooks become part of the container entrypoint.

xml
<hook type="build"><![CDATA[
  # Install dependencies
  composer install --no-dev --optimize-autoloader

  if [ -f package.json ]; then
      npm ci && npm run build
  fi
]]></hook>

Every hook also knows which stage it is running in: $TRAGWERK_PHASE is build, deploy or post_deploy respectively, and runtime in the server, worker and cron processes. See platform variables.

Each line is trimmed and blank lines are dropped, so indentation is not preserved. Here-documents and other indentation-sensitive constructs therefore do not survive — keep those in a script file in your repository and call it from the hook.

Hooks run once, in the application container

Workers and the cron sidecar share the application image but start with an empty entrypoint, so they do not re-run deploy hooks. A worker restarting on its own therefore cannot run migrations behind the application's back.

A slow deploy hook fails the deployment

deploy hooks run in the entrypoint before the web server starts listening, so the container counts as unhealthy for as long as the hook runs. The default budget is 90 seconds (a 30s grace period plus 12 probes at 5s); exceed it and the deployment tears the container down mid-hook and rolls back. Raise it with <health> when the hook does heavy work such as a large data import, or move the work to a post_deploy hook, which runs in the background after traffic is already being served.

Examples ​

Install dependencies at build time:

xml
<hook type="build"><![CDATA[
  composer install --no-dev --optimize-autoloader
]]></hook>

Run migrations before traffic is routed:

xml
<hook type="deploy"><![CDATA[
  php artisan migrate:status
  php artisan migrate --force
]]></hook>

Warm caches after going live:

xml
<hook type="post_deploy"><![CDATA[
  php artisan cache:warm
]]></hook>

Tragwerk — self-hosted PHP application hosting.