Installing ntfy¶
The ntfy CLI allows you to publish messages, subscribe to topics as well as to self-host your own ntfy server. It's all pretty straight forward. Just install the binary, package or Docker image, configure it and run it. Just like any other software. No fuzz.
Info
The following steps are only required if you want to self-host your own ntfy server or you want to use the ntfy CLI. If you just want to send messages using ntfy.sh, you don't need to install anything. You can just use curl.
General steps¶
The ntfy server comes as a statically linked binary and is shipped as tarball, deb/rpm packages and as a Docker image. We support amd64, armv7 and arm64.
- Install ntfy using one of the methods described below
- Then (optionally) edit
/etc/ntfy/server.ymlfor the server (Linux only, see configuration or sample server.yml) - Or (optionally) create/edit
~/.config/ntfy/client.yml(for the non-root user),~/Library/Application Support/ntfy/client.yml(for the macOS non-root user), or/etc/ntfy/client.yml(for the root user), see sample client.yml)
To run the ntfy server, then just run ntfy serve (or systemctl start ntfy when using the deb/rpm). To send messages, use ntfy publish. To subscribe to topics, use ntfy subscribe (see subscribing via CLI for details).
If you like tutorials, check out Kris Occhipinti's ntfy install guide on YouTube, or Alex's Docker-based setup guide. Both are great resources to get started. I am not affiliated with Kris or Alex, I just liked their video/post.
Linux binaries¶
Please check out the releases page for binaries and deb/rpm packages.
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_amd64.tar.gz
tar zxvf ntfy_2.16.0_linux_amd64.tar.gz
sudo cp -a ntfy_2.16.0_linux_amd64/ntfy /usr/local/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.16.0_linux_amd64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv6.tar.gz
tar zxvf ntfy_2.16.0_linux_armv6.tar.gz
sudo cp -a ntfy_2.16.0_linux_armv6/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.16.0_linux_armv6/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv7.tar.gz
tar zxvf ntfy_2.16.0_linux_armv7.tar.gz
sudo cp -a ntfy_2.16.0_linux_armv7/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.16.0_linux_armv7/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_arm64.tar.gz
tar zxvf ntfy_2.16.0_linux_arm64.tar.gz
sudo cp -a ntfy_2.16.0_linux_arm64/ntfy /usr/bin/ntfy
sudo mkdir /etc/ntfy && sudo cp ntfy_2.16.0_linux_arm64/{client,server}/*.yml /etc/ntfy
sudo ntfy serve
Debian/Ubuntu repository¶
Info
As of September 2025, the official ntfy.sh Debian/Ubuntu repository has moved to archive.ntfy.sh. The old repository archive.heckel.io is still available for now, but will likely go away soon. I suspect I will phase it out some time in early 2026.
Installation via Debian/Ubuntu repository (fingerprint 55BA 774A 6F5E E674 31E4 6B7C CFDB 962D 4F1E C4AF):
sudo mkdir -p /etc/apt/keyrings
sudo curl -L -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg
sudo apt install apt-transport-https
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/ntfy.gpg] https://archive.ntfy.sh/apt stable main" \
| sudo tee /etc/apt/sources.list.d/ntfy.list
sudo apt update
sudo apt install ntfy
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo mkdir -p /etc/apt/keyrings
sudo curl -L -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg
sudo apt install apt-transport-https
echo "deb [arch=armhf signed-by=/etc/apt/keyrings/ntfy.gpg] https://archive.ntfy.sh/apt stable main" \
| sudo tee /etc/apt/sources.list.d/ntfy.list
sudo apt update
sudo apt install ntfy
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo mkdir -p /etc/apt/keyrings
sudo curl -L -o /etc/apt/keyrings/ntfy.gpg https://archive.ntfy.sh/apt/keyring.gpg
sudo apt install apt-transport-https
echo "deb [arch=arm64 signed-by=/etc/apt/keyrings/ntfy.gpg] https://archive.ntfy.sh/apt stable main" \
| sudo tee /etc/apt/sources.list.d/ntfy.list
sudo apt update
sudo apt install ntfy
sudo systemctl enable ntfy
sudo systemctl start ntfy
Manually installing the .deb file:
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_amd64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv6.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv7.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
wget https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_arm64.deb
sudo dpkg -i ntfy_*.deb
sudo systemctl enable ntfy
sudo systemctl start ntfy
Fedora/RHEL/CentOS¶
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_amd64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv6.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_armv7.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_linux_arm64.rpm
sudo systemctl enable ntfy
sudo systemctl start ntfy
Arch Linux¶
Community maintained
ntfy can be installed using an AUR package. You can use an AUR helper like paru, yay or others to download, build and install ntfy and keep it up to date.
paru -S ntfysh-bin
Alternatively, run the following commands to install ntfy manually:
curl https://aur.archlinux.org/cgit/aur.git/snapshot/ntfysh-bin.tar.gz | tar xzv
cd ntfysh-bin
makepkg -si
NixOS / Nix¶
Community maintained
ntfy is packaged in nixpkgs as ntfy-sh. It can be installed by adding the package name to the configuration file and calling nixos-rebuild. Alternatively, the following command can be used to install ntfy in the current user environment:
nix-env -iA ntfy-sh
NixOS also supports declarative setup of the ntfy server.
FreeBSD¶
Community maintained
ntfy is ported to FreeBSD and available via the ports collection as sysutils/go-ntfy. You can install it via pkg:
pkg install go-ntfy
macOS¶
The ntfy CLI (ntfy publish and ntfy subscribe only) is supported on macOS as well. To install, please download the tarball, extract it and place it somewhere in your PATH (e.g. /usr/local/bin/ntfy).
If run as root, ntfy will look for its config at /etc/ntfy/client.yml. For all other users, it'll look for it at ~/Library/Application Support/ntfy/client.yml (sample included in the tarball).
curl -L https://github.com/binwiederhier/ntfy/releases/download/v2.16.0/ntfy_2.16.0_darwin_all.tar.gz > ntfy_2.16.0_darwin_all.tar.gz
tar zxvf ntfy_2.16.0_darwin_all.tar.gz
sudo cp -a ntfy_2.16.0_darwin_all/ntfy /usr/local/bin/ntfy
mkdir ~/Library/Application\ Support/ntfy
cp ntfy_2.16.0_darwin_all/client/client.yml ~/Library/Application\ Support/ntfy/client.yml
ntfy --help
Info
Only the ntfy CLI is supported on macOS. ntfy server is currently not supported, but you can build and run it for development as well. Check out the build instructions for details.
Homebrew¶
Community maintained
To install the ntfy CLI (ntfy publish and ntfy subscribe only) via Homebrew (Linux and macOS), simply run:
brew install ntfy
Windows¶
The ntfy server and CLI are fully supported on Windows. You can run the ntfy server directly or as a Windows service. To install, you can either
- Download the latest ZIP, extract it and place the
ntfy.exebinary somewhere in your%Path%. - Or install ntfy from the Scoop main repository via
scoop install ntfy
Once installed, you can run the ntfy CLI commands like so:
ntfy.exe -h
The default configuration file location on Windows is %ProgramData%\ntfy\server.yml (e.g., C:\ProgramData\ntfy\server.yml) for the server, and %AppData%\ntfy\client.yml for the client. You may need to create the directory and config file manually.
To install the ntfy server as a Windows service, you can use the built-in sc command. For example, run this in an elevated command prompt (adjust the path to ntfy.exe accordingly):
sc create ntfy binPath="C:\path\to\ntfy.exe serve" start=auto
sc start ntfy
Docker¶
The ntfy image is available for amd64, armv6, armv7 and arm64. It should be pretty straight forward to use.
The server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent message cache, you also need to map a volume to /var/cache/ntfy. To change other settings, you should map /etc/ntfy, so you can edit /etc/ntfy/server.yml.
Info
Note that the Docker image does not contain a /etc/ntfy/server.yml file. If you'd like to use a config file, please manually create one outside the image and map it as a volume, e.g. via -v /etc/ntfy:/etc/ntfy. You may use the server.yml file on GitHub as a template.
Basic usage (no cache or additional config):
docker run -p 80:80 -it binwiederhier/ntfy serve
With persistent cache (configured as command line arguments):
docker run \
-v /var/cache/ntfy:/var/cache/ntfy \
-p 80:80 \
-it \
binwiederhier/ntfy \
serve \
--cache-file /var/cache/ntfy/cache.db
With other config options, timezone, and non-root user (configured via /etc/ntfy/server.yml, see configuration for details):
docker run \
-v /etc/ntfy:/etc/ntfy \
-e TZ=UTC \
-p 80:80 \
-u UID:GID \
-it \
binwiederhier/ntfy \
serve
Using docker-compose with non-root user and healthchecks enabled:
services:
ntfy:
image: binwiederhier/ntfy
container_name: ntfy
command:
- serve
environment:
- TZ=UTC # optional: set desired timezone
user: UID:GID # optional: replace with your own user/group or uid/gid
volumes:
- /var/cache/ntfy:/var/cache/ntfy
- /etc/ntfy:/etc/ntfy
ports:
- 80:80
healthcheck: # optional: remember to adapt the host:port to your environment
test: ["CMD-SHELL", "wget -q --tries=1 http://localhost:80/v1/health -O - | grep -Eo '\"healthy\"\\s*:\\s*true' || exit 1"]
interval: 60s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
init: true # needed, if healthcheck is used. Prevents zombie processes
If using a non-root user when running the docker version, be sure to chown the server.yml, user.db, and cache.db files and attachments directory to the same uid/gid.
Alternatively, you may wish to build a customized Docker image that can be run with fewer command-line arguments and without delivering the configuration file separately.
FROM binwiederhier/ntfy
COPY server.yml /etc/ntfy/server.yml
ENTRYPOINT ["ntfy", "serve"]
Kubernetes¶
The setup for Kubernetes is very similar to that for Docker, and requires a fairly minimal deployment or pod definition to function. There are a few options to mix and match, including a deployment without a cache file, a stateful set with a persistent cache, and a standalone unmanned pod.
apiVersion: apps/v1
kind: Deployment
metadata:
name: ntfy
spec:
selector:
matchLabels:
app: ntfy
template:
metadata:
labels:
app: ntfy
spec:
containers:
- name: ntfy
image: binwiederhier/ntfy
args: ["serve"]
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: "/etc/ntfy"
readOnly: true
volumes:
- name: config
configMap:
name: ntfy
---
# Basic service for port 80
apiVersion: v1
kind: Service
metadata:
name: ntfy
spec:
selector:
app: ntfy
ports:
- port: 80
targetPort: 80
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: ntfy
spec:
selector:
matchLabels:
app: ntfy
serviceName: ntfy
template:
metadata:
labels:
app: ntfy
spec:
containers:
- name: ntfy
image: binwiederhier/ntfy
args: ["serve", "--cache-file", "/var/cache/ntfy/cache.db"]
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: "/etc/ntfy"
readOnly: true
- name: cache
mountPath: "/var/cache/ntfy"
volumes:
- name: config
configMap:
name: ntfy
volumeClaimTemplates:
- metadata:
name: cache
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
apiVersion: v1
kind: Pod
metadata:
labels:
app: ntfy
spec:
containers:
- name: ntfy
image: binwiederhier/ntfy
args: ["serve"]
resources:
limits:
memory: "128Mi"
cpu: "500m"
ports:
- containerPort: 80
name: http
volumeMounts:
- name: config
mountPath: "/etc/ntfy"
readOnly: true
volumes:
- name: config
configMap:
name: ntfy
Configuration is relatively straightforward. As an example, a minimal configuration is provided.
apiVersion: v1
kind: ConfigMap
metadata:
name: ntfy
data:
server.yml: |
# Template: https://github.com/binwiederhier/ntfy/blob/main/server/server.yml
base-url: https://ntfy.sh
kubectl create configmap ntfy --from-file=server.yml
Kustomize¶
ntfy can be deployed in a Kubernetes cluster with Kustomize, a tool used to customize Kubernetes objects using a kustomization.yaml file.
- Create new folder -
ntfy - Add all files listed below
kustomization.yaml- stores all configmaps and resources used in a deploymentntfy-deployment.yaml- define deployment type and its parametersntfy-pvc.yaml- describes how persistent volumes will be createdntfy-svc.yaml- expose application to the internal kubernetes networkntfy-ingress.yaml- expose service to outside the network using ingress controllerserver.yaml- simple server configuration
- Replace TESTNAMESPACE within
kustomization.yamlwith designated namespace - Replace ntfy.test within
ntfy-ingress.yamlwith desired DNS name - Apply configuration to cluster set in current context:
kubectl apply -k /ntfy
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ntfy-deployment.yaml # deployment definition
- ntfy-svc.yaml # service connecting pods to cluster network
- ntfy-pvc.yaml # pvc used to store cache and attachment
- ntfy-ingress.yaml # ingress definition
configMapGenerator: # will parse config from raw config to configmap,it allows for dynamic reload of application if additional app is deployed ie https://github.com/stakater/Reloader
- name: server-config
files:
- server.yml
namespace: TESTNAMESPACE # select namespace for whole application
apiVersion: apps/v1
kind: Deployment
metadata:
name: ntfy-deployment
labels:
app: ntfy-deployment
spec:
revisionHistoryLimit: 1
replicas: 1
selector:
matchLabels:
app: ntfy-pod
template:
metadata:
labels:
app: ntfy-pod
spec:
containers:
- name: ntfy
image: binwiederhier/ntfy:v1.28.0 # set deployed version
args: ["serve"]
env: #example of adjustments made in environmental variables
- name: TZ # set timezone
value: XXXXXXX
- name: NTFY_DEBUG # enable/disable debug
value: "false"
- name: NTFY_LOG_LEVEL # adjust log level
value: INFO
- name: NTFY_BASE_URL # add base url
value: XXXXXXXXXX
ports:
- containerPort: 80
name: http-ntfy
resources:
limits:
memory: 300Mi
cpu: 200m
requests:
cpu: 150m
memory: 150Mi
volumeMounts:
- mountPath: /etc/ntfy
subPath: server.yml
name: config-volume # generated vie configMapGenerator from kustomization file
- mountPath: /var/cache/ntfy
name: cache-volume #cache volume mounted to persistent volume
volumes:
- name: config-volume
configMap: # uses configmap generator to parse server.yml to configmap
name: server-config
- name: cache-volume
persistentVolumeClaim: # stores /cache/ntfy in defined pv
claimName: ntfy-pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ntfy-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-path # adjust storage if needed
resources:
requests:
storage: 1Gi
apiVersion: v1
kind: Service
metadata:
name: ntfy-svc
spec:
type: ClusterIP
selector:
app: ntfy-pod
ports:
- name: http-ntfy-out
protocol: TCP
port: 80
targetPort: http-ntfy
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ntfy-ingress
spec:
rules:
- host: ntfy.test #select own
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ntfy-svc
port:
number: 80
cache-file: "/var/cache/ntfy/cache.db"
attachment-cache-dir: "/var/cache/ntfy/attachments"