Notification Hub
1. What? — Definition and context
Section titled “1. What? — Definition and context”The Notification Hub is the N8N workflow that centralises every notification of the infrastructure. It deduplicates alerts, applies quiet hours, routes to the correct channels, and delegates callbacks to a dedicated sub-workflow extracted during refactoring #276.
Components
Section titled “Components”| Workflow | Nodes | Role |
|---|---|---|
| Notification Hub (parent) | 41 | Dedup + quiet hours + format + multi-channel send |
NH Callback Handler (9Xb3mplmzE5994bi) | 18 | Ack, approve, retry, ignore, delivery callbacks |
Notification sources
Section titled “Notification sources”| Source | Event types |
|---|---|
| Docker DIUN | New image versions, approval requests |
| Alertmanager | Prometheus alerts (CPU, RAM, disk) |
| Health Check | Service status |
| Global Error Handler | Classified N8N errors with [Retry] [Details] [Ignore] [Fix] actions |
| Security CVE Watch | CRITICAL alerts + weekly digest |
| N8N workflows | Execution results, business events |
Output channels
Section titled “Output channels”| Channel | Usage |
|---|---|
| Telegram | Primary, interactive notifications with buttons |
| ntfy | Mobile push backup (CRITICAL channel) |
| Discord | Optional, for teams |
Visual architecture
Section titled “Visual architecture”2. Why? — Stakes and motivations
Section titled “2. Why? — Stakes and motivations”Problems solved
Section titled “Problems solved”| Problem | Without hub | With hub |
|---|---|---|
| Notification spam | Each service notifies independently | Single controlled outbound point |
| Duplicates | Same alert sent multiple times | Key-based deduplication + TTL |
| Night-time alerts | Woken up by a warning | Quiet hours for non-criticals |
| No audit | Lost notifications | Full history in DB |
| Broken buttons | Callback logic scattered | Reusable dedicated sub-workflow |
Why extract a Callback Handler (#276)?
Section titled “Why extract a Callback Handler (#276)?”The initial hub mixed notification ingestion and Telegram callback handling in a single 57-node workflow. Three symptoms triggered the extraction:
| Symptom | Cause |
|---|---|
| Ambiguous double-trigger | An incoming webhook could be either a new notification OR a callback |
| Mishandled dedup bypass | Callbacks should not pass through dedup but the code sometimes did so |
| Hard to test | Testing a callback meant first sending a real notification |
Extracting the NH Callback Handler sub-workflow (18 nodes, ID 9Xb3mplmzE5994bi) resolves these tensions: the parent routes early in the chain via Is Callback?, and callbacks bypass dedup + quiet hours.
Key features
Section titled “Key features”| Feature | Benefit |
|---|---|
| Deduplication | Avoids duplicate notifications inside a time window |
| Quiet hours | Non-critical notifications deferred 22:00-08:00 |
| Multi-channel | Telegram + ntfy + Discord depending on severity |
| History | Every notification stored for audit |
| Routed callbacks | Bypass dedup for Telegram buttons, extracted sub-workflow |
3. How? — Technical implementation
Section titled “3. How? — Technical implementation”A notification’s journey
Section titled “A notification’s journey”1. Reception — The hub receives via the /webhook/notification-hub webhook (internal only) or via Execute Workflow.
2. Is Callback? — Early detection: if the payload contains a Telegram callback_data, route to NH Callback Handler. Bypasses dedup + quiet hours.
3. Validation — Required type, valid severity (info / warning / critical), conformant JSON format.
4. Deduplication — Compute a <type>:<container>:<severity> key, lookup Redis, skip if present.
5. Quiet hours — If the time is between 22:00 and 08:00 AND severity is not critical, mark deferred=true and defer.
6. Format — Template per type and channel (Telegram HTML, Discord embed, ntfy text).
7. Multi-channel send — Telegram always, ntfy if critical, Discord if configured.
8. History — Insert into notification_history with dedup_key, channels, timestamp.
Deduplication
Section titled “Deduplication”// Dedupe keyconst dedupeKey = `${type}:${container}:${severity}`;
// TTL depending on severityconst ttl = { info: 3600, // 1 hour warning: 1800, // 30 min critical: 300 // 5 min};
const existing = await redis.get(dedupeKey);if (existing) { return { skipped: true, reason: 'duplicate' };}
await redis.set(dedupeKey, Date.now(), { EX: ttl[severity] });Quiet hours
Section titled “Quiet hours”| Time | Info | Warning | Critical |
|---|---|---|---|
| 08:00-22:00 | Immediate | Immediate | Immediate |
| 22:00-08:00 | Queued | Queued | Immediate |
Deferred notifications are stored in a pending_notifications Data Table and dispatched on the next 08:00 pass via the Deferred Sender workflow.
Callback bypass
Section titled “Callback bypass”Telegram callbacks (inline buttons) come back to the hub through the same webhook. The Is Callback? node (IF at the start of the chain) detects them via the presence of source='telegram_callback' or callback_query in the payload:
Without this bypass, clicking [Retry] at 23:00 would either be deduplicated against the original notification or deferred until morning — which would break UX.
Input format
Section titled “Input format”{ "type": "update_success", "severity": "info", "title": "Docker update succeeded", "message": "n8n-stack updated to 1.73.0", "container": "n8n", "image": "n8nio/n8n:1.73.0", "project": "n8n-stack", "timestamp": "2026-01-20T10:30:00.000Z", "callback_actions": [ { "label": "Details", "callback": "notif_details_<id>" }, { "label": "Ignore", "callback": "notif_ignore_<id>" } ], "metadata": { "duration_seconds": 45, "previous_version": "1.72.0" }}Supported types
Section titled “Supported types”| Type | Default severity | Origin |
|---|---|---|
update_success / update_failed / update_queued | info / critical / info | Docker Updates |
approval_request | warning | Docker Updates, GEH Fix Applier |
alert_prometheus | variable | Alertmanager |
container_down | critical | Health Check |
error_classified | variable | Global Error Handler |
cve_alert | critical | Security CVE Watch |
Calling the hub
Section titled “Calling the hub”From an N8N workflow (Execute Workflow):
{ "workflowId": { "__rl": true, "value": "<notification-hub-id>", "mode": "id" }, "inputData": { "type": "update_success", "severity": "info", "title": "...", "message": "..." }}Via the internal webhook:
curl -X POST http://n8n:5678/webhook/notification-hub \ -H "Content-Type: application/json" \ -d '{"type":"custom","severity":"info","title":"Test","message":"Hello"}'History
Section titled “History”The notification_history table (PostgreSQL N8N):
| Column | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
type | Text | Notification type |
severity | Text | info / warning / critical |
payload | JSONB | Full content |
channels | Array | Channels used |
sent_at | Timestamp | Effective sending time |
deferred | Boolean | Whether deferred during quiet hours |
dedupe_key | Text | Dedupe key |
callback_results | JSONB | Button responses (cumulative) |
4. What if? — Outlook and limits
Section titled “4. What if? — Outlook and limits”Current limits
Section titled “Current limits”| Limit | Impact | Mitigation |
|---|---|---|
| Single Redis instance | Cache lost if Redis is down | Notification still sent (no dedupe) — fail-open |
| No automatic escalation | Unacknowledged alerts stay at warning | Planned but not implemented |
| Fixed quiet hours | No per-user configuration | Sufficient for solo use |
| Callback timeout | If the user never clicks, state stays pending | 7-day TTL on callbacks in the DT |
Evolution scenarios
Section titled “Evolution scenarios”If automatic escalation is needed:
- Track unacknowledged alerts in
pending_notifications - After a configurable delay, upgrade to
critical→ ntfy + optional Twilio call - Grafana dashboard with a heatmap of ignored alerts
If notification volume grows:
- Batch aggregation (hourly summary for
info) - Daily digest for non-urgent notifications
- Subscription-based filtering (opt-in per type via Data Table)
If a multi-user team is involved:
- Per-user quiet-hour configuration (
notification_routingData Table) - Routing based on the on-call user
- Shared acknowledgements (a single ack is enough)
Related pages
Section titled “Related pages”Infrastructure
Section titled “Infrastructure”- N8N in queue mode — Backend automation
- Monitoring Stack — Prometheus source
- Notify Stack — DIUN and ntfy
Workflows
Section titled “Workflows”- Telegram Orchestrator — Callback receiver
- Docker Updates — DIUN source + approval
- Error Handler — Notifies via Hub with action buttons
Reference
Section titled “Reference”- Glossary — Webhook, Deduplication, Quiet Hours