Self-Host Gotify: Push Notifications Without Google or Apple
Get real-time push notifications from your self-hosted apps without Firebase or APNs. Set up Gotify in Docker and never miss an alert again.
I was tired of missing alerts from my services.
My Uptime Kuma would scream into the void when a server went down. N8N workflows would silently fail at 3 AM. Backup scripts would finish without telling me if they succeeded or not. I’d check my monitoring dashboard in the morning and discover that half my infrastructure had been on fire for six hours.
The obvious solution? Push notifications. But Firebase wants to own your soul, and APNs requires an Apple Developer account. Plus, I’m already self-hosting everything else. Why should notifications be different?
Enter Gotify.
What Is Gotify?
Gotify is a self-hosted notification server. You run it on your infrastructure, and it pushes messages to your phone. No Google. No Apple. No tracking. Just a simple HTTP API that you can curl from anywhere.
The entire thing is stupid simple. You send a POST request with your message, and your phone buzzes. That’s it.
I’ve been using it for two years now, and honestly, I don’t know how I lived without it.
Why Not Just Use Email?
Look, email notifications are fine if you enjoy drowning in noise. My inbox gets hundreds of messages per day. An alert from my server? Lost in a sea of newsletters I never signed up for.
Also, email is slow. Like, really slow. SMTP delivery can take minutes. When my database is melting down, I don’t have minutes.
Gotify delivers in under a second. Every time.
Installation (The Easy Way)
I’m assuming you have Docker already. If not, go read the Docker Compose beginners guide on this site first.
Create a directory for Gotify:
mkdir -p ~/gotify
cd ~/gotify
Here’s the docker-compose.yml I use:
version: "3"
services:
gotify:
image: gotify/server:latest
container_name: gotify
restart: unless-stopped
ports:
- "8080:80"
environment:
- GOTIFY_DEFAULTUSER_NAME=admin
- GOTIFY_DEFAULTUSER_PASS=changeme
volumes:
- ./data:/app/data
Spin it up:
docker compose up -d
Check the logs to make sure it started:
docker logs gotify
You should see something like “Listening on :80” in the output. If you see errors about permissions, that’s usually the ./data volume. Make sure your user owns it.
Now open http://your-server-ip:8080 in a browser. You should see the Gotify login screen.
Default credentials are admin / changeme. Change that password immediately. I mean it. Don’t be that person.
Setting Up Your First App
Once you’re logged in, you’ll see a clean interface. No bloat. Just three sections: Apps, Clients, and Messages.
Click “Apps” in the sidebar, then “Create Application.”
Give it a name. I called mine “Server Alerts” because I’m boring like that. Hit create.
Gotify will generate an app token. Copy it. You’ll need it for sending messages.
That token is basically your API key. Keep it secret. Anyone with that token can spam notifications to your phone.
Installing the Mobile App
The Android app is on F-Droid and Google Play. I use the F-Droid version because I like my apps without tracking.
For iOS users… there’s a third-party app called “Gotify-iOS” on the App Store. I haven’t tested it personally because I don’t own an iPhone, but people seem to like it.
Open the app, add a new connection:
- Server URL:
http://your-server-ip:8080 - Client token: Generate this in the Gotify web interface under “Clients”
Don’t use the app token here. That’s a common mistake. App tokens are for sending messages. Client tokens are for receiving them.
Hit connect. If everything works, you’ll see your “Server Alerts” app appear in the list.
Sending Your First Notification
Let’s test it. Open a terminal and run:
curl -X POST "http://your-server-ip:8080/message?token=YOUR_APP_TOKEN" \
-F "title=Test Notification" \
-F "message=If you can read this, Gotify works" \
-F "priority=5"
Replace YOUR_APP_TOKEN with the app token you copied earlier.
Your phone should buzz within a second.
If it doesn’t:
- Check that your app token is correct
- Make sure your phone is connected to the internet
- Verify that the Gotify container is running:
docker ps | grep gotify - Check the container logs:
docker logs gotify
Most issues are typos in the token or firewall rules blocking port 8080.
Priority Levels Matter
Gotify supports priority levels from 0 to 10. Higher priority = louder notification.
- 0-3: Silent notifications. No sound.
- 4-7: Normal notifications. Sound and vibration.
- 8-10: High priority. Breaks through Do Not Disturb.
I use priority 10 for critical alerts (server down, backup failed) and priority 5 for everything else.
Here’s a trick I learned the hard way: Don’t use priority 10 for everything. I did that for a week and wanted to throw my phone in a lake. Reserve it for actual emergencies.
Securing Gotify with HTTPS
Running Gotify over plain HTTP is fine for testing. For production? Absolutely not.
Your app tokens are sent in plain text with every request. Anyone sniffing your network can intercept them and send notifications as you.
I use Nginx Proxy Manager to add HTTPS. If you already have NPM set up (there’s a guide on this site), adding Gotify takes two minutes.
Create a new proxy host:
- Domain:
gotify.yourdomain.com - Scheme:
http - Forward Hostname:
gotify(container name) - Forward Port:
80 - Enable “Block Common Exploits”
- Enable SSL and request a Let’s Encrypt certificate
Save it. Update your Gotify URLs to use https://gotify.yourdomain.com instead of the IP address.
If you’re using Traefik instead of NPM, add these labels to your docker-compose:
labels:
- "traefik.enable=true"
- "traefik.http.routers.gotify.rule=Host(`gotify.yourdomain.com`)"
- "traefik.http.routers.gotify.entrypoints=websecure"
- "traefik.http.routers.gotify.tls.certresolver=letsencrypt"
- "traefik.http.services.gotify.loadbalancer.server.port=80"
Restart the container, and you’re good.
Integration with Uptime Kuma
This is where Gotify becomes insanely useful.
Uptime Kuma monitors your services. Gotify notifies you when something breaks. Together, they’re perfect.
In Uptime Kuma, go to Settings → Notifications → Add Notification.
Choose “Gotify” from the list. Enter:
- Server URL:
https://gotify.yourdomain.com - App Token: Your Gotify app token
- Priority: I use 8 for downtime alerts
Hit “Test” to make sure it works, then save.
Now attach that notification to your monitors. When a service goes down, you’ll get an instant push notification.
I sleep better knowing that if my infrastructure explodes at 4 AM, my phone will wake me up. (Whether that’s a good thing is debatable.)
Integration with N8N
N8N doesn’t have a native Gotify node, but the HTTP Request node works perfectly.
Create a new workflow and add an HTTP Request node:
- Method: POST
- URL:
https://gotify.yourdomain.com/message - Query Parameters:
token: Your app token
- Body Content Type: Form-Data
- Form Data:
title: Your notification titlemessage: Your notification messagepriority: 5 (or whatever you want)
Test it. If your phone buzzes, you’re done.
I use this for workflow completion notifications. When a long-running automation finishes, Gotify tells me. When a workflow fails, Gotify screams at me with priority 10.
It’s saved my ass more times than I can count.
Integration with Shell Scripts
Want to get notified when your backup script finishes? Add this to the end:
#!/bin/bash
# Your backup logic here
rsync -av /data /backup
# Send notification
if [ $? -eq 0 ]; then
curl -s -X POST "https://gotify.yourdomain.com/message?token=YOUR_TOKEN" \
-F "title=Backup Complete" \
-F "message=Backup finished successfully at $(date)" \
-F "priority=3"
else
curl -s -X POST "https://gotify.yourdomain.com/message?token=YOUR_TOKEN" \
-F "title=Backup Failed" \
-F "message=Backup failed at $(date). Check logs." \
-F "priority=10"
fi
Now you’ll know immediately if your backups are working or not.
I have this on every cron job I care about. It’s a game changer.
Integration with Grafana
Grafana has built-in support for Gotify alerts.
Go to Alerting → Contact Points → Add Contact Point.
Choose “Webhook” (Grafana doesn’t have a native Gotify integration, but webhooks work fine).
URL: https://gotify.yourdomain.com/message?token=YOUR_TOKEN
HTTP Method: POST
Body:
{
"title": "{{ .GroupLabels.alertname }}",
"message": "{{ .CommonAnnotations.summary }}",
"priority": 8
}
Save it and attach it to your alert rules.
Now when your CPU usage spikes or your disk fills up, Gotify will let you know.
Multiple Apps vs Multiple Tokens
Here’s something I wish I’d known earlier: You can create multiple apps in Gotify.
Each app has its own token and its own notification settings. This is useful for organizing notifications by category.
I have:
- “Server Alerts” (Uptime Kuma, system monitoring)
- “Backup Notifications” (backup scripts, cron jobs)
- “Automation” (N8N workflows)
- “CI/CD” (build failures from Drone)
Each app has a different icon and priority level in the mobile app. I can mute “Automation” notifications during the day without missing critical server alerts.
It’s way better than dumping everything into one stream.
Common Mistakes I Made (So You Don’t Have To)
Mistake 1: Exposing Gotify to the internet without auth
I did this for about three hours. Someone found it and spammed my phone with “HELLO” messages for 20 minutes straight. Don’t do this.
Always use HTTPS. Always use strong passwords. Consider adding firewall rules to only allow connections from your VPN.
Mistake 2: Using priority 10 for everything
As I mentioned earlier, this makes your phone insufferable. Priority 10 should break through Do Not Disturb. Use it sparingly.
Mistake 3: Hardcoding tokens in scripts
I had app tokens scattered across a dozen bash scripts. Then I rotated the token and had to update all of them.
Store your token in an environment variable or a config file:
GOTIFY_TOKEN="your_token_here"
Source it in your scripts:
source /etc/gotify.conf
curl -X POST "https://gotify.yourdomain.com/message?token=$GOTIFY_TOKEN" ...
Much easier to manage.
Mistake 4: Not testing failure scenarios
I assumed my notifications would work when I needed them. Then my database crashed, and I didn’t get a notification because the N8N workflow that sends notifications… was down.
Test your failure modes. Unplug your server and make sure you get an alert. Break something on purpose. Make sure your safety net actually works.
Alternatives I Tried
Before Gotify, I tried ntfy.sh. It’s similar but designed to be used as a service. You can self-host it, but the main appeal is using their public server.
I didn’t love the idea of sending my alerts to someone else’s server, even if they’re encrypted. Call me paranoid.
I also tried Pushover. It works great, but it’s $5 per platform. If you have Android and iOS devices, that’s $10. Gotify is free.
For $10, I’d rather throw that money at my VPS.
Performance and Resource Usage
Gotify is lightweight as hell.
On my VPS, it uses about 20 MB of RAM and negligible CPU. The container starts in under a second.
The SQLite database stores your message history. Mine is about 50 MB after two years of use. It doesn’t grow fast.
The Android app is also light. Battery usage is minimal because it uses WebSocket for real-time updates instead of polling.
I’ve had zero performance issues. It just works.
Backup and Disaster Recovery
Your Gotify data lives in ./data (or wherever you mounted the volume).
Back it up. Seriously.
I include it in my daily backup script:
tar -czf gotify-backup-$(date +%Y%m%d).tar.gz ~/gotify/data
If your server explodes, you can restore Gotify by:
- Reinstalling the Docker container
- Extracting your backup into the
./datadirectory - Restarting the container
Your apps, tokens, and message history will be intact.
That said, message history isn’t critical. If you lose it, you lose old notifications. Not the end of the world.
The app tokens are what matter. If you lose those, you’ll need to regenerate them and update all your integrations.
I keep a copy of my tokens in Vaultwarden (covered in another article on this site).
Message Formatting and Extras
Gotify supports Markdown in messages.
Send a notification like this:
curl -X POST "https://gotify.yourdomain.com/message?token=YOUR_TOKEN" \
-F "title=System Report" \
-F "message=**Disk Usage:** 75%\n\n**CPU:** 45%" \
-F "priority=5"
The mobile app will render it with bold text and line breaks.
You can also attach extras (arbitrary JSON data) to notifications. I don’t use this feature often, but it’s there if you need it.
WebSocket API for Real-Time Updates
Gotify uses WebSockets for push notifications. This means your phone maintains a persistent connection to the server.
If your server reboots, the connection drops. The mobile app will automatically reconnect within a few seconds.
I mention this because I’ve seen people panic when their notifications stop after a server restart. Wait 10 seconds. They’ll come back.
Message Retention
By default, Gotify keeps all messages forever.
You can configure message retention in the web interface. Go to Settings → Message Retention and set a limit.
I keep the last 500 messages per app. Anything older gets auto-deleted.
This keeps the database small and makes the mobile app faster.
Gotify CLI
There’s an official CLI tool for sending notifications from your terminal.
Install it:
wget https://github.com/gotify/cli/releases/latest/download/gotify-cli-linux-amd64.tar.gz
tar -xzf gotify-cli-linux-amd64.tar.gz
sudo mv gotify /usr/local/bin/
Configure it:
gotify init
It’ll ask for your server URL and token. After that, you can send notifications with:
gotify push "Your message here"
I don’t use this much because curl is already everywhere, but it’s nice for interactive use.
Plugin System
Gotify has a plugin system. You can extend it with custom functionality.
There’s a plugin for RSS feed notifications. Another for webhook receivers. A few for integrating with external services.
I haven’t needed plugins yet. The core functionality is enough for my use case.
If you’re interested, check the official plugin repository on GitHub.
Running Gotify on a Raspberry Pi
Gotify runs fine on a Raspberry Pi. I tested it on a Pi 4 with 2 GB of RAM.
Use the ARM image:
services:
gotify:
image: gotify/server-arm7:latest # or arm64 for Pi 4
...
Performance is identical to x86. No issues.
If you’re running a Pi as your home server, Gotify is a great addition.
FAQ
Can I use Gotify without exposing it to the internet?
Yes. Use a VPN like WireGuard (covered in another article here) to access your home network. The Gotify mobile app can connect through the VPN.
I do this. My Gotify instance is only accessible from my Tailscale network.
Can I send notifications with images?
Not natively. Gotify is text-only. If you need images, you’d have to host them somewhere and include a link in the message.
Honestly, I’ve never needed this.
How secure is Gotify?
It’s as secure as you make it. Use HTTPS. Use strong passwords. Use VPN or firewall rules to restrict access.
The app tokens are the weak point. Anyone with a token can send notifications. Treat them like passwords.
Can I self-host the mobile app?
The mobile app is just a client. You don’t host it. You install it from F-Droid or Google Play.
The server is what you self-host.
Does Gotify work offline?
No. It requires an internet connection between your phone and the server.
If your server is down or unreachable, notifications won’t go through.
This is why I monitor Gotify itself with an external service. If Gotify goes down, I get an email alert from Uptime Kuma’s external status page.
Can I use Gotify for group notifications?
Sort of. You can create a shared app and give the client token to multiple people.
Everyone with that client token will receive notifications sent to that app.
It’s not ideal for large groups, but it works for small teams.
What happens if my phone is offline?
The notification waits on the server. When your phone reconnects, it’ll sync and display missed notifications.
I’ve tested this. It works reliably.
Can I delete old notifications?
Yes. In the mobile app, swipe to delete. In the web interface, there’s a “Clear All” button.
You can also set message retention limits to auto-delete old notifications.
Is there a desktop app?
There’s no official desktop app, but the web interface works great.
I keep a browser tab pinned to https://gotify.yourdomain.com on my laptop. It’s good enough.
Some people have built third-party desktop clients. I haven’t tried them.
Can I use Gotify with Windows?
The server runs anywhere Docker runs. So yes, you can run Gotify on Windows with Docker Desktop.
There’s no native Windows mobile app, though. You’d need to use the web interface.
What if I want to send notifications to multiple phones?
Generate a client token for each phone. All clients connected to the same Gotify instance will receive the same notifications.
I have Gotify on my phone and my partner’s phone. We both get alerts when the server goes down.
Can I rate-limit notifications?
Not natively. If you’re worried about spam, you’d need to implement rate-limiting in your scripts or use a reverse proxy with rate-limiting rules.
I’ve never needed this.
Does Gotify support sound customization?
Yes. In the mobile app, you can set custom notification sounds per app.
I use different sounds for different priority levels. Critical alerts get a loud siren. Normal notifications get a gentle ping.
Can I integrate Gotify with Home Assistant?
Yes. Home Assistant has a built-in Gotify integration.
I don’t use Home Assistant, so I can’t provide detailed instructions, but people seem to love the combo.
Final Thoughts
Gotify is one of those tools that’s so simple, you wonder why it didn’t exist sooner.
No subscriptions. No tracking. No vendor lock-in. Just a clean HTTP API and a mobile app that works.
I’ve been using it daily for two years. It’s never let me down.
If you’re self-hosting anything that needs notifications, Gotify is a no-brainer. Set it up once, forget about it, and enjoy never missing an important alert again.
Now go install it. Your future self will thank you.
Stay in the loop 📬
Get self-hosting tutorials, tool reviews, and infrastructure tips delivered to your inbox. No spam, unsubscribe anytime.
Join 0 self-hosters. Free forever.