---
title: "Deploy NTFY"
description: "Pushover alternative. Self-hosted ntfy push notification server"
category: "Other"
url: https://railway.com/deploy/ntfy-server
---

# Deploy NTFY

Pushover alternative. Self-hosted ntfy push notification server

**[Deploy NTFY on Railway](https://railway.com/template/ntfy-server)**

- **Creator:** A3A
- **Category:** Other
- **Total deploys:** 1

## Template content

### Postgres https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### ntfy https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/ntfy.svg

- **Source:** https://github.com/gridalpha/ntfy-railway
- **Public domain:** Yes

## Buckets

- **ntfy-attachments**

## Documentation

![ntfy logo](https://res.cloudinary.com/rroe4rtk/image/upload/v1786979115/002a7650-b441-4a69-aae6-38305af2a0da.png)

# Deploy and Host ntfy on Railway

ntfy is an open source pub-sub notification server that turns a plain HTTP request into a push notification on your phone, desktop or browser. `POST` a line of text to a topic URL and every subscriber gets it — no SDK, no app registration, no vendor account. Sysadmins use it to hear that a backup finished; developers wire it into CI, cron and shell scripts with one `curl` line. Self-host ntfy and every alert stays on infrastructure you control.

Deploy ntfy on Railway and what a production instance needs arrives wired together: the ntfy server, built from [gridalpha/ntfy-railway](https://github.com/gridalpha/ntfy-railway) on the official `binwiederhier/ntfy` image; PostgreSQL for the message cache, accounts, access rules and Web Push subscriptions; and an object storage bucket for attachments. All state lives in those two, so the container is stateless — no volume to size or migrate. Access control starts closed, an admin is created from your password, and Web Push keys are generated on first boot.

![ntfy Railway architecture](https://res.cloudinary.com/rroe4rtk/image/upload/v1786979110/f07ec5df-c795-44f5-9d03-ff0728bd0367.png)

## Getting Started with ntfy on Railway

Set `NTFY_ADMIN_PASSWORD` before deploying — the one value with no useful default. When the deploy goes green, open the Railway URL. The server requires a login, so you land on `/login`; sign in as `admin` (or your `NTFY_ADMIN_USER`). Click **Subscribe to topic**, name it something unguessable such as `server-alerts`, confirm, then publish from anywhere:

```
curl -u admin:YOUR_PASSWORD \
  -H "Title: Nightly backup finished" \
  -H "Tags: white_check_mark" \
  -d "postgres dump 412 MB uploaded in 38s" \
  https://your-app.up.railway.app/server-alerts
```

The message appears in the open tab immediately — the web app holds a live stream, so nothing reloads. That round trip is the quickest proof the deployment works; `GET /v1/health` returns `{"healthy":true}` unauthenticated for a machine check. Then install the Android or iOS app, point it at your server and add the topic.

![ntfy topic feed showing deploy, disk and backup alerts](https://res.cloudinary.com/rroe4rtk/image/upload/v1786955296/ntfy-topic-alerts.png)
![ntfy publish dialog with title, tags and priority filled in](https://res.cloudinary.com/rroe4rtk/image/upload/v1786955300/ntfy-publish-dialog.png)
![ntfy notification previewing an attached latency chart image](https://res.cloudinary.com/rroe4rtk/image/upload/v1786955302/ntfy-attachment-preview.png)

## About Hosting ntfy

ntfy solves a small problem with no cheap commercial answer: getting a notification out of a script and onto a phone. Teams self-host once they hit a free tier's daily cap, or once compliance says alert bodies cannot leave their stack.

Key features:

- Publish with plain `curl` — a body plus optional `Title`, `Tags`, `Priority` and `Actions` headers
- Subscribe over WebSocket, SSE or JSON stream, or via the Android, iOS, web and CLI clients
- Templates render a Grafana, Alertmanager or GitHub webhook payload as readable text
- Attachments, scheduled delivery, action buttons, markdown, per-topic access control
- Web Push for browser notifications with the tab closed, and UnifiedPush so Android apps can skip Firebase

The **ntfy** service is the only public one and serves both publishing and subscribing. **Postgres** holds cached messages, users, tokens, ACLs and Web Push subscriptions over private networking. The **bucket** holds attachment bodies, which ntfy streams back through its `/file/` route, so it never needs to be public.

## Why Deploy ntfy on Railway

One deploy replaces a VPS, a proxy and a certificate.

- No volume to provision — Postgres and object storage hold everything
- Managed TLS, a public HTTPS URL and custom domains
- Private networking to the database
- Access control closed by default; admin from your password
- Web Push keys generated on first boot
- One-click redeploys from Git as new releases land

## Common Use Cases

- **Cron and backup alerts** — one appended `curl` tells you a nightly job ran, output included
- **CI and deploy notifications** — a GitHub Actions step pings a topic with the commit and run link
- **Monitoring sink** — point Grafana, Alertmanager or Uptime Kuma at a topic and template it
- **UnifiedPush backend** — self-host push for Android apps like FluffyChat

## Dependencies for ntfy

- **ntfy server** — [gridalpha/ntfy-railway](https://github.com/gridalpha/ntfy-railway), built on `binwiederhier/ntfy:latest` (upstream: [binwiederhier/ntfy](https://github.com/binwiederhier/ntfy))
- **PostgreSQL** — required. Holds the message cache, user database and Web Push subscriptions instead of three SQLite files, which is what makes the container stateless
- **Object storage bucket** — optional. Holds attachment bodies; without it attachments are disabled.

### Environment Variables Reference

| Variable | Description | Required |
|----------|-------------|----------|
| `NTFY_ADMIN_PASSWORD` | Password for the first admin | Yes |
| `NTFY_ADMIN_USER` | Admin username, default `admin` | No |
| `NTFY_DATABASE_URL` | PostgreSQL connection string | Yes |
| `NTFY_BASE_URL` | Public URL, used for attachments and Web Push | Yes |
| `NTFY_AUTH_DEFAULT_ACCESS` | `deny-all` / `read-only` / `read-write` | No |
| `NTFY_ENABLE_SIGNUP` | Let visitors create accounts | No |
| `NTFY_REQUIRE_LOGIN` | Force login before the web app loads | No |
| `NTFY_UPSTREAM_BASE_URL` | iOS push relay; blank disables iOS | No |

Any other setting in ntfy's [config reference](https://docs.ntfy.sh/config/) works as an `NTFY_*` variable — `NTFY_SMTP_SENDER_ADDR` for email, `NTFY_TWILIO_ACCOUNT` for phone calls.

### Deployment Dependencies

- Source repo: https://github.com/gridalpha/ntfy-railway
- Upstream: https://github.com/binwiederhier/ntfy
- Image: https://hub.docker.com/r/binwiederhier/ntfy
- Docs: https://docs.ntfy.sh
- Runtime: one static Go binary on Alpine; PostgreSQL 14+

## Hardware Requirements for Self-Hosting ntfy

ntfy is unusually light — one Go process, no runtime, no build step.

| Resource | Minimum | Recommended |
|---|---|---|
| CPU | 0.5 vCPU | 1 vCPU |
| RAM | 128 MB | 512 MB |
| Storage | none on the app; ~1 GB Postgres | 5 GB Postgres + bucket |
| Runtime | ntfy 2.27+, PostgreSQL 14+ | ntfy latest, PostgreSQL 18 |

Memory scales with *open subscriptions*, not message volume — each subscriber holds a connection. Attachments stream rather than buffer.

## How to Self-Host ntfy

Run the official image against your own Postgres — three variables are the whole configuration:

```
docker run -d --name ntfy -p 80:80 \
  -e NTFY_BASE_URL=https://ntfy.example.com \
  -e NTFY_DATABASE_URL=postgres://ntfy:secret@db:5432/ntfy \
  -e NTFY_AUTH_DEFAULT_ACCESS=deny-all \
  binwiederhier/ntfy:latest serve
```

Note the trailing `serve`: the entrypoint is `ntfy` with no default command, so without it the container prints help and exits. Then create an admin and generate Web Push keys:

```
docker exec -it ntfy ntfy user add --role=admin alice
docker exec -it ntfy ntfy access alice "*" rw
docker exec -it ntfy ntfy webpush keys
```

Set the printed keypair as `NTFY_WEB_PUSH_PUBLIC_KEY`/`NTFY_WEB_PUSH_PRIVATE_KEY`, plus `NTFY_WEB_PUSH_EMAIL_ADDRESS`. On Railway all three steps happen at boot.

## How Much Does ntfy Cost to Self-Host?

ntfy is free and open source under Apache-2.0 / GPL-2.0, with no paid edition — a self-hosted server does everything the hosted one does. On Railway you pay only for resources used, and ntfy idles under 100 MB of RAM, so Postgres is usually the larger line item. The hosted ntfy.sh is free for basic use and sells plans from about $6/month for higher message limits; on your own instance those limits are yours to set.

## ntfy vs Pushover vs Gotify

| | ntfy | Pushover | Gotify |
|---|---|---|---|
| Open source | Yes | No | Yes |
| Self-hostable | Yes | No | Yes |
| iOS app | Yes | Yes | No |
| Attachments | Disk or S3 | Images only | No |
| Web Push / UnifiedPush | Yes | No | No |

Pushover is the polished commercial option but cannot be self-hosted. Gotify is simpler, at the cost of iOS support, attachments and Web Push.

## FAQ

**What is ntfy?**
An open source notification server. `PUT` or `POST` a message to a topic URL and anyone subscribed — phone, browser or script — gets it instantly. Topics are created on first use.

**What does this Railway template deploy?**
The ntfy server on a public HTTPS URL, PostgreSQL for messages, users and Web Push subscriptions, and a bucket for attachments. Access control starts closed and an admin is created from your password.

**Why does the template include a PostgreSQL database?**
ntfy 2.27 can keep its message cache, user database and Web Push subscriptions in PostgreSQL instead of local SQLite files, so the container carries no state and there is no volume to manage.

**Can I receive notifications on my iPhone from a self-hosted ntfy server?**
Yes. Apple requires push via APNs, so your server relays a content-free poll request through ntfy.sh and the iOS app fetches the message from you. `NTFY_UPSTREAM_BASE_URL` is set for this; clear it to opt out, losing iOS delivery.

**How do I let other people publish without giving them the admin account?**
`ntfy user add bob`, then `ntfy access bob "alerts-*" rw`. Patterns work, so one grant covers a family of topics. For scripts, `ntfy token add bob`.

**Is this safe to run in production?**
Yes, with one caveat: attachment URLs under `/file/` are not covered by topic access control — the unguessable ID is the only protection. Keep sensitive payloads in the message body, which is ACL-protected.


## Similar templates

- [Rocky Linux](https://railway.com/deploy/rocky-linux) — Hosted Rocky Linux 9 workspace with SSH and persistent storage. 🚀
- [Foundry Virtual Tabletop](https://railway.com/deploy/X5tR6G) — A Self-Hosted & Modern Roleplaying Platform
- [Letta Code Remote](https://railway.com/deploy/letta-code-remote) — Run a Letta Code agent 24/7. No inbound ports, just deploy.

Open this page in a browser: https://railway.com/deploy/ntfy-server
