ntfy-adapter is a customizable bridge for ntfy that fetches, filters, and formats notifications for display in Homepage dashboard widgets or a dedicated full-screen status board. It supports automatic priority-based formatting, individual topic limits, and flexible retention policies.
-
Multi-topic Support: Aggregates notifications from one or more topics.
-
Per-Topic Limits: Displays the last N notifications (Default: 5) for each topic individually.
-
Priority-Based Status: Automatically prefixes messages based on ntfy priority:
-
π¨ Urgent (5+): High-visibility alerts.
-
β οΈ High (4): Warning-level alerts. -
βοΈ Normal (1--3): Standard notifications.
-
-
Flexible Emoji Formatting:
-
Unicode: Use the
**Uprefix (e.g.,**U0001F6A8). -
Shortcodes: Wrap in asterisks (e.g.,
*warning*).
-
-
Smart Timezone Handling: Localized timestamps via the
TZenvironment variable. -
URL Handling: Automatically replaces long URLs with
πto keep feeds clean while maintaining clickability. -
Dedicated Display UI: A built-in, auto-refreshing full-screen status board (
/display).
Notifications are filtered based on their age and priority level:
| Priority | Level | Default Expiry | Env Variable |
|---|---|---|---|
| 5+ | Urgent | 48 Hours | EXPIRY_MAX |
| 4 | High | 24 Hours | EXPIRY_HIGH |
| 1-3 | Normal | 12 Hours | EXPIRY_STANDARD |
- A running ntfy instance (e.g.,
http://192.168.1.50:8080orhttps://ntfy.domain.com) - A running Homepage instance (
https://gethomepage.dev) - Update your ntfy Docker Compose file to enable caching:
environment:
- NTFY_CACHE_FILE=/var/cache/ntfy/cache.db
- NTFY_CACHE_DURATION=48h
# fix for error 409, adjust for your IP if needed
- NTFY_VISITOR_REQUEST_LIMIT_EXEMPT_HOSTS=127.0.0.1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10
volumes:
- /path-to/ntfy/var/cache/ntfy:/var/cache/ntfy- Docker and Docker Compose installed.
Create a docker-compose.yml file:
services:
ntfy-adapter:
image: ghcr.io/chr0nzz/ntfy-adapter:latest
container_name: ntfy-adapter
restart: unless-stopped
ports:
- "5000:5000"
environment:
# --- Connection Settings ---
- NTFY_URL=http://<YOUR_NTFY_IP>:<PORT>
- TZ=America/Toronto
- CLOCK_FORMAT=24h # Set time format 12h or 24h
- ENABLE_DISPLAY=true # Set to false to disable
# --- AUTH ---
# Leave these as empty strings or comment them out if NOT using auth
- NTFY_USER=
- NTFY_PASS=
# --- Notification Limits ---
# Max number of messages returned in the list (Default: 5)
- MAX_NOTIFICATIONS=5
# --- Expiry Settings (Hours) ---
# How long messages stay visible based on priority
- EXPIRY_MAX=48 # Priority 5
- EXPIRY_HIGH=24 # Priority 4
- EXPIRY_STANDARD=12 # Priority 1-3
# --- Visual Customization ---
# Use **U prefix for Unicode or *short_code* for emojis
- EMOJI_MAX=**U0001F6A8
- EMOJI_HIGH=*warning*
- EMOJI_STANDARD=*check_mark*
# --- Optional for Display UI customization ---
volumes:
- ./config.json:/app/config.jsonRun the command:
docker compose up -dRun the container with a single command:
docker run -d \
--name ntfy-adapter \
-p 5000:5000 \
-e NTFY_URL="http://<YOUR_NTFY_IP>:<PORT>" \
-e TZ="America/Toronto" \
ghcr.io/chr0nzz/ntfy-adapter:latestOverride notification expiry:
docker run -d \
--name ntfy-adapter \
-p 5000:5000 \
-e NTFY_URL="http://<YOUR_NTFY_IP>:<PORT>" \
-e TZ="America/Toronto" \
-e EXPIRY_MAX="24" \
-e EXPIRY_HIGH="12" \
-e EXPIRY_STANDARD="6" \
ghcr.io/chr0nzz/ntfy-adapter:latestIf you want to build the image yourself instead of pulling from a registry:
-
Clone the repository:
git clone https://github.com/chr0nzz/ntfy-adapter.git cd ntfy-adapter -
Build the Docker image:
docker build -t ntfy-adapter:local . -
Run your local build:
docker run -d -p 5000:5000 -e NTFY_URL="http://<IP>:<PORT>" ntfy-adapter:local
Add the following to your services.yaml file in Homepage:
You can add one widget per ntfy topic, or group them by separating them with a comma (e.g., media,alerts)
- Notifications:
# href: https://ntfy.domain.com/topic
# icon: sh-ntfy
widget:
type: customapi
# change to your adapter ip and NTFY topic or
# group them by separating them with a comma (e.g., media,alerts)
url: http://<ADAPTER_IP>:5000/notifications?topic=<YOUR_TOPIC>
display: dynamic-list
refreshInterval: 5000
method: get
mappings:
label: time
name: message
target: "{click_url}"The adapter includes a dedicated page for monitors or tablets. Customize it by creating a config.json in the same directory as your Docker Compose file.
{
"theme": "dark",
"auto_scroll": true,
"display_title": "Home Monitoring",
"display_icon": "π ",
"api": {
"host_ip": "192.168.1.10",
"port": "5000",
"topic": "media,alerts,security",
"refresh_rate": 10000
}
}
-
theme:"dark"or"light". -
auto_scroll: Enables vertical scrolling if notifications exceed screen height. -
display_title: Header text (Defaults to topic name if empty). -
display_icon: Header emoji icon. -
api.topic: Comma-separated list of topics to fetch. -
api.refresh_rate: How often (ms) the UI polls for new data.
http://<ADAPTER_IP>:5000/display
| Variable | Description | Example |
|---|---|---|
NTFY_URL |
The URL of your ntfy server | http://192.168.1.10:8080 or https://ntfy.domain.com |
NTFY_USER |
The User of your ntfy server | leave empty if none |
NTFY_PASS |
The Password of your ntfy server | leave empty if none |
ENABLE_DISPLAY |
Toggle the /display UI | true / false |
TZ |
Your local timezone for timestamps | America/Toronto |
CLOCK_FORMAT |
Set time format for timestamps | 24h or 12h |
MAX_NOTIFICATIONS |
Returns the max number of notifications | 5 |
EXPIRY_MAX |
Expiry time (hours) for Urgent notifications | 24 |
EXPIRY_HIGH |
Expiry time (hours) for High notifications | 12 |
EXPIRY_STANDARD |
Expiry time (hours) for Normal notifications | 6 |
EMOJI_MAX |
Emoji for urgent notifications | **U0001F6A8 or *rotating_light* |
EMOJI_HIGH |
Emoji for high notifications | *warning* |
EMOJI_STANDARD |
Emoji for normal notifications | *white_check_mark* |
Issues and pull requests are welcome! Feel free to open a ticket if you have suggestions for new features.
Questions, ideas, or want to show off your setup? Join the xyzlab.dev Discord.
This project is licensed under the GNU General Public License v3.0.
Copyright (C) 2026 chronzz (https://github.com/chr0nzz)


