- Dockerfile 100%
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| Dockerfile | ||
| fly.toml | ||
| notes.md | ||
| README.md | ||
| server.yml | ||
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:
- Visit
https://your-ntfy-app.fly.devin your browser - Click Settings → Manage Users (client-side password storage)
- Click + Add user and enter your server URL + username + password
- The web UI will now use these credentials for all API calls
- 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 imageserver.yml— ntfy server configurationnotes.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