Hooks
Runix supports 10 lifecycle hook points that execute shell commands at specific stages of a process's lifecycle.
Hook Points
| Hook | When | Blocks Process? | Failure Behavior |
|---|---|---|---|
pre_start | Before process starts | Yes — prevents start | Process enters errored state |
post_start | After process starts | No (async) | Logged as warning |
pre_stop | Before process stops | No (best-effort) | Logged as warning |
post_stop | After process stops | No (async) | Logged as warning |
pre_restart | Before process restarts | No | Logged as warning |
post_restart | After process restarts | No (async) | Logged as warning |
pre_reload | Before graceful reload | No | Logged as warning |
post_reload | After graceful reload | No (async) | Logged as warning |
pre_healthcheck | Before health check runs | No | Logged as warning |
post_healthcheck | After health check completes | No (async) | Logged as warning |
Configuration
processes:
api:
entrypoint: ./cmd/api
hooks:
pre_start:
command: "echo 'Starting API at $(date)' >> /var/log/runix-events.log"
timeout: 10s
post_start:
command: "curl -X POST http://localhost:8080/warm-cache"
timeout: 30s
pre_stop:
command: "curl -X POST http://localhost:8080/drain-connections"
timeout: 15s
post_stop:
command: "echo 'API stopped' >> /var/log/runix-events.log"
pre_reload:
command: "echo 'Reloading API' >> /var/log/runix-events.log"
timeout: 10s
Hook Configuration
Each hook has two fields:
| Field | Type | Default | Description |
|---|---|---|---|
command | string | (none) | Shell command to execute (sh -c) |
timeout | duration | 30s | Maximum execution time |
If command is empty or not set, the hook is skipped.
Execution
All hooks are executed via sh -c <command>:
cmd := exec.CommandContext(ctx, "sh", "-c", hook.Command)
This means hooks have full shell capabilities — pipes, redirects, environment variables, etc.
Environment
Hooks inherit the process's environment variables plus any overlay from the process config.
Timeout
If a hook exceeds its timeout, the context is cancelled and the process is killed. The hook's stdout/stderr are captured and logged.
Blocking vs Non-Blocking
Only pre_start is a true blocking hook — if it fails, the process does not start. All other hooks are non-blocking: failures are logged but do not prevent the lifecycle action.
Hook Chain
Multiple hooks can be defined. They execute in this order during each lifecycle event:
Start: pre_start → start process → post_start
Stop: pre_stop → stop process → post_stop
Restart: pre_restart → stop → start → post_restart
Reload: pre_reload → reload process → post_reload
Health check: pre_healthcheck → run check → post_healthcheck
Examples
Database Migration Before Start
hooks:
pre_start:
command: "goose -dir migrations postgres $DATABASE_URL up"
timeout: 60s
Cache Warming After Start
hooks:
post_start:
command: "sleep 2 && curl -s http://localhost:8080/preload"
timeout: 30s
Graceful Drain Before Stop
hooks:
pre_stop:
command: "curl -X PUT http://localhost:8080/health/unhealthy && sleep 5"
timeout: 15s
Health Check Notification
hooks:
post_healthcheck:
command: 'curl -X POST https://hooks.slack.com/services/XXX -d ''{"text": "Health check completed for process"}'''
timeout: 10s
What's Next
- Process Lifecycle — Where hooks fit in the lifecycle
- Configuration Reference — Hook config fields