Skip to content

Self-hosted push notification service for personal apps, homelab services, and scripts to send alerts to iOS via HTTP POST, without requiring native APNs.

📌 Quick Reference

Component

Details

Service URL

https://ntfy.parthjain.tech

Docker Path

~/docker/ntfy

CLI Path

~/bin/notify

Config File

~/docker/ntfy/config/server.yml

Primary Topic

parth-apps

Network

proxy (Docker external network)

🏗️ Architecture & Networking

Flow: Internet ➔ Cloudflare ➔ Cloudflare Tunnel (cloudflared) ➔ Docker proxy network ➔ http://ntfy:80 ➔ ntfy container.

Ports: ntfy does not expose host ports. It is only accessible via the proxy network.

Verification: Both cloudflared and ntfy must appear when running docker network inspect proxy.

Tunnel Target: http://ntfy:80

Cloudflare Access: Do NOT put CF Access authentication in front of ntfy; it breaks the iOS app's direct communication.

📂 Docker Configuration

Directory Structure (~/docker/ntfy/):

├── docker-compose.yml ├── cache/ │ ├── auth.db │ └── cache.db └── config/ └── server.yml

docker-compose.yml:

services: ntfy: image: binwiederhier/ntfy:latest container_name: ntfy command: - serve environment: TZ: Asia/Kolkata volumes: - ./cache:/var/cache/ntfy - ./config:/etc/ntfy restart: unless-stopped networks: - proxy

networks: proxy: external: true

⚙️ Server & iOS Configuration

server.yml key settings:

base-url: "https://ntfy.parthjain.tech" upstream-base-url: "https://ntfy.sh" cache-file: "/var/cache/ntfy/cache.db" auth-file: "/var/cache/ntfy/auth.db" auth-default-access: "deny-all"

Note: Both base-url and upstream-base-url are required for instant iOS push delivery.

🔐 Authentication & Tokens

Anonymous access is disabled. Authentication is required.

Primary User: parth (has read/write access to topic *)

Check Users: docker exec -it ntfy ntfy user list

Check ACLs: docker exec -it ntfy ntfy access

List Tokens: docker exec -it ntfy ntfy token list parth

Create Token: docker exec -it ntfy ntfy token add parth

Security Warning: Never commit access tokens (tk_...) to Git or place them directly in source code.

🗂️ Topics & Priorities

Topic Strategy:

parth-apps (Current): Reader, finance, scrapers, background jobs.

parth-homelab (Planned): Backups, docker, disk usage, updates.

parth-urgent (Planned): Security alerts, disk/backup failures (Immediate attention).

Supported Priorities:

min, low, default, high, max (Reserve max for critical failures).

💻 notify CLI Adapter

A lightweight Python adapter (~/bin/notify) is used to abstract the ntfy HTTP API. It requires no virtual environments or pip dependencies.

Required Environment Variables (e.g., in ~/.zshrc):

export NTFY_URL="https://ntfy.parthjain.tech" export NTFY_TOKEN="tk_REDACTED" export NTFY_TOPIC="parth-apps" export PATH="$HOME/bin:$PATH"

Basic Usage:

Bash

Standard notification

notify "Hello from the server"

With source and title

notify --source homelab --title "Backup complete" "Nightly backup completed successfully"

With priority and URL

notify --source reader --title "Article ready" --priority high --url "https://reader.parthjain.tech/articles/123" "Processing finished"

Cron & Script Usage:

Cron jobs must use the absolute path (/home//bin/notify) as interactive shell paths and env vars may not load.

Keep the source naming short and stable (e.g., backup, reader, docker). Titles render as [source] Title.

Direct API Testing (Without CLI):

Bash

curl -i -H "Authorization: Bearer $NTFY_TOKEN" -d "curl auth test" "$NTFY_URL/$NTFY_TOPIC"

🛠️ Troubleshooting

HTTP 403 Forbidden:

Verify the user's ACLs and token validity.

If curl works but the Python script fails, check the User-Agent. Cloudflare blocks default Python user agents. The CLI explicitly uses User-Agent: parth-notify/1.0 to bypass this.

Docker Compose Errors (not found):

Ensure you are executing commands from ~/docker/ntfy.

Alternatively use docker compose -f ~/docker/ntfy/docker-compose.yml logs.

Checking Logs & Status:

Logs: docker compose logs --tail=50 -f ntfy (Run from ~/docker/ntfy)

Status: docker ps --filter name=ntfy

📐 Design Rules

Notifications are side-effects: A failed notification must not cause the primary application/script to fail.

Keep it simple: Do not implement databases, delivery tracking, message brokers (Redis/RabbitMQ), or retry queues unless an application explicitly demands it. ntfy is the service.

Do not preemptively over-engineer: Features like separate app tokens, tags, actions, or Apprise routing should only be added when actively needed.