Observability Azure

GoAlert on Ubuntu 24.04 on Azure User Guide

| Product: GoAlert on Ubuntu 24.04 LTS on Azure

Overview

This image runs GoAlert 0.34.1, the open source on-call scheduling and alerting platform maintained by Target, on Ubuntu 24.04 LTS. GoAlert routes incidents to the right responder at the right time through on-call schedules, rotations and escalation policies, and delivers notifications by SMS, voice call, email and Slack once you connect a delivery provider. It ships as a single self-contained Go binary, run by an unprivileged goalert system account under a systemd service that starts it on boot and restarts it on failure, backed by a local PostgreSQL 16 database.

GoAlert listens on the loopback address 127.0.0.1:8081 by design and is never exposed directly. nginx is installed as a reverse proxy on port 80 that forwards every request to GoAlert, including the WebSocket connections the UI uses for live updates. PostgreSQL is bound to loopback only.

What is included:

  • GoAlert 0.34.1 (official release binary), run by systemd as the unprivileged goalert user
  • The GoAlert web UI and API on :80 (nginx reverse proxy to 127.0.0.1:8081)
  • A per-VM admin account created on first boot and recorded in a root-only file
  • A per-VM data encryption key and a per-VM PostgreSQL password, both generated on first boot
  • PostgreSQL 16, bound to loopback, holding schedules, escalation policies, services and users
  • postgresql.service, goalert.service and nginx.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • 24/7 cloudimg support

Prerequisites

An active Azure subscription, an SSH key pair, and a VNet plus subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a sensible starting point; size up for larger teams. NSG inbound: allow 22/tcp from your management network and 80/tcp for the web UI. GoAlert serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain and set the Public URL accordingly (see Step 8).

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for GoAlert by cloudimg, and select Create. On Basics 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). Then Review + create then Create.

Step 2 - Deploy from the Azure CLI

az vm create \
  --resource-group <your-rg> \
  --name goalert \
  --image <marketplace-image-urn> \
  --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 goalert --port 80 --priority 1010

Step 3 - Connect to your VM

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

GoAlert is served by nginx on port 80, which reverse-proxies to the Go binary on 127.0.0.1:8081, which in turn talks to PostgreSQL on 127.0.0.1:5432. Confirm all three services are active, and note that only nginx listens on a public address - GoAlert and PostgreSQL are bound to loopback:

systemctl is-active postgresql goalert nginx goalert-firstboot
sudo ss -tlnp | grep -E ':80 |:8081 |:5432 '

Expected output:

active
active
active
active
LISTEN 0      4096       127.0.0.1:8081      0.0.0.0:*
LISTEN 0      511          0.0.0.0:80        0.0.0.0:*
LISTEN 0      200        127.0.0.1:5432      0.0.0.0:*
LISTEN 0      511             [::]:80           [::]:*

postgresql, goalert, nginx and goalert-firstboot active, with goalert on loopback 8081, PostgreSQL on loopback 5432 and only nginx on public port 80

Step 5 - Retrieve your admin password

On the first boot of every VM, goalert-firstboot.service generates a unique administrator password, a PostgreSQL password and a data encryption key, runs the database migrations, then creates the single admin account with goalert add-user. It writes the admin username, password and dashboard URL into a root-only file. Read it with:

sudo cat /root/goalert-credentials.txt

There is no default or shared credential in the image: the admin account does not exist until first boot creates it with a password unique to this VM. Store the password somewhere safe.

The per-VM GoAlert admin credentials file, with the generated password and dashboard URL

Step 6 - Confirm the version and health endpoint

nginx serves an unauthenticated /healthz endpoint for load balancer probes. Confirm the GoAlert and PostgreSQL versions and that the UI responds:

goalert version | head -4
psql --version
curl -sI http://127.0.0.1/healthz | head -1
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/

Expected output:

Version:   v0.34.1
GitCommit: 0918387... (clean)
BuildDate: 2025-10-06T20:20:57Z
GoVersion: go1.25.1 (gc)
psql (PostgreSQL) 16.14 (Ubuntu 16.14-0ubuntu0.24.04.1)
HTTP/1.1 200 OK
200

GoAlert v0.34.1 and PostgreSQL 16.14 versions, the healthz endpoint returning 200 and the web UI returning 200

Step 7 - The local PostgreSQL backend

Everything GoAlert stores - schedules, rotations, escalation policies, services, users and alerts - lives in the local goalert PostgreSQL database, owned by the goalert role and reachable only over loopback. The image ships with the schema migrated and exactly one account (the per-VM admin):

sudo -u postgres psql -l | grep goalert
sudo -u postgres psql -d goalert -tAc "SELECT count(*) FROM information_schema.tables WHERE table_schema='public'"
sudo -u postgres psql -d goalert -tAc "SELECT count(*) FROM users"

The goalert PostgreSQL database with its migrated schema tables and a single admin user account

Step 8 - Sign in to the web UI

Browse to http://<vm-public-ip>/. Enter admin and the password from Step 5.

The GoAlert sign-in page

GoAlert validates that your browser's address matches its configured Public URL when you sign in. On this image goalert-configure.service sets the Public URL to the VM's detected public IP on every boot, so signing in over the Azure public IP works out of the box. If you reach GoAlert through a different hostname (a DNS name, a load balancer or your own TLS proxy), set the correct URL once: edit GOALERT_PUBLIC_URL in /etc/goalert/goalert.env and run sudo systemctl restart goalert, or set it from the Admin page after signing in.

Step 9 - Build your on-call schedules

Open Schedules from the left navigation and click Create Schedule. Give the schedule a name and time zone, then add rotations and assign users to define who is on call and when. Schedules are the foundation of GoAlert: escalation policies notify whoever a schedule says is on call at the moment an alert fires.

The GoAlert Schedules page listing on-call schedules with a Create Schedule action

Step 10 - Define escalation policies

Open Escalation Policies and click Create Escalation Policy. An escalation policy is an ordered set of steps - notify this schedule, wait N minutes, then notify the next - with an optional repeat count, so an unacknowledged alert is escalated automatically until someone responds.

The GoAlert Escalation Policies page with a production incidents policy

Step 11 - Register your services

Open Services and click Create Service, attaching it to an escalation policy. A service represents something you want alerts for (an API, a website, a job). Each service gets integration keys - a generic API endpoint, plus Grafana, Prometheus Alertmanager, email and other formats - that your monitoring tools call to raise an alert, which GoAlert then routes through the service's escalation policy.

The GoAlert Services page listing services attached to escalation policies

Step 12 - Enable alert delivery (SMS, voice, email, Slack)

The image ships the fully functional UI, schedules, escalation policies and services. To actually deliver notifications you connect a provider from the Admin -> Config page after signing in:

  • Twilio for SMS and voice calls (Account SID, Auth Token, From Number)
  • SMTP for email notifications to responders
  • Slack to notify channels as part of an escalation policy
  • Mailgun or the built-in SMTP ingress to create alerts from inbound email

Each provider is toggled on and configured in its own section of the Admin Config page. See the GoAlert documentation for provider-specific setup.

Maintenance

  • Back up your data: everything GoAlert stores lives in the local goalert PostgreSQL database. Snapshot the OS disk, or use pg_dump goalert for logical backups.
  • Public URL: the Public URL is set to the detected public IP on each boot. For a stable hostname or HTTPS, set GOALERT_PUBLIC_URL in /etc/goalert/goalert.env and sudo systemctl restart goalert.
  • Security updates: unattended security upgrades are enabled, so the OS keeps itself patched. Reboot when a new kernel is installed.
  • TLS: for production, front GoAlert with your own domain and a TLS-terminating reverse proxy or Azure Application Gateway, and set the Public URL to the https:// address.
  • Add responders: open Users to manage accounts, and enable GitHub or OIDC single sign-on from Admin -> Config for larger teams.

Support

cloudimg provides 24/7/365 expert technical support for this image. Contact support@cloudimg.co.uk. GoAlert is licensed under the Apache License 2.0. This image is provided by cloudimg; additional charges apply for build, maintenance and 24/7 support.