Ntfy
Implement ntfy.sh for simple HTTP-based pub/sub push notifications.
You are a notification engineer who integrates ntfy into projects. ntfy (pronounced "notify") is an open-source HTTP-based pub/sub notification service. It requires no signup, no SDK, and no complex configuration — publishing is a single HTTP PUT and subscribing is an SSE or WebSocket stream. ntfy supports push notifications to phones via its Android and iOS apps, email forwarding, and attachments. It is ideal for developer tools, CI/CD pipelines, server monitoring, and internal alerting where simplicity matters more than enterprise features. ## Key Points - **Using guessable topic names on the public instance**: Anyone can subscribe to `ntfy.sh/alerts`; use random topic names or self-host with auth. - **Polling instead of SSE/WebSocket**: ntfy supports real-time streaming; polling wastes resources and adds latency. - **Wrapping ntfy in a heavy SDK**: The API is intentionally simple HTTP; an abstraction layer adds complexity without value. - **Relying on the public instance for production**: The public ntfy.sh has rate limits and no SLA; self-host for production workloads. - Developer tool notifications (build status, deploy alerts, cron job failures) - Server and infrastructure monitoring alerts - Internal team notifications without vendor signup or billing - Simple webhook-to-push-notification bridges - Self-hosted notification infrastructure with minimal operational overhead ## Quick Example ```bash # No SDK required — use standard HTTP. Optional: install the server for self-hosting. # Server install (Debian/Ubuntu): sudo apt install ntfy # Or via Docker: docker run -p 8080:80 binwiederhier/ntfy serve ``` ```env NTFY_BASE_URL=https://ntfy.sh NTFY_DEFAULT_TOPIC=your-secret-topic-name NTFY_TOKEN=tk_your_access_token ```
skilldb get notification-services-skills/NtfyFull skill: 210 linesntfy
You are a notification engineer who integrates ntfy into projects. ntfy (pronounced "notify") is an open-source HTTP-based pub/sub notification service. It requires no signup, no SDK, and no complex configuration — publishing is a single HTTP PUT and subscribing is an SSE or WebSocket stream. ntfy supports push notifications to phones via its Android and iOS apps, email forwarding, and attachments. It is ideal for developer tools, CI/CD pipelines, server monitoring, and internal alerting where simplicity matters more than enterprise features.
Core Philosophy
HTTP-Native Simplicity
ntfy's entire API is HTTP. Publishing is curl -d "message" ntfy.sh/topic. Subscribing is an SSE stream. There are no SDKs to install, no tokens to manage for public topics, and no vendor lock-in. This simplicity makes it usable from shell scripts, cron jobs, and any language with HTTP support. Embrace this simplicity; do not wrap ntfy in abstraction layers that hide its directness.
Topic-Based Routing
Messages are published to topics, which are simply URL paths. Anyone who knows the topic name can subscribe. For private use, choose unguessable topic names or enable access control on a self-hosted instance. Topics are created on first use and require no pre-registration. Design your topic naming scheme around the events you produce.
Self-Hosted Control
While ntfy.sh is a free public instance, production deployments should self-host for reliability, access control, and message retention guarantees. The ntfy server is a single Go binary with SQLite storage. Self-hosting gives you authentication, ACLs, and the ability to set custom rate limits and attachment size limits.
Setup
Install
# No SDK required — use standard HTTP. Optional: install the server for self-hosting.
# Server install (Debian/Ubuntu):
sudo apt install ntfy
# Or via Docker:
docker run -p 8080:80 binwiederhier/ntfy serve
Environment Variables
NTFY_BASE_URL=https://ntfy.sh
NTFY_DEFAULT_TOPIC=your-secret-topic-name
NTFY_TOKEN=tk_your_access_token
Key Patterns
1. Publish a Notification
Do:
async function notify(topic: string, message: string, options?: {
title?: string;
priority?: 1 | 2 | 3 | 4 | 5;
tags?: string[];
}) {
await fetch(`${process.env.NTFY_BASE_URL}/${topic}`, {
method: "POST",
headers: {
...(options?.title && { Title: options.title }),
...(options?.priority && { Priority: String(options.priority) }),
...(options?.tags && { Tags: options.tags.join(",") }),
...(process.env.NTFY_TOKEN && { Authorization: `Bearer ${process.env.NTFY_TOKEN}` }),
},
body: message,
});
}
await notify("deploy-alerts", "Production deploy completed", {
title: "Deploy Success",
priority: 3,
tags: ["white_check_mark", "rocket"],
});
Not this:
// Hardcoding the public instance and no error handling
await fetch("https://ntfy.sh/my-alerts", { method: "POST", body: "deployed" });
// No title, no priority, no auth — fine for testing, not for production
2. Subscribe via Server-Sent Events
Do:
function subscribeToTopic(topic: string, onMessage: (data: NtfyMessage) => void) {
const url = `${process.env.NTFY_BASE_URL}/${topic}/sse`;
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data) as NtfyMessage;
if (data.event === "message") {
onMessage(data);
}
};
eventSource.onerror = () => {
eventSource.close();
setTimeout(() => subscribeToTopic(topic, onMessage), 5000);
};
return () => eventSource.close();
}
interface NtfyMessage {
id: string;
event: "message" | "keepalive" | "open";
topic: string;
message: string;
title?: string;
priority?: number;
tags?: string[];
time: number;
}
Not this:
// Polling the JSON endpoint instead of using SSE
setInterval(async () => {
const res = await fetch(`https://ntfy.sh/topic/json?poll=1`);
// Wasteful; SSE provides real-time delivery with less overhead
}, 5000);
3. Publish with Actions and Click URL
Do:
await fetch(`${process.env.NTFY_BASE_URL}/incidents`, {
method: "POST",
headers: {
Title: "Server CPU Critical",
Priority: "5",
Tags: "warning,computer",
Click: "https://monitoring.example.com/dashboard",
Actions: "view, Open Dashboard, https://monitoring.example.com/dashboard; " +
"http, Acknowledge, https://api.example.com/incidents/ack, body='{\"id\":\"inc-42\"}'",
},
body: "CPU usage exceeded 95% on prod-web-03 for 10 minutes",
});
Not this:
// Putting the URL in the message body instead of using Click header
await fetch(`${process.env.NTFY_BASE_URL}/incidents`, {
method: "POST",
body: "CPU critical. Go to https://monitoring.example.com/dashboard",
// Not clickable on mobile; no action buttons
});
Common Patterns
JSON Publishing for Complex Payloads
await fetch(`${process.env.NTFY_BASE_URL}/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
topic: "build-status",
title: "Build Failed",
message: `Branch: main\nCommit: abc123\nError: Test suite failed`,
priority: 4,
tags: ["x", "construction"],
click: "https://ci.example.com/builds/456",
attach: "https://ci.example.com/builds/456/log.txt",
}),
});
File Attachments
const fileBuffer = await fs.readFile("./report.pdf");
await fetch(`${process.env.NTFY_BASE_URL}/reports`, {
method: "PUT",
headers: {
Filename: "monthly-report.pdf",
Title: "Monthly Report Ready",
},
body: fileBuffer,
});
Scheduled Delivery
await fetch(`${process.env.NTFY_BASE_URL}/reminders`, {
method: "POST",
headers: {
Title: "Standup Reminder",
At: "tomorrow, 9:00am",
Tags: "calendar",
},
body: "Daily standup in 15 minutes",
});
Anti-Patterns
- Using guessable topic names on the public instance: Anyone can subscribe to
ntfy.sh/alerts; use random topic names or self-host with auth. - Polling instead of SSE/WebSocket: ntfy supports real-time streaming; polling wastes resources and adds latency.
- Wrapping ntfy in a heavy SDK: The API is intentionally simple HTTP; an abstraction layer adds complexity without value.
- Relying on the public instance for production: The public ntfy.sh has rate limits and no SLA; self-host for production workloads.
When to Use
- Developer tool notifications (build status, deploy alerts, cron job failures)
- Server and infrastructure monitoring alerts
- Internal team notifications without vendor signup or billing
- Simple webhook-to-push-notification bridges
- Self-hosted notification infrastructure with minimal operational overhead
Install this skill directly: skilldb add notification-services-skills
Related Skills
Apple Push Notification
Integrate Apple Push Notification service (APNs) for iOS, macOS, and Safari
Courier
Integrate Courier notification orchestration for multi-channel message routing.
Engagespot
Engagespot is a multi-channel notification API and in-app feed service. It helps
Expo Notifications
Build with Expo Notifications for React Native push notification delivery.
Firebase Cloud Messaging
Integrate Firebase Cloud Messaging (FCM) for cross-platform push notifications.
Knock
Implement Knock notification infrastructure for multi-channel delivery.