Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Ntfy Ubuntu
GUIDEInstall Guides

How to Install Ntfy on Ubuntu 24.04 VPS — Self-Hosted Push Notifications

26 min read

How to Install Ntfy on Ubuntu 24.04 VPS — Self-Hosted Push Notifications

When a server crashes at 3 a.m., when your backup job finishes, when your doorbell camera detects motion, when a GitHub Action fails -- you want to know immediately, on the device in your pocket, without paying a SaaS per-device fee or exposing data to a third party. Self-hosted push notifications solve this, and ntfy is the most ergonomic tool for the job. This guide walks you through installing ntfy on an Ubuntu 24.04 VPS, configuring authentication and ACLs, enabling iOS push support, and wiring it up to Home Assistant, Alertmanager, webhooks, and plain curl.

Skip the setup? Our Starter VPS plan gives you 2 vCPU, 4 GB RAM, and 50 GB NVMe for EUR 7.99/month -- plenty for a self-hosted ntfy instance plus monitoring and reverse proxy.

Table of Contents

  • What is Ntfy?
  • Why Self-Host Push Notifications?
  • Ntfy vs. Pushover vs. Pushbullet vs. Gotify
  • Prerequisites
  • Step 1: Update System Packages
  • Step 2: Add the Heckel.io APT Repository
  • Step 3: Install Ntfy
  • Step 4: Configure server.yml
  • Step 5: Start the Service and Publish a Test Message
  • Step 6: Enable Authentication and ACLs
  • Step 7: Attachment Caching
  • Step 8: iOS Push Configuration
  • Step 9: TLS with Caddy
  • Integrations
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Ntfy?

    Ntfy (pronounced "notify") is an open-source HTTP-based pub-sub notification server. Publishers send messages to a named topic with a single HTTP request; subscribers listening on that topic receive the message instantly on any platform -- Android, iOS, web, desktop, or raw CLI. There are no SDKs to pull in, no access tokens to rotate per client, and no protocol to learn. If you can run curl, you can publish a notification:

    bash
    curl -d "Backup finished" https://ntfy.yourdomain.com/backups

    That single line reaches every device subscribed to the backups topic. This simplicity is what makes ntfy irreplaceable for infrastructure work. Cron jobs, shell scripts, Kubernetes hooks, CI pipelines, and even smart home devices can publish notifications without any client library. The full API and documentation live at docs.ntfy.sh.

    Ntfy supports rich features beyond plain text: priority (min through max, with urgent bypassing Do Not Disturb), tags that render as emoji, action buttons that trigger HTTP calls or open URLs, file attachments, click actions, markdown formatting, scheduled delivery, and email forwarding. Every feature except phone-call delivery works in the open-source self-hosted binary.

    Typical use cases include disk space and backup alerts, Uptime Kuma downtime pings, motion detection from security cameras, CI/CD failures, Prometheus Alertmanager routing, and webhook receivers for GitHub, GitLab, or Stripe. Many individuals adopt it as a private replacement for Pushover or Pushbullet.

    Why Self-Host Push Notifications?

    Managed notification services work until they don't. Once you have more than a few devices or push mildly sensitive content, the tradeoffs bite:

    • Data privacy -- Alerts contain server names, IPs, error messages, customer IDs. Self-hosting keeps all of it on infrastructure you control.
    • No per-device licensing -- Pushover charges per platform. Pushbullet caps free accounts at 100 messages/month. Ntfy has no artificial caps.
    • Unlimited topics -- Create one per server, environment, or customer. Topics are zero-cost and created on first publish.
    • Control over retention -- Configure message cache duration, attachment size, and expiry to meet GDPR and internal policies.
    • Reliable under your SLA -- You decide uptime and restart windows.
    • Works in air-gapped networks -- Runs entirely inside a VPN if needed.
    • Integrates with anything -- The publish API is plain HTTP, so every tool already supports it.

    CloudCore / Starter Pricing for Ntfy

    Ntfy's resource footprint is tiny, which makes it perfect for an entry-level VPS:

    PlanvCPURAMStorageMonthlyBest for
    Starter24 GB50 GB NVMeEUR 7.99Personal use, homelab, up to ~1,000 subscribers
    CloudCore Professional612 GB100 GB NVMeEUR 19.99Small team, CI/CD alerts, 5k-10k subscribers
    CloudCore Business824 GB200 GB NVMeEUR 29.99Multi-service alerting, large topic list, 10k+ subscribers
    For 95% of self-hosted ntfy deployments, the Starter plan is more than enough. Ntfy idles at about 25 MB of RAM and scales nearly linearly with subscribers.

    Ntfy vs. Pushover vs. Pushbullet vs. Gotify

    FeatureNtfy (self-hosted)PushoverPushbulletGotify
    LicenseApache 2.0, open sourceProprietary SaaSProprietary SaaSMIT, open source
    Self-hostableYesNoNoYes
    Per-device costFreeUSD 5 one-time per platformFree (limited) / USD 5/mo ProFree
    Publish APIcurl -d "msg" URL/topicToken + user key requiredOAuth + SDKApp token required
    Topic modelPublic or ACL-protected topicsDelivery groupsDevice-to-deviceApplications
    iOS supportYes (via upstream APNs)YesYes (Pro)No native app (third-party)
    Android supportNative appNative appNative appNative app
    Web subscribe UIYes (built-in)LimitedLimitedYes
    AttachmentsYes, up to configured limitImages onlyFiles up to 25 MBYes (via message URL)
    Priorities5 levels5 levelsNone0-10 scale
    Action buttonsYesYes (paid)NoNo
    Webhook receiverAny HTTP clientRequires Pushover formatProprietary formatRequires Gotify format
    Typical useInfra alerts, scripts, smart homePersonal remindersCross-device syncSimple self-hosted alerts
    Ntfy wins on API simplicity, topic flexibility, and feature parity between free self-hosted and paid cloud. Gotify is lighter and has no external dependencies for iOS, but it lacks a native iOS app and its publish API requires per-application tokens. Pushover is polished but locked to a single vendor and not self-hostable. Pushbullet is aimed at device-to-device file sharing rather than infrastructure alerting.

    If you need a native iOS app, server-side topics, and publish-by-curl, ntfy is the right choice.

    Prerequisites

    Before starting, you need:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access
    • SSH access to your server
    • At least 1 GB of RAM (4 GB recommended for headroom)
    • At least 10 GB of free disk space (more if you plan heavy attachment use)
    • A domain name pointing to your VPS (required for TLS and iOS push support) -- configure an A record like ntfy.yourdomain.com -> your VPS IP
    • Ports 80 and 443 open in your firewall for HTTP and HTTPS
    Recommended Plan: Starter
    >
    The Starter VPS plan is perfect for ntfy:
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 7.99/month
    >
    That leaves room for ntfy, Caddy, a monitoring agent, and several other light services on the same box.

    Connect to your server:

    bash
    ssh root@your-server-ip

    Step 1: Update System Packages

    Always start with a fresh package index and latest security patches:

    bash
    sudo apt update && sudo apt upgrade -y

    Install a few helper utilities you will need throughout the guide:

    bash
    sudo apt install -y curl ca-certificates gnupg apt-transport-https debian-archive-keyring

    If your kernel was updated, reboot:

    bash
    sudo reboot

    Reconnect after a minute.

    Step 2: Add the Heckel.io APT Repository

    Ntfy is developed by Philipp Heckel, who publishes signed Debian packages via his apt repository at archive.heckel.io. Adding this repo gives you reliable, versioned upgrades through apt upgrade going forward.

    Create the keyring directory and import the GPG key:

    bash
    sudo install -d -m 0755 /etc/apt/keyrings
    curl -fsSL https://archive.heckel.io/apt/pubkey.txt | sudo gpg --dearmor -o /etc/apt/keyrings/archive.heckel.io.gpg

    Add the repository to your sources:

    bash
    sudo tee /etc/apt/sources.list.d/archive.heckel.io.list > /dev/null <<'EOF'
    deb [arch=amd64 signed-by=/etc/apt/keyrings/archive.heckel.io.gpg] https://archive.heckel.io/apt debian main
    EOF

    On ARM64 hosts (Ampere, AWS Graviton), change arch=amd64 to arch=arm64.

    Refresh the package index:

    bash
    sudo apt update

    Expected output (abbreviated):

    text
    Get:1 https://archive.heckel.io/apt debian InRelease [3,000 B]
    Get:2 https://archive.heckel.io/apt debian/main amd64 Packages [5,000 B]
    Reading package lists... Done

    Step 3: Install Ntfy

    With the repo configured, install the ntfy package:

    bash
    sudo apt install -y ntfy

    Expected output:

    text
    Setting up ntfy (2.11.0) ...
    Created symlink /etc/systemd/system/multi-user.target.wants/ntfy.service -> /lib/systemd/system/ntfy.service.

    The installer creates:

  • The binary at /usr/bin/ntfy
  • A system user named ntfy
  • A default configuration file at /etc/ntfy/server.yml
  • A systemd unit at /lib/systemd/system/ntfy.service
  • A data directory at /var/cache/ntfy
  • Verify the version:

    bash
    ntfy --version

    Expected output:

    text
    ntfy version 2.11.0 (c3db03a, runtime=go1.22.3, built=2026-03-20T14:22:11Z)

    Do not start the service yet -- we want to edit server.yml first.

    Step 4: Configure server.yml

    The default config file is heavily commented. Back it up before editing:

    bash
    sudo cp /etc/ntfy/server.yml /etc/ntfy/server.yml.bak

    Replace it with a production-oriented baseline:

    bash
    sudo tee /etc/ntfy/server.yml > /dev/null <<'EOF'
    

    Public base URL of your ntfy instance

    base-url: "https://ntfy.yourdomain.com"

    Listen on all interfaces, port 2586 (we'll put Caddy in front)

    listen-http: ":2586"

    Behind a reverse proxy, trust X-Forwarded-For

    behind-proxy: true

    Where to store the sqlite message cache and how long to keep messages

    cache-file: "/var/cache/ntfy/cache.db" cache-duration: "12h"

    Attachment storage

    attachment-cache-dir: "/var/cache/ntfy/attachments" attachment-total-size-limit: "5G" attachment-file-size-limit: "15M" attachment-expiry-duration: "3h"

    Rate limits (generous defaults for personal use)

    visitor-request-limit-burst: 60 visitor-request-limit-replenish: "5s" visitor-message-daily-limit: 10000

    Web UI

    web-root: "app"

    Logging

    log-level: "INFO" log-format: "text" log-file: "/var/log/ntfy/ntfy.log" EOF

    Replace ntfy.yourdomain.com with your actual domain. Create the log directory:

    bash
    sudo mkdir -p /var/log/ntfy
    sudo chown ntfy:ntfy /var/log/ntfy
    sudo chown ntfy:ntfy /var/cache/ntfy

    Key configuration fields explained:

    • base-url -- The public URL clients will use. Required for iOS push and click actions.
    • listen-http -- The local port ntfy binds to. We use 2586 because 80 and 443 will be handled by Caddy.
    • cache-file -- SQLite database that holds recent messages for late subscribers. cache-duration: 12h means a subscriber connecting now will see messages from the last 12 hours.
    • attachment-* -- Controls file uploads. Total disk budget is 5 GB; each attachment is capped at 15 MB and auto-deleted after 3 hours.
    • web-root: app -- Serves the web UI at /. Set to disable to only expose the API.
    • behind-proxy: true -- Tells ntfy to use X-Forwarded-For so per-IP rate limits work correctly behind Caddy.

    Step 5: Start the Service and Publish a Test Message

    Reload systemd and start ntfy:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable --now ntfy

    Check the service status:

    bash
    sudo systemctl status ntfy

    Expected output:

    text
    ● ntfy.service - ntfy server
         Loaded: loaded (/lib/systemd/system/ntfy.service; enabled; preset: enabled)
         Active: active (running) since Thu 2026-04-16 09:00:00 UTC; 3s ago
       Main PID: 4312 (ntfy)
          Tasks: 8 (limit: 4587)
         Memory: 25.4M
            CPU: 142ms

    Confirm the HTTP endpoint:

    bash
    curl http://127.0.0.1:2586/v1/health

    Expected output:

    json
    {"healthy":true}

    Publish a test message to a topic called hello:

    bash
    curl -d "First ntfy message" http://127.0.0.1:2586/hello

    Expected output:

    json
    {"id":"8fxRzVw6pM3N","time":1713260800,"expires":1713304000,"event":"message","topic":"hello","message":"First ntfy message"}

    Open another shell and subscribe:

    bash
    curl -s http://127.0.0.1:2586/hello/json

    In the first shell, publish again -- the subscriber prints the message in real time. The core is working.

    Step 6: Enable Authentication and ACLs

    By default every topic is public. Anyone who knows or guesses a topic name can read or publish. For any real use you want authentication and per-topic ACLs.

    Ntfy stores users in a SQLite file referenced by auth-file. Add these entries to /etc/ntfy/server.yml:

    yaml
    # Authentication
    auth-file: "/var/lib/ntfy/user.db"
    auth-default-access: "deny-all"

    deny-all flips the default so unauthenticated clients cannot read or publish anything. Create the auth directory:

    bash
    sudo mkdir -p /var/lib/ntfy
    sudo chown ntfy:ntfy /var/lib/ntfy

    Restart to apply:

    bash
    sudo systemctl restart ntfy

    Create Admin and User Accounts

    The ntfy user CLI manages the auth database. Always run it with sudo -u ntfy so file permissions stay correct:

    bash
    sudo -u ntfy ntfy user add --role=admin admin

    You will be prompted for a password twice. Admin users can read and write every topic.

    Create a regular user for scripts and devices:

    bash
    sudo -u ntfy ntfy user add alerter

    Create a separate user for your phone:

    bash
    sudo -u ntfy ntfy user add phone

    List users:

    bash
    sudo -u ntfy ntfy user list

    Expected output:

    text
    user admin (role: admin, tier: none)
    
    • read-write access to all topics (admin role)
    user alerter (role: user, tier: none)
    • no topic-specific permissions
    • no access to any topic (server config: deny-all)
    user phone (role: user, tier: none)
    • no topic-specific permissions
    • no access to any topic (server config: deny-all)

    Define Topic ACLs

    Grant alerter write access to the servers and backups topics:

    bash
    sudo -u ntfy ntfy access alerter servers write-only
    sudo -u ntfy ntfy access alerter backups write-only

    Grant phone read access to every topic starting with servers or backups:

    bash
    sudo -u ntfy ntfy access phone "servers*" read-only
    sudo -u ntfy ntfy access phone "backups*" read-only

    The asterisk lets one rule cover servers, servers-prod, servers-staging, etc.

    Publish with Authentication

    Every request now needs either a bearer token or basic auth:

    bash
    curl \
      -u alerter:yourpassword \
      -d "Nightly backup complete" \
      http://127.0.0.1:2586/backups

    Or with a token (safer for scripts). Create one:

    bash
    sudo -u ntfy ntfy token add alerter

    Expected output:

    text
    token tk_abcdefghijklmnopqrstuvwxyz0123 created for user alerter, never expires

    Publish with the token:

    bash
    curl \
      -H "Authorization: Bearer tk_abcdefghijklmnopqrstuvwxyz0123" \
      -d "Token-based auth works" \
      http://127.0.0.1:2586/backups

    Tokens are revocable (ntfy token del) without touching the user's password.

    Step 7: Attachment Caching

    Ntfy supports attachments up to the configured size limit (15 MB in our config). Files are cached in attachment-cache-dir and auto-expire after attachment-expiry-duration.

    Attach a file when publishing:

    bash
    curl \
      -u alerter:yourpassword \
      -T /var/log/backup.log \
      -H "Filename: nightly-backup.log" \
      -H "Title: Backup Report" \
      http://127.0.0.1:2586/backups

    The response includes an attachment object with the direct URL:

    json
    {
      "id": "p8fMv9RzQ2",
      "time": 1713260900,
      "event": "message",
      "topic": "backups",
      "title": "Backup Report",
      "message": "You received a file: nightly-backup.log",
      "attachment": {
        "name": "nightly-backup.log",
        "type": "text/plain",
        "size": 421038,
        "expires": 1713271700,
        "url": "https://ntfy.yourdomain.com/file/p8fMv9RzQ2.log"
      }
    }

    Mobile apps render image attachments inline. Log files and PDFs appear as tappable downloads.

    Pruning Orphaned Attachments

    attachment-total-size-limit: 5G is a hard cap -- new uploads beyond that are rejected. Ntfy also runs an internal janitor that deletes attachments once attachment-expiry-duration elapses. If you need to manually prune:

    bash
    sudo systemctl stop ntfy
    sudo find /var/cache/ntfy/attachments -type f -mtime +1 -delete
    sudo systemctl start ntfy

    Step 8: iOS Push Configuration

    iOS requires Apple Push Notification service (APNs) for any app to receive background pushes. Building your own APNs pipeline requires an Apple developer account and a signed certificate. To avoid that, the official iOS app uses a "poke" mechanism: when your self-hosted ntfy server has a message for an iOS subscriber, it sends a tiny wake-up ping through the hosted ntfy.sh instance, which is registered with APNs. The iOS app then wakes up and fetches the actual message directly from your server. Your message content never leaves your VPS.

    Enable this by adding to /etc/ntfy/server.yml:

    yaml
    # iOS push via upstream APNs relay
    upstream-base-url: "https://ntfy.sh"

    Restart:

    bash
    sudo systemctl restart ntfy

    On your iPhone:

  • Install the ntfy app from the App Store
  • Tap the + button to add a subscription
  • Toggle Use another server and enter https://ntfy.yourdomain.com
  • Enter the topic name (e.g., backups)
  • If authentication is required, enter your phone user credentials
  • Send a test:

    bash
    curl -u alerter:yourpassword -d "iOS test" https://ntfy.yourdomain.com/backups

    Within 1-3 seconds, the notification should appear on your iPhone's lock screen. If it does not, see Troubleshooting.

    Step 9: TLS with Caddy

    Caddy is the easiest way to get TLS: it pulls Let's Encrypt certificates automatically, renews them, and handles HTTPS redirection with zero config.

    Install Caddy:

    bash
    sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
    curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/gpg.key" | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
    curl -1sLf "https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt" | sudo tee /etc/apt/sources.list.d/caddy-stable.list
    sudo apt update
    sudo apt install -y caddy

    Replace the default Caddyfile:

    bash
    sudo tee /etc/caddy/Caddyfile > /dev/null <<'EOF'
    ntfy.yourdomain.com {
        reverse_proxy 127.0.0.1:2586 {
            flush_interval -1
            transport http {
                read_timeout 24h
            }
        }

    encode gzip } EOF

    Key settings:

    • flush_interval -1 -- Disables buffering so the server-sent-event stream reaches subscribers in real time.
    • read_timeout 24h -- Long-polling subscribers hold the connection open for hours.
    • encode gzip -- Compresses JSON responses for the web UI.
    Reload Caddy:

    bash
    sudo systemctl reload caddy

    Caddy will request a certificate for ntfy.yourdomain.com on first request. Verify:

    bash
    curl https://ntfy.yourdomain.com/v1/health

    Expected output:

    json
    {"healthy":true}

    Open https://ntfy.yourdomain.com in a browser -- the web UI loads over HTTPS.

    Integrations

    Plain curl from Shell Scripts

    The smallest possible integration. Drop this in any bash script:

    bash
    notify() {
      curl -s \
        -H "Authorization: Bearer $NTFY_TOKEN" \
        -H "Title: $1" \
        -H "Priority: $2" \
        -H "Tags: $3" \
        -d "$4" \
        "https://ntfy.yourdomain.com/servers" > /dev/null
    }

    Example use

    notify "Backup complete" "default" "floppy_disk,white_check_mark" "Nightly rsync to B2 finished in 8m32s" notify "Disk alert" "urgent" "warning,rotating_light" "Root filesystem 92% full on vps-prod-01"

    Tags map to emoji via this reference. Priority accepts min, low, default, high, urgent.

    Home Assistant

    Home Assistant has first-class ntfy support via the REST notify integration. Add to configuration.yaml:

    yaml
    notify:
      - name: ntfy_home
        platform: rest
        resource: https://ntfy.yourdomain.com/home
        method: POST_JSON
        authentication: basic
        username: alerter
        password: !secret ntfy_password
        title_param_name: title
        message_param_name: message
        data:
          priority: "default"
          tags: ["house_with_garden"]

    Then in an automation:

    yaml
    automation:
      - alias: "Front door opened"
        trigger:
          platform: state
          entity_id: binary_sensor.front_door
          to: "on"
        action:
          service: notify.ntfy_home
          data:
            title: "Front door"
            message: "Door opened at {{ now().strftime('%H:%M') }}"

    For a deeper tutorial on the HA side of this integration, see our Home Assistant install guide.

    Prometheus Alertmanager

    Alertmanager's generic webhook receiver is a perfect fit. Add a receiver to alertmanager.yml:

    yaml
    receivers:
      - name: ntfy
        webhook_configs:
          - url: "https://ntfy.yourdomain.com/alerts"
            send_resolved: true
            http_config:
              authorization:
                type: Bearer
                credentials: "tk_abcdefghijklmnopqrstuvwxyz0123"

    Alertmanager will POST JSON that ntfy interprets as a message. For richer formatting, use a webhook template that maps alert labels into Title, Priority, and Tags headers. A full example lives in our Alertmanager guide.

    Uptime Kuma

    Uptime Kuma has a native ntfy notification type. In Uptime Kuma settings:

  • Notifications -> Setup Notification
  • Notification Type: ntfy
  • ntfy Server URL: https://ntfy.yourdomain.com
  • Topic: uptime
  • Priority: 4 (high)
  • Username/Password: your ntfy credentials
  • Test the notification and monitor downtime alerts arrive on your phone within seconds of a monitor going red.

    GitHub / GitLab Webhooks via Middleware

    Ntfy doesn't natively parse provider-specific webhook formats, but a tiny proxy in Caddy handles it. Add to the Caddyfile:

    text
    ntfy.yourdomain.com {
        route /webhook/github {
            request_header X-Ntfy-Title "GitHub: {http.request.header.X-Github-Event}"
            request_header X-Ntfy-Tags "octopus"
            rewrite * /github-events
            reverse_proxy 127.0.0.1:2586
        }

    reverse_proxy 127.0.0.1:2586 { flush_interval -1 } }

    Point your GitHub webhook at https://ntfy.yourdomain.com/webhook/github and every push, PR, and release becomes a push notification.

    Troubleshooting

    ProblemCauseSolution
    curl: (7) Failed to connect to localhost port 2586Ntfy not runningsudo systemctl status ntfy; check journalctl -u ntfy -n 50
    403 Forbidden on publishACL or deny-all blocking unauthenticatedCheck user access: sudo -u ntfy ntfy access <user>; supply -u user:pass or bearer token
    iOS app not receiving messagesupstream-base-url missing or wrong base-urlEnsure base-url is your public HTTPS URL and upstream-base-url: https://ntfy.sh is set; restart ntfy
    Caddy TLS failsDNS not pointing to VPS or port 80 blockedVerify A record: dig ntfy.yourdomain.com; open 80/443: sudo ufw allow 80,443/tcp
    attachment too large errorFile exceeds attachment-file-size-limitIncrease the limit in server.yml and restart, or compress the file
    High memory after many subscribersEach SSE connection holds stateExpected; 1,000 concurrent subscribers use ~200 MB. Upgrade plan or reduce keepalive
    Messages lost on restartcache-file not configured or pointing to ephemeral locationEnsure cache-file points to a persistent path like /var/cache/ntfy/cache.db
    Too Many Requests (429)Per-visitor rate limit hitIncrease visitor-request-limit-burst or switch the caller to a user token (tokens have separate tier-based limits)
    Web UI shows Connection lost repeatedlyReverse proxy buffering SSEConfirm flush_interval -1 in Caddy and behind-proxy: true in server.yml

    Viewing Logs

    Stream ntfy logs in real time:

    bash
    sudo tail -f /var/log/ntfy/ntfy.log

    Or via journald:

    bash
    sudo journalctl -u ntfy -f

    Increase verbosity temporarily for debugging:

    bash
    sudo sed -i 's/log-level: "INFO"/log-level: "DEBUG"/' /etc/ntfy/server.yml
    sudo systemctl restart ntfy

    Don't forget to switch back to INFO once debugging is done -- DEBUG logs every single HTTP request.

    FAQ

    Is ntfy really free to self-host?

    Yes. Ntfy is open source under the Apache 2.0 license. The server binary is free and has no feature gates. The only cost is your VPS. The hosted ntfy.sh service has optional paid tiers for increased limits, but a self-hosted instance on a 4 GB VPS can easily handle thousands of subscribers without any license fees. Every feature in the official documentation works on your self-hosted install except for phone-call delivery, which is exclusive to the hosted cloud service because it integrates with Twilio.

    Does ntfy work with iOS push notifications?

    Yes, but with one caveat. The iOS app requires a round trip through Apple's APNs infrastructure, which the official ntfy.sh instance handles for you. When you self-host, you configure upstream-base-url: https://ntfy.sh in server.yml to forward iOS devices through ntfy.sh for APNs delivery. Your messages stay private because only a short wake-up ping is sent upstream; the actual payload is fetched from your server by the iOS app when it wakes. If you need zero dependency on ntfy.sh for iOS, you would need an Apple developer account, a p8 key, and a custom fork of the iOS app -- not worth it for most deployments.

    How is ntfy different from Gotify or Pushover?

    Ntfy uses a pub-sub topic model where any HTTP client can publish to a topic with a single curl call and no SDK. Gotify requires an application token per publisher and has a slightly heavier UI model built around "applications" and "messages." Pushover is a commercial SaaS with per-device licensing and isn't self-hostable. Ntfy is the best fit for infrastructure alerts and scripted notifications because the publish API is trivial, and it supports attachments, actions, and priorities natively. See our Gotify install guide for a direct comparison if you're choosing between the two.

    Can I use ntfy with Prometheus Alertmanager?

    Yes. Alertmanager has a generic webhook receiver, and ntfy's publish endpoint accepts JSON. You point Alertmanager at https://ntfy.yourdomain.com/alerts, add a bearer token header for auth, and Alertmanager will POST firing and resolved alerts directly to your topic. Templating in Alertmanager lets you shape the title, priority, and tags per alert label. See the Integrations section above for a config example, or our dedicated Alertmanager install guide for the Prometheus side.

    How much RAM does ntfy need?

    Ntfy is remarkably lightweight. A base install uses under 30 MB of RAM and handles hundreds of concurrent subscribers on a 1 vCPU server. Memory scales with the number of open SSE connections -- each subscriber adds about 100-200 KB. Even at 10,000 concurrent subscribers you are looking at under 2 GB. For most homelabs and small teams, our Starter plan (2 vCPU, 4 GB RAM) provides enormous headroom and leaves room on the box for Caddy, monitoring, and occasional attachment traffic.

    How do I rotate or revoke tokens?

    Run sudo -u ntfy ntfy token list to see all active tokens. Revoke one with sudo -u ntfy ntfy token del <token-id>. Issue a new one with sudo -u ntfy ntfy token add <user> and update the caller. Unlike passwords, tokens can be rotated without disrupting the user's other clients.

    Next Steps

    Now that ntfy is running securely on your VPS, here's how to build on it:

    • Add status monitoring with Uptime Kuma -- Get instant push notifications the moment any site or service goes down, without any SaaS in the loop.
    • Wire up Prometheus Alertmanager -- Forward every firing and resolved alert to ntfy with rich title, priority, and tag templating.
    • Integrate with Home Assistant -- Route smart home events (doors, motion, temperature) to your phone through a single protected topic.
    • Compare with Gotify -- If you want a second opinion on the self-hosted notification space, spin up Gotify alongside and A/B test for a week.
    • Read the official docs -- The full reference at docs.ntfy.sh covers scheduled delivery, email forwarding, actions, Firebase integration, and tier-based rate limits in depth.

    Get ntfy running in minutes on the right-sized VPS
    >
    The Starter VPS plan is ideal for a self-hosted ntfy deployment: enough RAM for thousands of subscribers, enough disk for a generous attachment cache, and plenty of spare capacity for Caddy and monitoring.
    >
    - 2 vCPU cores
    - 4 GB RAM
    - 50 GB NVMe SSD
    - Unmetered bandwidth
    - EUR 7.99/month
    >
    Get Your Starter VPS -- deploy in under 60 seconds and start pushing notifications from any curl command you write.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket