Skip to main contentSkip to navigation
[email protected]
Client AreaSupport
Hosting Mammoth
HostingMammothYour Data, Our Responsibility
Home
Solutions
Hosting Services
Store
Pricing
About
Blog
API
Contact

Stay Ahead of the Curve

Get the latest insights on cybersecurity, AI innovations, and enterprise data solutions delivered to your inbox.

Hosting Mammoth
HostingMammothEnterprise Solutions

Enterprise-grade data solutions. Hosting, recovery, cybersecurity, and AI-powered services for businesses worldwide.

[email protected]
Sun - Fri, 9:00am - 5:00pm

Services

  • Cloud Hosting
  • Data Recovery
  • Cybersecurity
  • Legal Support
  • MSP Services
  • Web Development
  • AI Services
  • Free Server Migration

Hosting

  • VPS Hosting (NVMe SSD)
  • VDS Hosting (NVMe)
  • Storage VPS (High SSD)
  • GPU Servers
  • Managed Services
  • Cloud Firewall
  • Load Balancer
  • One-Click Apps
  • n8n Hosting
  • Object Storage
  • FAQ

Company

  • Store
  • Pricing
  • About Us
  • Locations
  • Blog
  • Testimonials
  • Contact
  • Affiliate Program
  • White-Label
  • Terms of Service
  • Privacy Policy
  • Browser Cookies
  • SLA

Support

  • Client Area
  • Submit Ticket
  • Knowledge Base
  • Server Status
  • API Documentation

© 2026 Hosting Mammoth. All rights reserved.

Knowledge Base
Getting StartedAccount ManagementVPS HostingGPU ServersStorage VPSCloud FirewallLoad BalancerServer ManagementBilling & PaymentsSupport & TicketsAffiliate ProgramReseller ProgramMarketplace & Appsn8n HostingManaged ServicesServer MigrationAPI & DevelopersSecurityTroubleshootingGlossaryInstall Guides
  1. Home
  2. /
  3. Support
  4. /
  5. Install Guides
  6. /
  7. How To Install Gotify Ubuntu
GUIDEInstall Guides

How to Install Gotify on Ubuntu 24.04 — Self-Hosted Push Notification Server

27 min read

How to Install Gotify on Ubuntu 24.04 — Self-Hosted Push Notifications on Your VPS

If you run servers, you need notifications. Cron jobs fail, disks fill up, SSL certificates expire, backups break, and containers restart — and you want to know immediately without paying per-message fees to a SaaS vendor or granting a third party access to your alert stream. Gotify is a tiny, self-hosted push notification server written in Go that delivers messages over WebSocket to a web UI and a native Android app. This guide takes you from a fresh Ubuntu 24.04 VPS to a TLS-secured Gotify instance wired into your monitoring stack.

Prefer the managed route? Spin up a CloudCore Starter VPS in 60 seconds and follow along. At EUR 7.99/month, it is more than enough to run Gotify plus a reverse proxy.

Table of Contents

  • What is Gotify?
  • Why Self-Host a Push Notification Server?
  • Gotify vs. Ntfy vs. Pushover vs. Apprise
  • Prerequisites
  • Step 1: Update and Harden Ubuntu 24.04
  • Step 2a: Install Gotify from the Official Binary
  • Step 2b: Install Gotify with Docker Compose
  • Step 3: Configure config.yml
  • Step 4: Create a systemd Service (Binary Install)
  • Step 5: Reverse Proxy with Nginx and TLS
  • Step 6: Create Apps, Clients, and Users
  • Step 7: Install the Gotify Android App
  • Step 8: Send Your First Notification with curl
  • Step 9: Plugins and Extensions
  • Integrations: Monitoring Stack Examples
  • Performance and Scaling
  • Troubleshooting
  • FAQ
  • Next Steps
  • What is Gotify?

    Gotify is an open-source, self-hosted push notification service. The entire server ships as a single static Go binary (roughly 15 MB) with an embedded React web UI. You send messages to Gotify via a simple HTTP POST using an application token, and Gotify fans those messages out to every connected client over a persistent WebSocket connection. The source code lives at github.com/gotify/server under the MIT license.

    The architecture has three core concepts:

    • Applications — Senders. Each application has its own token. A backup app, a prometheus app, and a home-assistant app would each have distinct tokens, so you can revoke one without breaking the others.
    • Clients — Receivers. Your phone, your browser, a desktop client, or a custom script. Each client has its own token and sees every message for every app the user owns.
    • Users — The owner of apps and clients. A single-user install is the common case. Multi-user mode lets you share one server across a team.
    Because everything runs over WebSocket (with long-poll fallback), messages appear on connected clients within milliseconds. There is no polling, no Firebase Cloud Messaging dependency, and no external broker. Your alerts travel from your server directly to your phone through your own DNS, your own TLS certificate, and your own binary.

    Typical use cases include monitoring alerts (Prometheus Alertmanager, Grafana, Uptime Kuma), cron job status reports, CI/CD pipeline notifications, home automation events (Home Assistant, Node-RED), security alerts from intrusion detection systems, and personal workflows ("notify me when my rsync backup finishes"). Anything that can curl a URL can push to Gotify in one line.

    Why Self-Host a Push Notification Server?

    Hosted notification services like Pushover, PushBullet, and Pushcut work well, but they come with trade-offs that matter for technical users:

    • Zero per-message cost — Gotify is free forever. Pushover limits you to 10,000 messages per app per month on the USD 5 one-time license. Gotify has no caps.
    • No third-party message inspection — Your alert text never leaves infrastructure you control. For security-sensitive alerts (failed login attempts, IDS triggers, leaked-credential warnings), that matters.
    • Low latency — Messages travel directly from your VPS to your phone over your own domain. No central broker round-trips.
    • No vendor lock-in — The API is a handful of HTTP endpoints. If Gotify ever stops development, swapping to Ntfy or a custom service takes hours, not weeks.
    • Works offline-ish — If your monitoring runs on the same VPS as Gotify, alerts still flow even when upstream providers are down.
    • Arbitrary payload control — Priorities, custom extras, Markdown rendering, click actions — all defined by you, not capped by a SaaS tier.

    CloudCore Plan Recommendations for Gotify

    Gotify is tiny. The hard floor is about 30 MB of RAM for the server itself. Most of your resource budget goes to the reverse proxy, the OS, and any co-located services.

    WorkloadRecommended PlanSpecsPrice
    Gotify only / Gotify + Nginx + small stackCloudCore Starter (recommended)1 vCPU, 2 GB RAM, 40 GB NVMeEUR 7.99/mo
    Gotify + full monitoring (Prometheus, Grafana, Uptime Kuma)CloudCore Professional6 vCPU, 12 GB RAM, 100 GB NVMeEUR 19.99/mo
    Team deployment, 50+ users, pluginsCloudCore Business8 vCPU, 24 GB RAM, 200 GB NVMeEUR 29.99/mo
    For this guide we assume the Starter plan — it has the headroom for Gotify, Nginx with Let's Encrypt, and a few Docker containers without breaking a sweat.

    Gotify vs. Ntfy vs. Pushover vs. Apprise

    Before you install anything, it is worth understanding where Gotify sits in the ecosystem.

    FeatureGotifyNtfyPushoverApprise
    LicenseMIT (server), GPLv3 (Android app)Apache 2.0ProprietaryMIT
    Self-hostableYesYesNoN/A (library)
    Official iOS appNoYesYesN/A
    Official Android appYesYesYesN/A
    Web UIYes (built-in)Yes (built-in)Yes (hosted)No
    User accountsYesOptionalYesN/A
    PersistenceYes (SQLite/MySQL/PostgreSQL)Yes (SQLite)Yes (cloud)No
    Pricing modelFree, self-hostedFree, self-hosted or paid cloudUSD 5 one-time per platformFree, self-hosted
    Message capUnlimitedUnlimited (self-hosted)10,000/app/monthN/A
    Plugin systemYes (Go plugins)LimitedNoN/A (library, fans out to 80+ services)
    Best forSingle-VPS alerting, power usersiOS users, topic-based pub/subNon-technical users, iOS/AndroidScripts that need to fan out to many services
    Short version:

    • Choose Gotify if you want a self-hosted server with a web UI, user accounts, and persistent history — the classic "personal alerting server."
    • Choose Ntfy if you need iOS support or prefer topic-based pub/sub without accounts.
    • Choose Pushover if you are not technical and just want it to work with a one-time fee.
    • Use Apprise inside your scripts when you need to push the same alert to Gotify, Ntfy, Telegram, and Slack simultaneously.
    Many teams run Gotify for infrastructure alerts and Apprise inside their scripts so that each script can fan out to multiple destinations without hardcoding endpoints.

    Prerequisites

    Before you begin, make sure you have:

    • A VPS running Ubuntu 24.04 LTS with root or sudo access — we recommend a CloudCore Starter
    • SSH access to the server
    • A domain name pointed to the VPS (for example, gotify.yourdomain.com) with an A record in place
    • Ports 80 and 443 open in your firewall (for Let's Encrypt and HTTPS)
    • 512 MB of RAM minimum, 2 GB recommended for comfortable co-located services
    • An Android device (optional, but highly recommended for the full push experience)
    Connect to your server:

    bash
    ssh root@your-server-ip

    Step 1: Update and Harden Ubuntu 24.04

    Bring the system up to date and install the packages we will need:

    bash
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y curl wget unzip ufw ca-certificates

    If a new kernel was installed, reboot:

    bash
    sudo reboot

    Configure the firewall. Gotify itself listens on port 80 by default, but we will front it with Nginx on 443, so we only need to expose HTTP and HTTPS:

    bash
    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw --force enable
    sudo ufw status

    Create a dedicated system user so Gotify does not run as root:

    bash
    sudo useradd --system --home /var/lib/gotify --shell /usr/sbin/nologin gotify
    sudo mkdir -p /var/lib/gotify /etc/gotify
    sudo chown -R gotify:gotify /var/lib/gotify /etc/gotify

    You now have a minimal hardened base. Pick one of the two install paths below.

    Step 2a: Install Gotify from the Official Binary

    The binary install is lighter than Docker and integrates natively with systemd and journald. It is the recommended option for single-purpose VPSs.

    Find the latest release version at github.com/gotify/server/releases. At time of writing, Gotify 2.6.x is current. We will download the Linux amd64 build:

    bash
    GOTIFY_VERSION="2.6.1"
    cd /tmp
    wget "https://github.com/gotify/server/releases/download/v${GOTIFY_VERSION}/gotify-linux-amd64.zip"
    unzip gotify-linux-amd64.zip
    sudo mv gotify-linux-amd64 /usr/local/bin/gotify
    sudo chmod +x /usr/local/bin/gotify

    For ARM servers (Ampere, Raspberry Pi, Oracle A1), replace amd64 with arm64.

    Verify the install:

    bash
    /usr/local/bin/gotify version

    Expected output:

    text
    Version:    2.6.1
    Commit:     abcdef1234567890
    BuildDate:  2026-03-10-12:00:00
    ModuleMode: on
    Go:         go1.22.5
    OS:         linux
    Arch:       amd64

    Skip to Step 3.

    Step 2b: Install Gotify with Docker Compose

    If you prefer containerized services, Gotify publishes official images at gotify/server.

    Install Docker Engine if it is not already present:

    bash
    curl -fsSL https://get.docker.com | sudo sh
    sudo usermod -aG docker $USER

    Log out and back in for the group change to apply.

    Create a working directory and compose file:

    bash
    sudo mkdir -p /opt/gotify
    cd /opt/gotify

    Write /opt/gotify/docker-compose.yml:

    yaml
    services:
      gotify:
        image: gotify/server:2.6.1
        container_name: gotify
        restart: unless-stopped
        ports:
          - "127.0.0.1:8080:80"
        environment:
          - TZ=Europe/Berlin
          - GOTIFY_DEFAULTUSER_NAME=admin
          - GOTIFY_DEFAULTUSER_PASS=change-me-now
          - GOTIFY_PASSSTRENGTH=10
          - GOTIFY_UPLOADEDIMAGESDIR=data/images
          - GOTIFY_PLUGINSDIR=data/plugins
          - GOTIFY_SERVER_SSL_ENABLED=false
        volumes:
          - ./data:/app/data

    Notice the port binding 127.0.0.1:8080:80 — this keeps Gotify accessible only from localhost so Nginx can proxy to it. We never expose the container directly to the internet.

    Launch the stack:

    bash
    docker compose up -d
    docker compose logs -f

    You should see Gotify bind to :80 inside the container and be reachable on http://127.0.0.1:8080 on the host.

    Step 3: Configure config.yml

    For binary installs, Gotify reads /etc/gotify/config.yml. For Docker installs, environment variables (as shown above) cover most of what you need, but you can also mount a config.yml into /app/data/config.yml.

    Create /etc/gotify/config.yml (binary installs):

    yaml
    server:
      keepaliveperiodseconds: 0
      listenaddr: "127.0.0.1"
      port: 8080
      ssl:
        enabled: false
      responseheaders:
        X-Content-Type-Options: nosniff
        X-Frame-Options: DENY
        Referrer-Policy: no-referrer
      cors:
        alloworigins:
          - ".+.yourdomain.com"
        allowmethods:
          - "GET"
          - "POST"
        allowheaders:
          - "Authorization"
          - "content-type"
      stream:
        pingperiodseconds: 45
        allowedorigins:
          - ".+.yourdomain.com"

    database: dialect: sqlite3 connection: /var/lib/gotify/gotify.db

    defaultuser: name: admin pass: change-me-now

    passstrength: 10

    uploadedimagesdir: /var/lib/gotify/images pluginsdir: /var/lib/gotify/plugins registration: false

    Key settings explained:

    • server.listenaddr: "127.0.0.1" — Bind only to localhost. Nginx will handle public traffic on 443.
    • server.port: 8080 — Internal port. Does not need to match the public port.
    • server.ssl.enabled: false — TLS is terminated by Nginx, not Gotify. Simpler cert renewal with Certbot.
    • database.dialect: sqlite3 — Default. For MySQL, use dialect: mysql and a DSN connection string. For PostgreSQL, use postgres.
    • defaultuser — The admin account created on first boot. Change the password immediately in the web UI.
    • passstrength: 10 — bcrypt cost factor. 10 is the sweet spot.
    • registration: false — Disables public sign-up. You add users manually from the admin UI.
    • cors.alloworigins — Restricts which origins may hit the REST API from a browser. Lock this down.
    Fix ownership:

    bash
    sudo chown -R gotify:gotify /etc/gotify /var/lib/gotify

    Using MySQL instead of SQLite looks like this:

    yaml
    database:
      dialect: mysql
      connection: gotify:s3cret@tcp(127.0.0.1:3306)/gotify?charset=utf8&parseTime=True&loc=Local

    Switch only if you expect tens of thousands of messages per day or need multi-node HA. For everyone else, SQLite handles millions of rows without noticeable slowdown.

    Step 4: Create a systemd Service (Binary Install)

    Write /etc/systemd/system/gotify.service:

    ini
    [Unit]
    Description=Gotify Server
    Documentation=https://gotify.net
    After=network-online.target
    Wants=network-online.target

    [Service] Type=simple User=gotify Group=gotify WorkingDirectory=/var/lib/gotify Environment=GOTIFY_CONFIG=/etc/gotify/config.yml ExecStart=/usr/local/bin/gotify Restart=on-failure RestartSec=5

    Hardening

    NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ReadWritePaths=/var/lib/gotify ProtectHome=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictSUIDSGID=true LockPersonality=true

    [Install] WantedBy=multi-user.target

    Enable and start:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable --now gotify
    sudo systemctl status gotify

    Expected status output:

    text
    ● gotify.service - Gotify Server
         Loaded: loaded (/etc/systemd/system/gotify.service; enabled; preset: enabled)
         Active: active (running) since Thu 2026-04-16 12:05:00 UTC; 3s ago
       Main PID: 4215 (gotify)
          Tasks: 8 (limit: 4631)
         Memory: 28.4M

    Confirm the local endpoint answers:

    bash
    curl -s http://127.0.0.1:8080/health

    Expected output:

    json
    {"health":"green","database":"green"}

    Step 5: Reverse Proxy with Nginx and TLS

    Gotify must sit behind a TLS-terminating proxy because the Android app requires HTTPS and because WebSocket traffic should never traverse the public internet unencrypted. We will use Nginx with Let's Encrypt. For a deeper dive, see our Nginx install guide.

    Install Nginx and Certbot:

    bash
    sudo apt install -y nginx certbot python3-certbot-nginx

    Obtain a certificate (replace the domain):

    bash
    sudo certbot certonly --nginx -d gotify.yourdomain.com --agree-tos --email [email protected] --no-eff-email

    Write /etc/nginx/sites-available/gotify:

    nginx
    map $http_upgrade $connection_upgrade {
        default upgrade;
        '' close;
    }

    server { listen 80; server_name gotify.yourdomain.com; return 301 https://$host$request_uri; }

    server { listen 443 ssl http2; server_name gotify.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/gotify.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/gotify.yourdomain.com/privkey.pem;

    ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=63072000" always; add_header X-Content-Type-Options nosniff always; add_header X-Frame-Options DENY always; add_header Referrer-Policy no-referrer always;

    client_max_body_size 32m;

    location / { proxy_pass http://127.0.0.1:8080; proxy_http_version 1.1;

    proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;

    # WebSocket support for /stream proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade;

    proxy_read_timeout 3600s; proxy_send_timeout 3600s; proxy_connect_timeout 60s; proxy_buffering off; } }

    The map block and proxy_set_header Upgrade/Connection lines are critical — without them, the WebSocket connection used by clients to receive push messages silently fails and messages only arrive after an HTTP refresh.

    Enable and reload:

    bash
    sudo ln -s /etc/nginx/sites-available/gotify /etc/nginx/sites-enabled/
    sudo nginx -t
    sudo systemctl reload nginx

    Visit https://gotify.yourdomain.com in your browser. You should see the Gotify login screen.

    Step 6: Create Apps, Clients, and Users

    Log in with admin and the password you set in config.yml (or the Docker env). Change the password immediately under Users -> admin -> Edit password.

    Create an Application (Sender)

    Navigate to Apps -> Create Application:

    • Name — backups
    • Description — rsync + restic nightly backups
    • Default priority — 5
    Click Create. Gotify generates an application token that looks like A0Abc123def456. Copy this token. Every alert from your backup script will include it as the authentication credential.

    Repeat for each sender you plan to integrate: prometheus, uptime-kuma, home-assistant, ci-cd, etc.

    Create Clients (Receivers)

    A client token represents one subscriber. Your phone is one client. Your second phone is another. A desktop browser bookmark can be yet another.

    Navigate to Clients -> Create Client:

    • Name — Pixel 8
    Copy the token. You will paste it into the Android app during setup. Any client with a valid token receives every message sent to any app owned by the client's user.

    Create Additional Users (Optional)

    If your team shares one Gotify instance, create a user per team member. Navigate to Users -> Create User and set a role of user (normal) or admin (full access). Each user has their own apps and clients and cannot see other users' messages.

    Step 7: Install the Gotify Android App

    Install the Gotify Android client from F-Droid or the Google Play Store.

    On first launch:

  • Tap Add Server.
  • Enter https://gotify.yourdomain.com.
  • Log in with a user account (not an application token — that comes next).
  • The app requests permission to create a client token on your behalf. Confirm.
  • Allow background notifications and disable battery optimization for the app (Android will otherwise kill the WebSocket connection after a few minutes).
  • Once connected, the app shows a list of received messages grouped by application. Tapping a message opens the full body with Markdown rendered.

    A critical detail on Android 12+: grant the app the Alarms & reminders permission and, if available on your device, Autostart. These keep the WebSocket alive across deep-sleep windows. Without them, you may miss notifications for a few minutes after the phone sleeps.

    Step 8: Send Your First Notification with curl

    Test end to end. Replace APP_TOKEN with the token from the backups app you created:

    bash
    curl -X POST "https://gotify.yourdomain.com/message?token=APP_TOKEN" \
      -F "title=Backup Completed" \
      -F "message=Nightly restic backup finished in 4m12s (2.3 GB transferred)." \
      -F "priority=5"

    Within a second or two, the message appears in the web UI and on your phone.

    Priority Levels

    Gotify uses a numeric priority scale. The Android app maps these to Android notification channels so you can silence low-priority alerts without missing critical ones.

    PriorityAndroid Behavior
    0-3Min importance, no sound
    4-7Default importance, silent by default depending on channel
    8+High importance, sound and vibration, bypasses Do Not Disturb (if user allows)

    Markdown and Extras

    Gotify supports Markdown rendering via the content_type extra:

    bash
    curl -X POST "https://gotify.yourdomain.com/message?token=APP_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{
        "title": "Disk Usage Alert",
        "message": "/var/log is at 92%.\n\n- /var/log/journal — 4.1 GB\n- /var/log/nginx — 1.8 GB\n\nRun journalctl --vacuum-time=7d.",
        "priority": 8,
        "extras": {
          "client::display": {
            "contentType": "text/markdown"
          },
          "client::notification": {
            "click": { "url": "https://grafana.yourdomain.com/d/disk-dashboard" }
          }
        }
      }'

    The client::notification.click.url extra makes the Android notification open a URL when tapped — useful for linking straight to the Grafana dashboard that triggered the alert.

    Send from Python

    python
    import requests

    requests.post( "https://gotify.yourdomain.com/message", params={"token": "APP_TOKEN"}, json={ "title": "CI Pipeline Passed", "message": "Commit abc1234 passed all checks in 2m17s.", "priority": 4, }, timeout=5, )

    Send from Go

    go
    http.PostForm("https://gotify.yourdomain.com/message?token=APP_TOKEN",
        url.Values{
            "title":    {"Deploy OK"},
            "message":  {"Release v2.4.1 is live."},
            "priority": {"5"},
        })

    Step 9: Plugins and Extensions

    Gotify has a Go-based plugin system that lets you extend the server with custom message sources. Popular community plugins include:

    • gotify/cmd-plugin — Runs shell commands on incoming messages.
    • gotify/plugin-template — Scaffolding for building your own.
    • RSS plugins — Poll a feed and push new items as notifications.
    Plugins are compiled as Go shared libraries (.so files) against the exact Gotify version you are running. Drop them into /var/lib/gotify/plugins/ and restart:

    bash
    sudo systemctl restart gotify

    The web UI exposes plugin config under Plugins once they are loaded. For most users, the REST API plus a reverse proxy is enough — plugins are advanced territory.

    Integrations: Monitoring Stack Examples

    Prometheus Alertmanager

    Add Gotify as a webhook receiver in alertmanager.yml. See our deep dive in the Alertmanager install guide:

    yaml
    receivers:
      - name: gotify
        webhook_configs:
          - url: "https://gotify.yourdomain.com/message?token=PROM_APP_TOKEN"
            send_resolved: true
            http_config:
              follow_redirects: true

    Gotify accepts Alertmanager's JSON payload if you wrap it with a small adapter, or you can use the community alertmanager-gotify-bridge sidecar which translates the schema.

    Uptime Kuma

    Uptime Kuma has native Gotify support. In Notifications -> Setup Notification -> Gotify:

    • Gotify Application Token — paste your uptime-kuma app token
    • Gotify Server URL — https://gotify.yourdomain.com
    • Priority — 8 for outages, 4 for recovery
    Tests fire in under a second and arrive on Android before your coffee cools.

    Home Assistant

    In configuration.yaml:

    yaml
    notify:
      - name: gotify
        platform: rest
        resource: https://gotify.yourdomain.com/message
        method: POST_JSON
        headers:
          X-Gotify-Key: HA_APP_TOKEN
        data:
          priority: 5
        title_param_name: title
        message_param_name: message

    Any automation can now call notify.gotify to push a message.

    Shell Script One-Liner

    Define a helper in ~/.bashrc:

    bash
    gotify() {
      local title="${1:-Notification}"
      local message="${2:-(no body)}"
      local priority="${3:-5}"
      curl -s -X POST "https://gotify.yourdomain.com/message?token=$GOTIFY_TOKEN" \
        -F "title=$title" \
        -F "message=$message" \
        -F "priority=$priority" > /dev/null
    }

    Then sprinkle gotify "Backup done" "$(du -sh /backups | cut -f1)" 4 throughout your cron jobs and scripts.

    Performance and Scaling

    Gotify is built on Gin (HTTP) and Gorilla WebSocket. Single-node performance is remarkable:

    • Idle RAM — ~30 MB
    • RAM per 1,000 concurrent WebSocket clients — ~60 MB
    • Messages per second — 5,000+ on a 1 vCPU VPS with SQLite, limited mostly by fsync
    • Message latency — 5-50 ms local network, 50-200 ms across continents
    For a personal or small-team deployment, you will never hit a ceiling. For larger deployments:

    • Switch SQLite to PostgreSQL for concurrent writes.
    • Set server.keepaliveperiodseconds to 30 for mobile clients behind NATs with short idle timeouts.
    • Put Gotify behind HAProxy or Cloudflare for TLS offload at higher volume.
    • Run multiple Gotify replicas sharing a PostgreSQL backend if you exceed 10,000 concurrent clients per node.

    Backups

    The entire state lives in one SQLite file plus the plugins directory. Back it up nightly:

    bash
    sudo install -m 0750 -o gotify -g gotify -d /var/backups/gotify
    sudo -u gotify sqlite3 /var/lib/gotify/gotify.db ".backup /var/backups/gotify/gotify-$(date +%F).db"

    Pipe the backup file into your offsite backup job (restic, rclone, etc.).

    Troubleshooting

    ProblemCauseSolution
    502 Bad Gateway from NginxGotify not running or wrong upstream portsudo systemctl status gotify, confirm listenaddr/port in config.yml match proxy_pass
    Web UI loads but messages do not arrive in real timeNginx missing WebSocket headersVerify proxy_set_header Upgrade $http_upgrade and the map block in your Nginx config
    Android app shows "Connection failed"TLS cert invalid or mixed HTTP/HTTPSUse https:// only, confirm Let's Encrypt renewal with sudo certbot renew --dry-run
    Notifications delayed on AndroidAggressive battery optimizationDisable battery optimization for the Gotify app; grant autostart permission
    curl: (22) The requested URL returned error: 401Wrong or missing app tokenCheck the token exactly; tokens are case-sensitive and include leading capital letter
    Gotify fails to start after upgradeDatabase schema migration failedStop service, back up gotify.db, then start with journalctl -u gotify -f to see exact error
    High memory use after weeks of uptimeOld messages accumulatingGotify auto-prunes per-app message history; lower per-app message limits in the admin UI
    Messages missing after rebootDocker volume not mountedConfirm ./data:/app/data in docker-compose.yml and that ./data/gotify.db exists

    Useful Debug Commands

    Stream logs in real time:

    bash
    sudo journalctl -u gotify -f

    Check the public endpoint with verbose output:

    bash
    curl -vk https://gotify.yourdomain.com/health

    Tail Nginx error log during a failed connection:

    bash
    sudo tail -f /var/log/nginx/error.log

    Test the WebSocket endpoint directly (requires websocat):

    bash
    sudo apt install -y websocat
    websocat -H="X-Gotify-Key: CLIENT_TOKEN" wss://gotify.yourdomain.com/stream

    You should see a JSON frame per incoming message.

    FAQ

    What are the minimum hardware requirements for Gotify?

    Gotify is one of the lightest self-hosted services you can run. The server binary uses about 30 MB of RAM at idle and tens of MB under load. A 1 vCPU, 512 MB RAM VPS is enough for a personal instance. For production alongside Nginx, Let's Encrypt, and a small monitoring stack, the CloudCore Starter plan at EUR 7.99/month (1 vCPU, 2 GB RAM, 40 GB NVMe) is the recommended sweet spot. Teams running dozens of apps and hundreds of clients are fine on CloudCore Professional.

    Is Gotify free and open source?

    Yes. The Gotify server is MIT-licensed and fully open source. Source code is at github.com/gotify/server. The Android app is GPLv3. There are no paid tiers, no telemetry, no license keys, and no usage caps when you self-host. The project is maintained by volunteers and accepts contributions on GitHub.

    How is Gotify different from Ntfy, Pushover, and Apprise?

    Gotify is a self-hosted server with a web UI, user accounts, persistent SQLite/MySQL storage, and a plugin system. Ntfy is also self-hostable but uses topic-based pub/sub with no built-in accounts — anyone who knows your topic can listen. Pushover is a paid managed service (one-time USD 5 per platform, with a 10,000 messages-per-app-per-month cap). Apprise is a Python library that fans out notifications to 80+ services — it is not a server, so you typically combine Apprise inside your scripts with Gotify as one of its destinations.

    Does Gotify support iOS?

    There is no official Gotify iOS app. Apple Push Notification Service requires a paid Apple Developer account and a central relay server — a model that conflicts with Gotify's "everything self-hosted" philosophy. Third-party iOS clients (like Gomify) exist but vary in quality. If iOS is your primary mobile platform, Ntfy is the better choice — it has an official iOS app that polls through an open relay.

    Can I use Gotify without the Android app?

    Absolutely. The built-in web UI shows all messages in real time via WebSocket and works on any desktop browser. You can also write custom clients that authenticate with a client token and subscribe to the /stream WebSocket endpoint — a dozen lines of JavaScript, Go, or Python is enough. Some users run a permanent browser tab or a desktop Electron wrapper for work hours and skip mobile entirely.

    What database does Gotify use?

    Gotify defaults to SQLite, stored in /var/lib/gotify/gotify.db. SQLite is more than sufficient for almost any deployment — it handles millions of messages per day on modest hardware. For high-volume or multi-node setups, switch to MySQL or PostgreSQL by editing the database.dialect and database.connection fields in config.yml. Most users never need to switch.

    Will Gotify messages survive a server reboot?

    Yes. Messages are persisted to the database when Gotify receives them. After a reboot, all messages that were not yet delivered to a client remain queued, and connected clients see their full history on reconnect. You can also configure per-app message retention caps from the admin UI to prevent unbounded growth.

    How do I migrate from a hosted service like Pushover?

    For most integrations, you only change the URL and token. Scripts that POST to https://api.pushover.net/1/messages.json can be updated to POST to https://gotify.yourdomain.com/message?token=... with minimal field renaming (title and message are the same, priority uses a different scale). Write a small wrapper function and drop it in place of the Pushover client. Migrations typically take an afternoon.

    Next Steps

    Now that Gotify is live on your VPS, here is how to build out the surrounding stack:

    • Wire up Uptime Kuma — Deploy Uptime Kuma and add Gotify as a notification channel. Within minutes you will get real push alerts every time a monitored endpoint goes down.
    • Bring in Prometheus + Alertmanager — Follow our Alertmanager install guide and forward critical infrastructure alerts to Gotify with priority 8 so they bypass Do Not Disturb.
    • Harden the Nginx front-end further — Our Nginx install and hardening guide covers fail2ban, rate limiting, and HTTP/3 for Gotify's public endpoint.
    • Compare with Ntfy — If you need iOS support or topic-based pub/sub, read the Ntfy install guide. Many teams run both.
    • Wrap it with Apprise — Install Apprise in your scripts so a single notification call fans out to Gotify, Slack, Telegram, and email. Great for cross-team visibility.
    • Back up the database offsite — Schedule nightly SQLite snapshots and sync them with restic or rclone to an S3-compatible bucket. Losing gotify.db is losing your notification history.
    • Build a dashboard tile — Expose /health and /metric on an internal Grafana dashboard alongside your other services. Treat Gotify like any other production service.

    Ready to deploy? Grab a CloudCore Starter VPS for EUR 7.99/month and you will have Gotify running with TLS, an Android client, and monitoring integrations in under 30 minutes. No per-message fees, no vendor lock-in, no third party in your alert path — just a tiny Go binary doing exactly one job extremely well.

    Was this article helpful?

    ← Back to Install GuidesBrowse all categories →

    Still have questions?

    Contact Support →Submit a Ticket