Receive push notifications on your Mac from any source β servers, IoT devices, home automation, CI pipelines, or custom scripts. No account required, works with the public ntfy.sh service or your own self-hosted server.
ntfy-macos is a native macOS client that subscribes to ntfy topics and delivers rich notifications with SF Symbols, images, and interactive buttons. Trigger shell scripts automatically when messages arrive.
- Native macOS Notifications: Rich notifications with SF Symbols and local images
- Multi-Server Support: Connect to multiple ntfy servers simultaneously
- Emoji Tags: Automatic conversion of ntfy tags to emojis in notification titles
- Interactive Actions: Add custom buttons to notifications that execute scripts or open URLs
- Automatic Script Execution: Run shell scripts automatically when messages arrive
- Silent Notifications: Receive messages without displaying notification banners
- Secure Authentication: Store tokens securely in macOS Keychain
- Robust Reconnection: Handles network interruptions and sleep/wake gracefully
- Priority Mapping: Maps ntfy priority levels to macOS interruption levels (critical, time-sensitive)
- Menu Bar App: Runs in the menu bar with quick access to config and reload
- Live Config Reload: Configuration changes are detected and applied automatically
- Config Validation: Warns about unknown keys and typos in the menu bar
- Click to Open: Click notifications to open in browser (configurable per topic)
- Automatic Permission Request: Prompts for notification permission on first launch
- Local Notification Server: Built-in HTTP server on localhost for scripts to trigger notifications
- Settings Window: Native SwiftUI interface for server and topic configuration with real-time connection status
# Add the tap
brew tap laurentftech/ntfy-macos
# Install
brew install ntfy-macos# Clone the repository
git clone https://github.com/laurentftech/ntfy-macos.git
cd ntfy-macos
# Build the app bundle
./build-app.sh
# Install
sudo cp -r .build/release/ntfy-macos.app /Applications/# Update via Homebrew
brew update && brew upgrade ntfy-macos
# Restart the service to apply the update
brew services restart ntfy-macosNote: Homebrew installation requires Xcode (not just Command Line Tools) because the app is built from source.
- Initialize Configuration
ntfy-macos initThis creates a sample configuration at ~/.config/ntfy-macos/config.yml.
- Edit Configuration
Edit the configuration file to add your servers and topics:
servers:
- url: https://ntfy.sh
topics:
- name: alerts
icon_symbol: bell.fill
- url: https://your-private-server.com
token: tk_yourtoken
topics:
- name: deployments
icon_symbol: arrow.up.circle.fill
auto_run_script: ~/scripts/deploy-handler.sh- (Optional) Store Authentication Token in Keychain
ntfy-macos auth add https://ntfy.sh tk_yourtoken- Start the Service
# Using Homebrew services (recommended - auto-restarts on crash)
brew services start ntfy-macos
# Or run directly
ntfy-macos serveOn first launch, the app will automatically request notification permission.
The app runs in the menu bar with options to:
- Server Status: Shows connection state for each server (green=connected, red=disconnected, orange flashing=connecting)
- Settings: Open the Settings window to configure servers, topics, and local server (β,)
- Show Config in Finder: Reveal config directory
- Reload Config: Apply configuration changes (βR)
- View Logs: Open log files (with automatic rotation) (βL)
- About: Credits and links
- Quit: Stop the service
- (Optional) Add to Launchpad
sudo ln -sf /usr/local/opt/ntfy-macos/ntfy-macos.app /Applications/The configuration file is located at ~/.config/ntfy-macos/config.yml.
For a complete list of all configuration options and detailed examples, please see the config-examples.yml file.
servers:
- url: https://ntfy.sh
topics:
- name: alerts
icon_symbol: bell.fill
actions:
- title: Open Dashboard
type: view
url: "https://dashboard.example.com"
- url: https://your-private-server.com
token: tk_yourtoken
topics:
- name: deployments
icon_symbol: arrow.up.circle.fill
auto_run_script: ~/scripts/deploy-handler.shlocal_server_port(optional): Port for the local notification HTTP server (e.g.,9292). Disabled if omitted.
url(required): Server URLtoken(optional): Authentication tokentopics(required): List of topics
name(required): Topic nameicon_symbol(optional): SF Symbol nameicon_path(optional): Path to a local image fileauto_run_script(optional): Script to execute on every messagesilent(optional): Iftrue, skip notification bannerclick_url(optional): Custom URL to open on clickactions(optional): List of interactive buttons
title(required): Button labeltype(required):script,view,shortcut, orapplescriptpath(forscriptandapplescriptfile): Absolute path to the script fileurl(forview): URL to openname(forshortcut): macOS Shortcut namescript(forapplescriptinline): AppleScript source code
Note: Config-defined actions ALWAYS override any actions sent with the ntfy message.
Hereβs a summary of how actions are handled:
| Action type | ntfy protocol | ntfy-macos (payload) | ntfy-macos (config.yml) |
|---|---|---|---|
| view | β Standard | β Supported | β Supported |
| http | β Standard | β Supported | β Not supported (by design) |
| broadcast | β Standard | β Ignored (Android-only) | β Not applicable |
| script | β | β Blocked | β Supported |
| applescript | β | β Blocked | β Supported |
| shortcut | β | β Blocked | β Supported |
script,applescript, andshortcutactions are only available via config.yml β they cannot be triggered from message payloads. This prevents remote code execution.
Note: applescript and shortcut are ntfy-macos client-specific action types. Only view, http, broadcast, and dismiss are part of the official ntfy protocol.
Start the notification service:
ntfy-macos serveManage authentication tokens in Keychain:
# Add a token
ntfy-macos auth add <server-url> <token>
# List all stored tokens
ntfy-macos auth list
# Remove a token
ntfy-macos auth remove <server-url>Keychain tokens take priority over tokens in the YAML configuration.
Send a test notification (and request permissions):
ntfy-macos test-notify --topic <NAME>Create a sample configuration file:
ntfy-macos initDisplay help information:
ntfy-macos helpScripts receive the message body as the first argument ($1) and full message context as environment variables:
#!/bin/bash
MESSAGE="$1"
# Environment variables available:
# NTFY_ID - Unique message ID
# NTFY_TOPIC - Topic name
# NTFY_TIME - Unix timestamp
# NTFY_EVENT - Event type (always "message")
# NTFY_TITLE - Message title (if set)
# NTFY_MESSAGE - Message body (if set)
# NTFY_PRIORITY - Priority level 1-5 (if set)
# NTFY_TAGS - Comma-separated tags (if set)
# NTFY_CLICK - Click URL (if set)
echo "[$NTFY_TOPIC] $NTFY_TITLE: $MESSAGE"
# Your automation logic hereScripts are executed with an enhanced PATH:
/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
This ensures Homebrew-installed tools are available.
chmod +x /path/to/your/script.shScripts are only executed if explicitly configured in your local config.yml file. ntfy-macos will never execute arbitrary code from incoming messages.
Best practices:
- Only configure scripts that you trust and have reviewed
- Use absolute paths to scripts
- For public topics, avoid
auto_run_script - Keep your configuration file secure (
chmod 600 ~/.config/ntfy-macos/config.yml) - For sensitive automation, use a self-hosted ntfy server with authentication
- Use
allowed_schemesandallowed_domainsto restrict which URLs can be opened.
ntfy-macos can run a local HTTP server on localhost that allows scripts and local tools to trigger macOS notifications directly, without going through an external ntfy server.
flowchart LR
subgraph External
ntfy["π ntfy server"]
cron["β° cron / launchd"]
ci["π§ CI pipeline"]
end
subgraph ntfy-macos
client["π‘ ntfy client"]
script["π auto_run_script"]
local["π₯οΈ Local HTTP server
127.0.0.1:9292"]
notif["π macOS notification"]
end
ntfy -- "push messages" --> client
client -- "show notification" --> notif
client -- "run script + NTFY_* env" --> script
script -- "POST /notify" --> local
local -- "follow-up" --> notif
cron -- "POST /notify" --> local
ci -- "POST /notify" --> local
style ntfy fill:#4a90d9,stroke:#2c5f8a,color:#fff
style cron fill:#6c757d,stroke:#495057,color:#fff
style ci fill:#6c757d,stroke:#495057,color:#fff
style client fill:#28a745,stroke:#1e7e34,color:#fff
style script fill:#ffc107,stroke:#d39e00,color:#000
style local fill:#17a2b8,stroke:#117a8b,color:#fff
style notif fill:#e83e8c,stroke:#c21c6b,color:#fff
Add local_server_port to the root of your config.yml:
local_server_port: 9292
servers:
- url: https://ntfy.sh
topics:
- name: alertsSend a POST request to trigger a notification:
curl -X POST http://127.0.0.1:9292/notify \
-H "Content-Type: application/json" \
-d '{"title": "Build Complete", "message": "Project compiled successfully", "priority": 3, "tags": ["white_check_mark"]}'POST /notify
| Field | Type | Required | Description |
|---|---|---|---|
title |
string | Yes | Notification title |
message |
string | Yes | Notification body |
priority |
integer | No | Priority 1-5 (maps to macOS levels) |
tags |
string[] | No | Emoji tags (e.g., ["warning", "fire"]) |
GET /health - Returns {"status": "ok"} for health checks.
Combine auto_run_script with the local server to create rich feedback loops:
#!/bin/bash
# auto_run_script for topic "deployments"
# Receives message via $1 and env vars, then triggers a local follow-up notification
RESULT=$(deploy.sh "$NTFY_MESSAGE" 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
curl -s -X POST http://127.0.0.1:9292/notify \
-d "{\"title\": \"Deploy Success\", \"message\": \"$NTFY_MESSAGE deployed successfully\", \"tags\": [\"white_check_mark\"]}"
else
curl -s -X POST http://127.0.0.1:9292/notify \
-d "{\"title\": \"Deploy Failed\", \"message\": \"$RESULT\", \"priority\": 4, \"tags\": [\"x\"]}"
fi- Binds to
127.0.0.1only (not accessible from the network) - Maximum request body size: 4 KB
- No code execution - only triggers notifications
- Disabled by default (requires
local_server_portin config) - Configure via Settings window (click lock to edit, enter port)
- Test your setup with the built-in curl example (click Copy button)
ntfy priority levels map to macOS interruption levels:
- Priority 5 β Critical (bypasses Focus modes)
- Priority 4 β Time Sensitive (prominently displayed)
- Priority 1-3 β Active (normal notifications)
You can use any SF Symbol name for icons. Browse all symbols using the SF Symbols app (free from Apple).
macOS native notifications only support plain text and cannot render formatted markdown. ntfy-macos automatically strips markdown syntax from messages for cleaner display.
ntfy supports emoji shortcodes in the Tags field. These are automatically converted to emojis and prepended to the notification title.
Example: Tags: warning,fire β
- Focus Mode: Check if Focus/Do Not Disturb is enabled.
- Permissions: Go to System Settings β Notifications β ntfy-macos and ensure notifications are allowed.
- Background execution: If launched via
brew services, ensure the app has permission to run in the background.
If the permission dialog is unresponsive:
- Stop the service:
brew services stop ntfy-macos - Grant permission manually: System Settings β Notifications β ntfy-macos β Allow Notifications
- Restart the service:
brew services start ntfy-macos
- Logs:
~/.local/share/ntfy-macos/logs/ntfy-macos.log - Test Notifications:
ntfy-macos test-notify --topic test - Connection Issues: Verify server URL and token.
- Script Not Executing: Ensure the script is executable (
chmod +x).
- Swift 6: Modern Swift with strict concurrency
- URLSession: Native streaming JSON support
- UserNotifications: Rich macOS notifications
- Security Framework: Keychain integration
- Yams: YAML parsing
- Foundation & AppKit: Core macOS frameworks
Contributions are welcome! Please open issues or pull requests on GitHub.
MIT License
This project is a third-party client for ntfy, created by Philipp C. Heckel.
- ntfy - Simple pub-sub notification service
- ntfy-android - Official Android app
- ntfy-ios - Official iOS app
For bugs and feature requests, please open an issue on GitHub.
