No description
  • Dockerfile 100%
Find a file
2026-05-31 15:27:48 +02:00
.dockerignore init commit 2026-05-31 15:06:27 +02:00
.env.example init commit 2026-05-31 15:06:27 +02:00
.gitignore init commit 2026-05-31 15:06:27 +02:00
Dockerfile init commit 2026-05-31 15:06:27 +02:00
fly.toml make cheapest possible on fly 2026-05-31 15:27:48 +02:00
notes.md init commit 2026-05-31 15:06:27 +02:00
README.md init commit 2026-05-31 15:06:27 +02:00
server.yml init commit 2026-05-31 15:06:27 +02:00

ntfy-server

Self-hosted ntfy push notification server for UnifiedPush + Matrix/FluffyChat.

See notes.md for architecture, debugging findings, and detailed configuration.

Quick Start

# Clone or cd into this directory
cd ~/Repos/ntfy-server

# Build and run locally
docker build -t ntfy-server .
docker run -p 8080:80 ntfy-server

# Or deploy to fly.io
fly launch

Deploy to Fly.io

# Install flyctl if you haven't: https://fly.io/docs/hands-on/install-flyctl/

# Launch (creates app, picks region, deploys)
fly launch --name your-ntfy-app

# Set secrets from .env file (copy .env.example → .env, fill in your values)
# Make sure .env is NOT quoted — fly secrets import reads values literally.
fly secrets import < .env

# Redeploy with the secrets
fly deploy

After deploy, configure your ntfy Android app to use https://your-ntfy-app.fly.dev as the server URL.

Configuration & Secrets

Never commit sensitive values to git. This repo keeps server.yml minimal and safe. All app-specific or sensitive config is injected via environment variables at runtime.

ntfy supports env vars with the NTFY_ prefix. On Fly.io, use fly secrets set — values are encrypted in Fly's vault and injected as env vars at boot.

Common env vars to set via secrets

Secret Purpose
NTFY_BASE_URL Public URL of your server (e.g. https://your-ntfy-app.fly.dev)
NTFY_AUTH_DEFAULT_ACCESS Set to deny-all to lock down the server
NTFY_AUTH_ACCESS Set to *:up*:write to allow UnifiedPush (required!)
NTFY_AUTH_USERS Pre-provision users: user:$bcrypt:role
NTFY_AUTH_TOKENS Pre-provision access tokens

Why *:up*:read-write is required (and the security trade-off)

UnifiedPush topics start with up (e.g. upuG4jG0CayXnn). The ntfy Android app subscribes to these topics via WebSocket (needs read), and the Matrix Push Gateway publishes to them (needs write).

Topic creation in ntfy is implicit — topics don't exist until someone subscribes or publishes to them. With *:up*:read-write, an anonymous user who knows the exact topic name can subscribe to it, which creates the topic internally. They cannot discover or list topics.

What this means:

  • Anyone who discovers your server URL could subscribe to an up* topic — but only if they know the exact 20+ character random name
  • Topic names are practically unguessable (64^20 combinations)
  • Rate limiting on requests (60 burst, 1 per 5s) makes brute-forcing infeasible
  • Additional protection: visitor-topic-creation-limit (default 100 burst, 1 per minute) prevents topic squatting
  • The topic URL itself acts as the secret (like an unlisted link)

If you remove *:up*:read-write, UnifiedPush stops working. This is a fundamental trade-off of the protocol.

See notes.md for full ACL explanation and security considerations.

Official documentation

User Management

Users are managed via CLI inside the container, not through the web UI.

The ntfy web UI has a "Manage Users" section in Settings, but that is client-side only — it stores credentials in your browser's localStorage for convenience (like a password manager). It does not create users on the server.

Creating your first admin user

# SSH into the running Fly machine
fly ssh console

# Create an admin user (admin = read-write access to everything)
ntfy user add --role=admin your_username
# You'll be prompted to set a password

# Generate an access token for scripts/apps
ntfy token add your_username
# Returns: tk_xxxxxxxx...

# Exit the container
exit

What the admin role does

The admin role is a shorthand for "access to everything":

  • Read and write to all topics without needing explicit ACL entries
  • Manage other users and tokens via CLI
  • Access the web UI without per-topic restrictions

Regular user role requires explicit ACL grants per topic.

Testing in the web UI

Once you have created a user via CLI:

  1. Visit https://your-ntfy-app.fly.dev in your browser
  2. Click SettingsManage Users (client-side password storage)
  3. Click + Add user and enter your server URL + username + password
  4. The web UI will now use these credentials for all API calls
  5. You can subscribe to private topics, send messages, etc.

Common CLI commands

# List all users
ntfy user list

# Change a password
ntfy user change-pass username

# Create a regular (non-admin) user
ntfy user add someuser

# Grant access to a specific topic
ntfy access someuser mytopic read-write

# List access tokens
ntfy token list your_username

Files

  • Dockerfile — Docker image based on official ntfy image
  • server.yml — ntfy server configuration
  • notes.md — Architecture, research, debugging notes

Ports

  • 8080 — HTTP (internal; fly.io handles TLS termination)

Volumes

  • /data/ntfy — Persistent SQLite databases (cache + auth) on Fly.io volume