ntfy

Push notifications to your phone from Nuxt with ntfy: no account and no keys, priorities, tags and one notification that replaces itself instead of repeating.

ntfy is the shortest path from your server to a phone. Install the app, pick a topic name, and that is the whole setup. No account, no registration, no keys.

Copy this

Pick a topic and subscribe

Install ntfy on your phone, tap subscribe, and type a topic name. Anything, as long as it is hard to guess.

Configure it

.env
PIGEON_NTFY_TOPIC=my-deploys-8f2a1c
nuxt.config.ts
export default defineNuxtConfig({
  modules: ['nuxt-pigeon'],
  nuxtPigeon: {
    channels: {
      ntfy: true,
    },
  },
})

Send

server/api/hello.get.ts
export default defineEventHandler(async () => {
  await ntfy.send('Deploy failed on main', {
    title: 'nuxt-pigeon',
    priority: 5,
    tags: ['warning'],
  })

  return { ok: true }
})

Your phone rings, through do not disturb, with a warning triangle.

A public topic is a password. On ntfy.sh anyone who knows the name can read your notifications and publish to them. Use something nobody guesses, or run your own instance and put a token in PIGEON_NTFY_TOKEN.

One notification that updates itself

This is the best thing ntfy has, and it is one option. Publish again with the same sequence id and the notification on the phone is replaced rather than repeated:

for (const [index, step] of steps.entries()) {
  await ntfy.send(`Step ${index + 1} of ${steps.length}: ${step}`, {
    sequenceId: 'deploy-status',
    title: 'Deploy',
  })
}

One entry that counts up, instead of seven piling up in the shade. Leave sequenceId out and ntfy assigns one, which works just as well as a handle for edit and delete.

Every option

OptionWhat it does
titleThe bold line above the message
priority1 silent to 5, which rings through do not disturb
tagsEmoji and labels, ['warning', 'skull']
clickOpened when the notification is tapped
sequenceIdYour own id, so a later message replaces this one
mediaAn image. A url is fetched by ntfy itself
attachA url to attach without downloading it
iconA small icon next to the text
actionsButtons: open a url, send an HTTP request, broadcast
emailAlso send it as an email
callAlso call a phone number
delayDeliver later, 30min or tomorrow, 9am
markdownRender the body as Markdown
topicAnother topic than the configured one
cache, firebaseTurn off caching or push delivery

What will cost you an hour

  • It counts bytes, not characters. 4096 of them, and an umlaut costs two, an emoji four.
  • Editing needs server 2.16.0 or newer. An older self hosted instance does not know the field and publishes a second notification instead of replacing the first. No error, just a duplicate.
  • Titles travel as JSON, not as headers. ntfy also accepts X-Title, but HTTP headers are ASCII, so an umlaut in a title would have to be encoded. The module sends the body as JSON for that reason.
  • Bytes take a different route entirely. With a file the body is the file, so every option moves into headers. That happens for you, but it is why filename matters there.
  • There is no receiving. ntfy could be subscribed to over SSE, but that is a process that stays alive and nobody has asked for it yet.

Read more