Observability AWS

GoAlert On-Call Alerting on AWS User Guide

| Product: GoAlert on AWS

Overview

This AMI runs GoAlert 0.34.1, the open source on-call scheduling and alerting platform maintained by Target. 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 on a dedicated data volume.

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 upstream release binary, sha256 pinned), 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-instance admin account created on first boot and recorded in a root-only file
  • A per-instance data encryption key and a per-instance PostgreSQL password, both generated on first boot
  • PostgreSQL 16 on a dedicated EBS data volume, 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 Elastic Load Balancing health checks
  • 24/7 cloudimg support

Connecting to your instance

Connect over SSH as the default login user for the operating system variant you launched. The security group must allow inbound 22/tcp from your management network and 80/tcp for the web UI.

OS variant SSH login user Connect
Ubuntu 24.04 ubuntu ssh ubuntu@<instance-public-ip>

Prerequisites

An AWS account, an EC2 key pair, and a VPC subnet in the target region. m5.large (2 vCPU / 8 GiB RAM) is a sensible starting point; size up for larger teams. Security group 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 6).

Step 1 - Launch the instance

Subscribe to the listing in AWS Marketplace and launch it through the EC2 console or the AWS CLI, selecting your key pair, an m5.large (or larger) instance type, and a security group that allows 22/tcp and 80/tcp inbound. The dedicated PostgreSQL data volume is attached and mounted automatically.

Step 2 - Connect and 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. Connect over SSH, then 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      511          0.0.0.0:80        0.0.0.0:*
LISTEN 0      200        127.0.0.1:5432      0.0.0.0:*
LISTEN 0      4096       127.0.0.1:8081      0.0.0.0:*
LISTEN 0      511             [::]:80           [::]:*

Step 3 - Retrieve your admin password

On the first boot of every instance, 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 instance. Store the password somewhere safe.

Step 4 - Confirm the version and health endpoint

nginx serves an unauthenticated /healthz endpoint for load balancer health checks. 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: 0918387e38650aaddd6a923d445ee992f64d6ab6 (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

Step 5 - The local PostgreSQL backend on a dedicated volume

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 database lives on a dedicated EBS data volume mounted at /var/lib/postgresql, separate from the OS disk and independently resizable and snapshottable. The image ships with the schema migrated and exactly one account (the per-instance admin):

findmnt /var/lib/postgresql
sudo -u postgres psql -tAc "SHOW data_directory"
sudo -u postgres psql -l | grep goalert
sudo -u postgres psql -d goalert -tAc "SELECT count(*) FROM users"

Expected output:

TARGET              SOURCE       FSTYPE OPTIONS
/var/lib/postgresql /dev/nvme1n1 ext4   rw,relatime
/var/lib/postgresql/16/main
 goalert | goalert | UTF8 | libc | C.UTF-8 | C.UTF-8 |
1

Step 6 - Sign in to the web UI

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

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 instance's detected public IP on every boot (resolved through EC2 IMDSv2), so signing in over the instance 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 7 - Build your on-call schedules

Open Schedules from the 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.

A GoAlert on-call schedule with its time zone and description

Step 8 - 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.

A GoAlert escalation policy with a step that notifies the on-call schedule

Step 9 - Register your services and receive alerts

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. Active alerts appear in the UI where responders acknowledge and close them.

An active unacknowledged GoAlert alert on a production service

Step 10 - 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 on the dedicated data volume. Snapshot the volume with EBS snapshots or AWS Backup, or use pg_dump goalert for logical backups.
  • Resize storage: the database volume is independent of the OS disk - grow it with an EBS volume modification and resize2fs as your alert history grows.
  • 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: keep the OS patched with sudo apt-get update && sudo apt-get upgrade, and reboot when a new kernel is installed.
  • TLS: for production, front GoAlert with your own domain and a TLS-terminating reverse proxy or an Application Load Balancer, 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.