Lifecycle Hooks
Lifecycle hooks run shell commands at fixed points in a single program’s start/stop pipeline. They are configured per program, not globally.
For reacting to system-wide events (crashes, recovery, daemon startup), see System Events — those use a different configuration model (notify.toml with the notify plugin; [[event_hooks]] in OSS).
Hook catalog
| Hook | When it runs | Blocks start/stop? | Execution | Implemented |
|---|---|---|---|---|
pre_start | After extension before_start, before spawn | Yes — non-zero exit aborts start; program → Fatal | Awaited (sync) | ✅ |
post_start | After PID assigned, extension after_start, and process_started event | No | Async (tokio::spawn) | ✅ |
pre_stop | After user/API stop requested, before SIGTERM / SIGKILL | Delays signal until hook returns | Awaited (sync) | ✅ |
post_stop | After process has exited | No | Async (tokio::spawn) | ✅ |
Execution details
- Commands run via
sh -c, so pipes, redirects, and$()work. - Empty or whitespace-only commands are skipped.
- Hook stdout/stderr inherit
superd’s logging stream. pre_startfailure emits aprocess_fatalsystem event with message"Pre-start hook failed".
Configuration
Hooks are stored on each program — in stack files (TOML/JSON), or via the API / CLI / Dashboard. Example: conf/conf.d/my-app.json:
{
"services": [
{
"name": "my-app",
"command": "./app",
"hooks": {
"pre_start": "mkdir -p /tmp/app-data && chmod 700 /tmp/app-data",
"post_start": "curl -X POST http://consul:8500/register -d '...'",
"pre_stop": "curl -X POST http://consul:8500/deregister -d '...'",
"post_stop": "if [ \"$SUPER_EXIT_CODE\" != \"0\" ]; then aws s3 cp logs/app.err s3://archive/; fi"
}
}
]
}The same shape works via the API:
{
"name": "my-app",
"command": "./app",
"hooks": {
"pre_start": "echo checking...",
"post_start": "echo registered"
}
}Where this config lives
| Source | Path / API | Persisted to |
|---|---|---|
| Stack file | conf/conf.d/* → services[].hooks | snapshot.json |
| Stack apply | PUT /api/v1/stack with "hooks": { ... } | snapshot.json |
| CLI | super add / super update --pre-start "..." | snapshot.json |
| Dashboard | Program create/edit form | snapshot.json |
Hooks are not defined in super.toml at the global level — super.toml has no program tables at all. Put hooks on each program (stack entry / API / CLI) instead.
Environment variables
Hook scripts receive context via environment variables (in addition to the program’s env / env_file). These are injected by the daemon at run time — distinct from the public environment variables that configure superd/super itself:
| Variable | pre_start | post_start | pre_stop | post_stop |
|---|---|---|---|---|
SUPER_ID | ✅ | ✅ | ✅ | ✅ |
SUPER_NAME | ✅ | ✅ | ✅ | ✅ |
SUPER_HOSTNAME | ✅ | ✅ | ✅ | ✅ |
SUPER_GROUP | ✅ if set | ✅ if set | ✅ if set | ✅ if set |
SUPER_PID | — | ✅ | ✅ | ✅ |
SUPER_EXIT_CODE | — | — | — | ✅ |
SUPER_UPTIME_SECS | — | — | — | ✅ |
The managed child process also receives SUPER_ID, SUPER_NAME, SUPER_HOSTNAME, and optional SUPER_GROUP at spawn time (not the full hook set).
Lifecycle vs system events
| Lifecycle hooks | System events | |
|---|---|---|
| Purpose | Setup/teardown around one program’s start/stop | Observe cluster-wide incidents |
| Config | Per program hooks (stack JSON / API) | Global ([[event_hooks]] in OSS; notify.toml with notify plugin) |
| Data to script | Environment variables | JSON on stdin (OSS event hooks) or HTTP payload (licensed notify) |
| Examples | mkdir, Consul register, drain flag | Slack alert, archive logs on crash |
Related
- System Events — full event catalog
- Config Reference —
hooks - Custom Extensions — in-process Rust
Extensiontrait and the licensed runtime-plugin model (see Writing Extensions)
Was this page helpful? Thanks for your feedback!
Still have questions? Open an issue or browse the source.