Get a phone notification from your computer, scripts or server

Subscribe to a topic on your phone, then POST a message to its URL. With ntfy, one HTTP request can notify you when a command finishes, a backup fails or a sensor changes.

Send your first notification

  1. On iPhone or iPad, install NTFY me - Next Gen, add a topic on https://ntfy-me.com, and allow notifications. On Android, use the official ntfy app with the same server. On a computer, use the web app.
  2. Pick a long, random topic name in the app. Anyone who knows the name can read and publish to it. Replace YOUR_RANDOM_TOPIC in every example below with that name; do not use the placeholder as your real topic.
  3. On your computer, send:
curl --fail --show-error --silent --max-time 15 \
      -H 'Content-Type: text/plain; charset=utf-8' \
      -d 'Hello from my computer' \
      https://ntfy-me.com/YOUR_RANDOM_TOPIC

The response is JSON with the accepted message. Your phone must subscribe to the same server and topic. A topic with the same name on a different server is a different topic. The examples send plain text over TLS; for sensitive content, use the app's encrypted-topic sender.

When a long command finishes: bash or zsh

Replace sleep 10 with your command. This one-liner sends an alert for either success or failure, then returns the command's original exit status. The subshell keeps exit from closing your terminal:

( sleep 10; result=$?; curl --fail --show-error --silent --max-time 15 -H 'Title: Command finished' -d "Exit status: $result" https://ntfy-me.com/YOUR_RANDOM_TOPIC; exit "$result" )

For repeated use, add this function to ~/.bashrc or ~/.zshrc, then reload that file. It works with programs and scripts; for a pipeline, pass bash -o pipefail -c 'your pipeline' as the command.

notify_when_done() (
      if "$@"; then result=0; else result=$?; fi
      curl --fail --show-error --silent --max-time 15 \
        -H 'Title: Command finished' \
        -d "Exit status: $result" \
        https://ntfy-me.com/YOUR_RANDOM_TOPIC || :
      return "$result"
    )

    notify_when_done sleep 10

macOS and Linux terminals

Use curl in Terminal on macOS or a Linux shell. Add ntfy headers for a title, priority and emoji tag:

curl --fail --show-error --silent --max-time 15 \
      -H 'Title: Server check' \
      -H 'Priority: high' \
      -H 'Tags: white_check_mark' \
      -d 'The server check finished' \
      https://ntfy-me.com/YOUR_RANDOM_TOPIC

Windows PowerShell

Use Invoke-RestMethod in Windows PowerShell 5.1 or PowerShell 7. Encode the body as UTF-8 so non-ASCII text is preserved:

$message = [System.Text.Encoding]::UTF8.GetBytes('Hello from Windows')
    Invoke-RestMethod -Method Post `
      -Uri 'https://ntfy-me.com/YOUR_RANDOM_TOPIC' `
      -ContentType 'text/plain; charset=utf-8' `
      -Headers @{ Title = 'Windows notification' } `
      -Body $message -TimeoutSec 15

Python, without extra packages

Python 3's standard library can send the same request. The explicit user agent identifies your script in server logs. HTTP and connection errors raise an exception:

import json
    from urllib.request import Request, urlopen

    request = Request(
        'https://ntfy-me.com/YOUR_RANDOM_TOPIC',
        data='Hello from Python'.encode('utf-8'),
        headers={'Title': 'Python notification',
                 'User-Agent': 'ntfy-python/1.0',
                 'Content-Type': 'text/plain; charset=utf-8'},
        method='POST',
    )
    with urlopen(request, timeout=15) as response:
        print(json.load(response)['id'])

cron: a daily reminder

Run crontab -e and add this line to send at 09:00 in the machine's local time. Check command -v curl first and replace /usr/bin/curl if your machine uses a different path. cron runs while the machine is awake and running:

0 9 * * * /usr/bin/curl --fail --show-error --silent --max-time 15 -d 'Daily server reminder' https://ntfy-me.com/YOUR_RANDOM_TOPIC >/dev/null

GitHub Actions: report a job result

Store your full https://ntfy-me.com/<random-topic> URL in an Actions secret named NTFY_TOPIC_URL. Add this step after the work in an Ubuntu job. always() lets it report failed earlier steps too. Secrets are not available to workflows from fork pull requests, so the condition skips those runs:

- name: Notify phone
      if: ${{ always() }}
      env:
        NTFY_TOPIC_URL: ${{ secrets.NTFY_TOPIC_URL }}
        JOB_STATUS: ${{ job.status }}
      run: |
        if [ -n "$NTFY_TOPIC_URL" ]; then
          curl --fail --show-error --silent --max-time 15 \
            -H 'Title: GitHub Actions' \
            -d "Job result: $JOB_STATUS" "$NTFY_TOPIC_URL"
        fi

A backup script: notify on success or failure

Save this as backup-and-notify.sh. Run sh backup-and-notify.sh /path/to/source /path/to/backup.tar.gz; choose an archive path outside the source folder. It reports the result of tar and preserves its exit status even if sending the notification fails:

#!/bin/sh
    if [ "$#" -ne 2 ]; then
      echo "Usage: $0 SOURCE_DIRECTORY ARCHIVE_PATH" >&2
      exit 2
    fi
    if tar -czf "$2" -C "$1" .; then
      result=0
      message='Backup finished'
    else
      result=$?
      message="Backup failed (exit $result)"
    fi
    curl --fail --show-error --silent --max-time 15 \
      -H 'Title: Backup' -d "$message" \
      https://ntfy-me.com/YOUR_RANDOM_TOPIC || :
    exit "$result"

An SSH login alert

For an interactive login on a Linux server running OpenSSH, put this in your user's ~/.ssh/rc. OpenSSH runs it before the user's shell; SSH_CONNECTION contains the client IP, client port, server IP and server port. This example deliberately sends no user name, host name or IP address to the public server and limits the wait to five seconds:

if [ -n "${SSH_CONNECTION:-}" ] && [ -n "${SSH_TTY:-}" ]; then
      curl --fail --show-error --silent --max-time 5 \
        -H 'Title: SSH login' \
        -d 'An interactive SSH session started' \
        https://ntfy-me.com/YOUR_RANDOM_TOPIC >/dev/null 2>&1 || :
    fi

This is a convenience alert, not an audit log: it covers this user's interactive sessions, and network failure can prevent delivery. If you use X11 forwarding, keep your existing xauth handling in this file; see OpenSSH's sshd manual.

Home Assistant: rest_command

Add this under rest_command in configuration.yaml, replacing the topic placeholder. Restart Home Assistant after editing. In Developer tools → Actions, call rest_command.ntfy_phone with message: "Hello from Home Assistant"; an automation can call the same action:

rest_command:
      ntfy_phone:
        url: "https://ntfy-me.com/YOUR_RANDOM_TOPIC"
        method: POST
        content_type: "text/plain; charset=utf-8"
        headers:
          Title: "Home Assistant"
        payload: "{{ message }}"
        timeout: 15

For more options, see Home Assistant's RESTful Command documentation and ntfy's publishing reference.

Uptime Kuma

Add a notification provider of type ntfy. Set Server URL to https://ntfy-me.com (the server root), Topic to your random topic, and authentication to None. Use the provider's test button, then assign that notification to the monitors that need it. The native provider sends JSON containing topic, title, message and priority to the server root; do not append the topic to Server URL. See the Uptime Kuma provider source.

Grafana Alerting

In a Grafana version that supports Webhook → Custom Payload, add a Webhook contact point with method POST and URL https://ntfy-me.com. Set its Custom Payload template to this, replacing the topic placeholder:

{{ coll.Dict "topic" "YOUR_RANDOM_TOPIC" "title" "Grafana alert" "message" (printf "Alert group is %s" .Status) | data.ToJSON }}

This publishes a firing/resolved status message; extend it with your alert details as needed. Use Grafana's test button and check both the JSON acceptance response and your phone. Grafana's ordinary webhook JSON is a different format: posting it unchanged to a topic can display the entire JSON document instead of a useful message. If your Grafana version has no Custom Payload option, use an adapter that emits ntfy's JSON schema rather than assuming the default webhook body will be formatted automatically. See Grafana's Webhook documentation.

What is NTFY me, and how does it relate to ntfy?

ntfy is the open-source notification service behind these HTTP requests. NTFY me - Next Gen is a free, independent iPhone and iPad app for ntfy, published by Legitimate LLC. It supports iOS 15 and later and works with any ntfy server. It is a fork of the MIT-licensed official ntfy iOS client, and is not affiliated with ntfy.sh or the ntfy project.

NTFY me is actively maintained: version 1.15.0 was released on October 8, 2026. It adds end-to-end encrypted topics (ntfy's draft JWE format with AES-256-GCM and PBKDF2), pinned topics, display names, guided setup with copy-ready commands and reliability fixes. The release notes list shipped changes. The official ntfy iOS app is another option; its version 1.7.0 was updated May 29, 2026.

ntfy.sh topics get no instant banners in NTFY me. Messages appear when you open or refresh the app, because ntfy.sh relays push only to the official app. Use ntfy-me.com for these examples, or configure your own ntfy server with upstream-base-url: "https://ntfy-me.com" for instant push in NTFY me. Switching that relay from ntfy.sh also stops instant delivery to the official iOS app on your server.

Cost, privacy and delivery

ntfy-me.com is a free public ntfy server and NTFY me's default server. No account is needed. It keeps messages for 12 hours and attachments for 3 hours, with attachments up to 15 MB. See the server limits and privacy policy.

Apple/Google push carries a poll request without message text; the app fetches the message over TLS. Plain messages are readable by the server and anyone who knows the topic. For end-to-end encryption, configure a topic password and use the sender shown in the app; adding a password does not encrypt the plain-text recipes above. Encryption protects content, not topic names or all metadata.

A successful HTTP response proves the server accepted the message. Phone delivery also needs network access, notification permission and a matching subscription. Check these if an alert is missing. On Android, use the official ntfy Android app; on desktop or in a browser, open ntfy-me.com's web app.