Deployment guide

Gotify

Overview

Gotify is a popular open-source, self-hosted push notification server. You send messages from any script, server or application over a simple REST API. You receive them in real time in the bundled web app, the official Android client, or any websocket consumer. It is a privacy-respecting alternative to hosted push services, running on infrastructure you control.

This SecureAnyCloud image runs the Gotify server, a single Go binary that serves both the REST API and the web app, behind nginx on port 80, with PostgreSQL as the datastore. On the first boot of every VM, a one-shot service creates an empty database, rotates the PostgreSQL password and seeds a single administrator account with its own password. Open registration is disabled, and two VMs deployed from the same image never share credentials.

What you get:

  • Gotify 2.9.1 server (single Go binary, bundles the web UI) at /opt/gotify/gotify, listening on 127.0.0.1:8080
  • An nginx reverse proxy on :80, with websocket upgrade support, in front of the Gotify server on loopback :8080
  • A PostgreSQL datastore on 127.0.0.1:5432, bound to the loopback interface only, with its datadir on a dedicated Azure data disk at /var/lib/postgresql
  • A second dedicated Azure data disk at /var/lib/gotify for uploaded application images and plugins. Both disks are separate from the OS disk and re-provisioned with every VM
  • A fresh empty database on first boot, which also yields a new per-instance session signing key
  • Per-VM administrator and PostgreSQL passwords generated at first boot, with the login written to /root/gotify-credentials.txt in a root-only file (mode 0600)
  • gotify.service, postgresql.service and nginx.service as enabled, active systemd units
  • A fully patched Ubuntu 24.04 LTS security baseline at capture time, with unattended security updates enabled
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting size, because Gotify is lightweight. Scale up for very large message volumes.

Configure the network security group (NSG) to allow inbound 22/tcp from your management network and 80/tcp from the networks that your users and senders will reach Gotify on. Also allow 443/tcp once you enable HTTPS.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Gotify by SecureAnyCloud, and select Create.

On the Basics tab, pick your subscription, resource group, region and size. Under Administrator account, choose SSH public key and paste your key. Under Inbound port rules, allow SSH (22) and HTTP (80).

Review the two dedicated data disks on the Disks tab, then select Review + create and Create. First-boot initialisation takes approximately one minute after the VM starts.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:gotify-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name gotify \
  --image sac1647283583153:gotify-ubuntu-24-04:default:latest \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_ed25519.pub \
  --vnet-name <your-vnet> --subnet <your-subnet> \
  --public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name gotify --port 80 --priority 1010

Step 3 - Connect to your VM

Connect to the VM over SSH:

ssh azureuser@<vm-public-ip>

Step 4 - Verify the Gotify stack

Three systemd services make up the stack. Confirm that all three are active and that the open version endpoint answers through nginx:

systemctl is-active gotify postgresql nginx curl -s http://127.0.0.1/version

All three lines read active, and the version endpoint returns JSON containing "version":"2.9.1".

Next, confirm that the Gotify server and PostgreSQL bind to loopback only, and that the two dedicated data disks are mounted:

ss -tln 2>/dev/null | grep -E ':(80|8080|5432) ' || sudo ss -tln | grep -E ':(80|8080|5432) ' df -h /var/lib/postgresql /var/lib/gotify | tail -2

Port 80 (nginx) listens on all interfaces. Ports 8080 (Gotify) and 5432 (PostgreSQL) are bound to 127.0.0.1 only.

Step 5 - Retrieve your administrator login

The login for the admin account is generated on the first boot of your VM and stored in a root-only file. The file lists the Gotify URL, the administrator username (admin), the password and the PostgreSQL credentials:

sudo cat /root/gotify-credentials.txt

Store the password in your password manager and treat the file as sensitive.

Step 6 - Verify authentication

The API rejects anonymous requests and accepts the generated administrator password. The first command reports 401 and the second reports 200:

curl -s -o /dev/null -w 'anonymous /current/user HTTP %{http_code}\n' http://127.0.0.1/current/user
PASS=$(sudo grep '^gotify.admin.pass=' /root/gotify-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'authenticated /current/user HTTP %{http_code}\n' -u admin:$PASS http://127.0.0.1/current/user

Step 7 - Sign in to the web app

Open a web browser and go to http://<vm-public-ip>/. Sign in with the username admin and the password from /root/gotify-credentials.txt.

The web app shows the message stream: all notifications, grouped by application, arriving in real time.

Step 8 - Create an application and push your first message

Senders authenticate with an application token, not your administrator password. Create an application in the web app under Apps, or over the API as shown below. Then push a message with its token:

PASS=$(sudo grep '^gotify.admin.pass=' /root/gotify-credentials.txt | cut -d= -f2-)
APP_TOKEN=$(curl -s -u admin:$PASS -X POST http://127.0.0.1/application -F name=server-alerts -F description='Demo application' | jq -r .token)
echo "application token: $APP_TOKEN" curl -s -X POST "http://127.0.0.1/message?token=$APP_TOKEN" -F title='Backup finished' -F message='Nightly backup completed successfully' -F priority=5 | jq .

The message appears instantly in the web app and on every connected client. Any script or application that can issue an HTTP POST can now push notifications. When you send from another host, replace 127.0.0.1 with your VM's address. You manage applications, clients and users in the web app.

Confirm that the message was stored by reading the stream back over the API:

PASS=$(sudo grep '^gotify.admin.pass=' /root/gotify-credentials.txt | cut -d= -f2-)
curl -s -u admin:$PASS http://127.0.0.1/message | jq '.messages[0] | {title, message, priority}'

Step 9 - Receive messages in real time

The web app receives messages over a websocket the moment they arrive.

For your phone, install the official Gotify Android client from F-Droid or Google Play. Point it at http://<vm-public-ip>/ and sign in. The client registers itself and shows every push as a native Android notification.

Any other consumer can subscribe to the stream with a client token:

curl -s -N -H "X-Gotify-Key: <client-token>" "ws://<vm-public-ip>/stream"

Because this is your own self-hosted VM, your notification traffic never leaves infrastructure you control.

Security baseline

The image is captured with the Ubuntu 24.04 security baseline fully applied, including phased updates. It ships with unattended security upgrades enabled, so your VM keeps patching itself after deployment:

cat /etc/apt/apt.conf.d/20auto-upgrades systemctl is-enabled unattended-upgrades

Enabling HTTPS

For production, serve Gotify over HTTPS so that login credentials and message traffic are encrypted in transit. Point a DNS A record at the VM's public IP address, then install a certificate with Certbot. Replace the domain with your own:

sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.example.com

After you enable HTTPS, open 443/tcp in the NSG and update the URL in your Android client and senders to https://. Then restrict or close inbound port 80.

Backup and maintenance

All Gotify state lives on the two dedicated Azure data disks. The PostgreSQL datadir at /var/lib/postgresql holds users, applications, clients and messages. Uploaded application images and plugins are at /var/lib/gotify.

Both disks are separate from the OS disk, so the simplest backup is an Azure disk snapshot:

az snapshot create --resource-group <your-rg> --name gotify-db-backup --source <data-disk-id>

For a logical database backup, run sudo -u postgres pg_dump gotify instead and copy the dump to object storage on a schedule.

Apply operating system security updates with sudo apt update && sudo apt upgrade. The services restart cleanly, and Gotify resumes serving immediately because all state is on the persistent data disks. Restart the server at any time with sudo systemctl restart gotify.

Support

This image comes with 24/7 SecureAnyCloud support. Contact us by email or chat for deployment help, integrations, TLS termination and database administration.

Gotify itself is open-source software licensed under the MIT license.

All product and company names are trademarks or registered trademarks of their respective holders. Use of them does not imply any affiliation with or endorsement by them.

Deploy on Azure

Launch Gotify on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure