Webhooks
Inbound push-to-deploy webhooks, and outbound deploy/health notifications.
"Webhooks" in VAC means two distinct things: inbound webhooks that trigger deploys when you push, and outbound notifications that VAC sends when something happens.
Inbound: push-to-deploy
Each app exposes a signed inbound webhook so your Git host can tell VAC to deploy.
1. Get the webhook URL and secret
In the app, open its webhook config. VAC gives you a delivery URL of the form:
https://vac.example.com/webhooks/{appID}Generate (or rotate) a secret for it — VAC seals the secret at rest and shows the plaintext once. Add the URL and secret to your repository's webhook settings (GitHub, GitLab, etc.).
2. Add a deploy trigger
A webhook delivery only deploys if it matches a deploy trigger you've created — so a push to a feature branch doesn't redeploy production. A trigger has:
- an event —
pushortag, - a filter — a pattern like
main,release/*, orv*(empty matches anything).
3. How a delivery is handled
When a delivery arrives, VAC:
- validates the HMAC signature against the stored secret,
- parses the ref (branch or tag) from the payload,
- matches it against the app's triggers,
- enqueues a deployment (
triggered_by: pushortag) if a trigger matches and no deploy is already running.
If a deploy is already in flight for the app, an incoming delivery is coalesced rather than queued behind it. Inbound webhooks are also rate-limited per window to absorb bursts.
Outbound: notifications
VAC can push events to Discord and Slack via incoming-webhook URLs.
Events
| Event | Fires when |
|---|---|
deploy_succeeded | a deploy completes and services are healthy |
deploy_failed | a deploy fails or times out on health checks |
crash_loop | a service is stopped after repeated crashes |
oom_killed | a container is killed by the OOM killer |
vac_restarted | the control plane restarts |
cert_expiring | a certificate is within ~14 days of expiry without renewing |
backup_failed | a scheduled or manual backup fails |
traffic_anomaly | the security monitor sees a request/error spike from one IP |
All events default on; you can toggle them individually.
Configuration
Set webhook URLs in Settings → Notifications (stored encrypted with the master key), or via
the VAC_NOTIFY_DISCORD_URL / VAC_NOTIFY_SLACK_URL environment variables, which take
precedence. There's a test button to confirm delivery.
Notifications are fire-and-forget on a background goroutine — they never block a deploy — with a few retries on failure. Messages are color-coded by severity and deep-link back to the app. URLs that resolve to private/loopback addresses are refused (SSRF guard). There is no email channel.