Gatus 5.36.0 on AWS User Guide
Overview
This guide covers launching and verifying Gatus 5.36.0 on Ubuntu 24.04 from cloudimg on Amazon EC2. It is one version of the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace; you pick it as the software version when you subscribe and launch. The other products in the listing are described on the listing's guide hub.
Gatus is an open source health dashboard and status page. You describe the services you want to watch in one YAML file: each endpoint has a URL (HTTP, HTTPS, TCP, ICMP, DNS, SSH, STARTTLS, WebSocket or gRPC), a check interval and a list of conditions such as [STATUS] == 200, [RESPONSE_TIME] < 300, [BODY].status == UP or [CERTIFICATE_EXPIRATION] > 720h. Gatus runs the checks, keeps the history, and shows a live dashboard with uptime and response-time charts, a JSON API and status badges. It can alert you through Slack, Microsoft Teams, email, PagerDuty, Discord, Telegram, ntfy and many other providers.
The image runs Gatus 5.36.0 compiled by cloudimg from the official 5.36.0 source with Go 1.27.3 and patched golang.org/x/net, golang.org/x/crypto and gRPC modules. Gatus publishes no release binary of its own (only container images), and the 5.36.0 source does not build with the current Go release until gRPC is updated. The build is reproducible, and its record ships with the image. The base is a fully patched Ubuntu 24.04 LTS, and unattended security upgrades stay on for your instance.
What is on the image:
- Gatus 5.36.0 (
gatus.service) running as the unprivilegedgatususer, listening on127.0.0.1:8080only - nginx publishing the dashboard over HTTPS on port 443 with a certificate created on your instance's first boot, behind HTTP Basic Auth with a password unique to your instance; port 80 redirects to HTTPS
- A starter configuration at
/etc/gatus/config.yamlthat monitors this instance only (its HTTPS front end and certificate expiry, Gatus itself, and SSH), so the dashboard is green on first boot. Nothing on the internet is probed until you add it. gatus-set-hostname, a helper to serve the dashboard under your own DNS name- The build record (
BUILD-INFO.txt),go.mod,go.sum, thegovulncheckresults and the Apache 2.0 licence in/usr/share/doc/gatus/
Default user: ubuntu · Architecture: x86_64 · Root volume: 8 GiB gp3
Security posture
- Password on everything. Gatus has no login by default, and its own optional login leaves the badge, uptime and history routes open. So nginx protects every path (the dashboard, the JSON API, the badges and Gatus' health route) with HTTP Basic Auth for the user
admin. The password is generated on each instance's first boot, stored only as a bcrypt hash in/etc/nginx/.gatus.htpasswd, and written in plain text to/root/gatus-credentials.txt(mode 0600, root only). No password or hash ships in the image. - Fails closed. nginx refuses to start until first boot has created the certificate and the password, so the dashboard is never reachable without a login.
- Loopback only. Gatus listens on
127.0.0.1only. nginx on 443 is the only way in. - HTTPS on first boot. nginx serves the dashboard with a TLS key and self-signed certificate created on your instance (TLS 1.2 and 1.3 only). Port 80 only redirects to HTTPS.
- Fixed host name. The dashboard is served under a name taken from the instance metadata at first boot (or one you set), never from the request. Requests for any other host name are dropped, and cross-site form posts and WebSocket connections are refused.
- Real client addresses. nginx passes the connecting address to Gatus and ignores any
X-Forwarded-Forheader a client sends. - No metadata access for the checks. Gatus fetches whatever URLs you configure, so
gatus.serviceandnginx.serviceare blocked from reaching the instance metadata service (169.254.169.254 and fd00:ec2::254). A check pointed at the metadata service fails instead of leaking it. Amazon Time Sync keeps working. - Least privilege.
gatus.serviceruns with no Linux capabilities, a read-only system (ProtectSystem=strict) except/var/lib/gatus, and its own private/tmp. - No surprise traffic. The starter configuration checks only the instance itself; the Prometheus
/metricsendpoint is off. - IMDSv2 required. The AMI is registered with
ImdsSupport v2.0, so instances launched from it refuse token-less (IMDSv1) metadata requests. - Per-instance identity. SSH host keys and the machine ID are generated fresh on every instance.
Known advisory in a bundled library
govulncheck reports one advisory against the binary, GO-2026-5932: the golang.org/x/crypto/openpgp package is deprecated and described by the Go team as unsafe by design, and it has no fixed version. It is compiled in only because the GitHub library behind Gatus' GitHub-issue alerting provider imports it for signing commits, which Gatus never does. Every call path govulncheck finds reaches it only through package initialisation, and the build fails if that ever changes. Every other finding was fixed by the module updates. The full output ships in /usr/share/doc/gatus/govulncheck-binary.txt and govulncheck-source.txt.
Prerequisites
- An AWS account and permission to launch EC2 instances
- An EC2 key pair in the Region you launch in
- A subnet that gives the instance a public IPv4 address (or private connectivity from where you will view the dashboard), with outbound access to the services you want to monitor
Recommended instance type: m5.large. Gatus is a single small Go process with an embedded SQLite database, so a smaller burstable type also works for a modest number of endpoints.
Connecting to your instance
| Version | Operating system | SSH user |
|---|---|---|
| Gatus 5.36.0 on Ubuntu 24.04 | Ubuntu 24.04 LTS | ubuntu |
Step 1: Launch the instance
Option A: AWS Marketplace console
- Open the cloudimg Hardened Uptime Kuma, Gatus & Uptime and Status Pages AMIs listing on AWS Marketplace and choose View purchase options, then Subscribe. One subscription covers every version in the listing.
- Once the subscription is active, choose Launch your software.
- Choose One-click launch from AWS Marketplace (or Launch from EC2 Console for the full launch wizard). In Version, choose Gatus 5.36.0 on Ubuntu 24.04, then choose your Region.
- Pick an instance type (
m5.largerecommended; the software charge is $0.04 per vCPU per hour) and your key pair. - Security group: the listing's default group allows SSH (22), HTTP (80) and HTTPS (443). Restrict SSH to your own IP address, and 80/443 to the people who need the status page.
- Choose your VPC and a subnet that gives the instance a public IP, leave the metadata defaults (the image already requires IMDSv2), then choose Launch.
Option B: AWS CLI
Run this on your own workstation. It creates a security group that allows SSH and the dashboard from your management address only, then launches the instance. Replace the AMI ID with the one shown for your Region on the Marketplace launch page, and the VPC, subnet, key pair and CIDR with your own:
SG=$(aws ec2 create-security-group --group-name gatus \
--description "Gatus" --vpc-id vpc-0123456789abcdef0 \
--query GroupId --output text)
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 22 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 443 --cidr <your-mgmt-cidr>
aws ec2 authorize-security-group-ingress --group-id "$SG" --protocol tcp --port 80 --cidr <your-mgmt-cidr>
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type m5.large \
--key-name my-key-pair \
--security-group-ids "$SG" \
--subnet-id subnet-0123456789abcdef0 \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=gatus}]'
You do not need --metadata-options: the AMI already makes IMDSv2 required.
Step 2: Connect and read your login
Connect as ubuntu with the key pair you chose:
ssh -i ~/.ssh/my-key-pair.pem ubuntu@<public-ip>
First boot finishes within a minute of launch. It creates the HTTPS certificate, generates the password and starts nginx, and then Gatus starts its checks. Your login is in a root-only file:
sudo cat /root/gatus-credentials.txt
# Gatus - generated on first boot by gatus-firstboot.service
# These credentials are unique to this instance. Store them somewhere safe.
GATUS_URL=https://ec2-3-93-242-11.compute-1.amazonaws.com/
GATUS_USERNAME=admin
GATUS_PASSWORD=<GATUS_PASSWORD>
#
# Open GATUS_URL in a browser (self-signed certificate minted on this instance's first boot; port 80
# redirects to https) and sign in with the username and password above. The same credentials protect
# the JSON API (/api/v1/endpoints/statuses) and the status badges.
# Change the name it is served as: sudo gatus-set-hostname <your-dns-name>
#
# The default endpoints monitor this instance only. Add your own services to /etc/gatus/config.yaml
# (Gatus reloads it automatically). Restrict 80/443 in the security group to the people who need the page.
# Full deployment guide: https://www.cloudimg.co.uk/guides/gatus-5-36-0-on-ubuntu-24-04-aws/
GATUS_PASSWORD holds your instance's own password.
Step 3: Confirm the services are running
First boot, Gatus and nginx are active, Gatus runs as the unprivileged gatus user, and the open health endpoint answers:
systemctl is-active gatus-firstboot.service gatus.service nginx.service
ps -o user= -C gatus
test "$(curl -sk https://localhost/healthz)" = ok && echo "healthz ok"
active
active
active
gatus
healthz ok
/healthz is served by nginx without a login, so you can use it for a load balancer health check over HTTPS. Everything else needs the password.
These are the only ports the instance listens on:
sudo ss -Hltnu | awk '{print $1, $5}' | sort -u
tcp 0.0.0.0:22
tcp 0.0.0.0:443
tcp 0.0.0.0:80
tcp 127.0.0.1:8080
tcp 127.0.0.53%lo:53
tcp 127.0.0.54:53
tcp [::]:22
tcp [::]:443
tcp [::]:80
udp 127.0.0.1:323
udp 127.0.0.53%lo:53
udp 127.0.0.54:53
udp 172.31.83.83%ens5:68
udp [::1]:323
Gatus (8080) listens on loopback only; nginx publishes it on 443. The loopback port 53 entries are Ubuntu's local stub resolver (systemd-resolved), 323 is chrony's local control socket and 68 is the DHCP client.
The binary is the reproducible cloudimg build of the 5.36.0 source. Its record ships with the image:
sha256sum /usr/local/bin/gatus
grep -E '^(source|toolchain |binary|govulncheck)' /usr/share/doc/gatus/BUILD-INFO.txt
07676fb59df06f69deb9d1c155c21eff7da6d0bb4e32585ed82d06cc7f982a66 /usr/local/bin/gatus
source https://github.com/TwiN/gatus/archive/refs/tags/v5.36.0.tar.gz
source sha256 b5543af591e602281406049ee2f822a6529a8f14be0cd54df5a31c210520159a (tag commit ed1107b41a30e22047eecfb6dbc3be5e39829d5a)
toolchain go1.27.3 linux/amd64, official tarball https://go.dev/dl/go1.27.3.linux-amd64.tar.gz
toolchain sha256 65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40
binary sha256 07676fb59df06f69deb9d1c155c21eff7da6d0bb4e32585ed82d06cc7f982a66 (reproducible)
govulncheck v1.9.0 -mode=binary and source mode: only GO-2026-5932 (DB updated: 2026-10-21 00:27:29 +0000 UTC)
Step 4: Sign in to the dashboard
Browse to the GATUS_URL from Step 2, for example https://ec2-3-93-242-11.compute-1.amazonaws.com/. The certificate is self-signed, so your browser warns once; accept it, or install your own certificate (Step 8). Without a login nginx answers 401, and your browser asks for a username and password:

Sign in as admin with the password from Step 2. The Gatus health dashboard lists the three starter endpoints, each with its latest result and a bar per check:

Select an endpoint for its detail page: current status, average and range of response times, the recent checks and a response-time chart:

Port 80 only redirects to the HTTPS address:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' http://localhost/
301 https://ec2-3-93-242-11.compute-1.amazonaws.com/
You can check the login from the command line too. These requests go to nginx on the instance itself: the API refuses a request with no password or a wrong one, and the instance's own password (read from the credentials file and passed to curl on standard input, never on the command line) returns the endpoint statuses. The block stops with an error if any answer is not the expected one:
H=$(sudo sed -n 's/^set $gatus_host "\(.*\)";$/\1/p' /etc/nginx/gatus-host.conf)
AUTH() { sudo sed -n 's/^GATUS_PASSWORD=\(.*\)$/user = "admin:\1"/p' /root/gatus-credentials.txt; }
U=$(curl -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H/api/v1/endpoints/statuses")
W=$(printf 'user = "admin:not-the-password"\n' | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H/api/v1/endpoints/statuses")
A=$(AUTH | curl -K - -sk --resolve "$H:443:127.0.0.1" -o /dev/null -w '%{http_code}' "https://$H/api/v1/endpoints/statuses")
echo "unauth : $U"; echo "wrongpw : $W"; echo "authed : $A"
[ "$U" = 401 ] && [ "$W" = 401 ] && [ "$A" = 200 ]
unauth : 401
wrongpw : 401
authed : 200
Step 5: Read the results from the API
The dashboard is built on a JSON API you can use from scripts. This lists every endpoint with how many results Gatus holds and whether the latest check passed, and fails unless all three starter endpoints are healthy:
H=$(sudo sed -n 's/^set $gatus_host "\(.*\)";$/\1/p' /etc/nginx/gatus-host.conf)
AUTH() { sudo sed -n 's/^GATUS_PASSWORD=\(.*\)$/user = "admin:\1"/p' /root/gatus-credentials.txt; }
J=$(AUTH | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/api/v1/endpoints/statuses")
echo "$J" | jq -r '.[] | "\(.key) results=\(.results|length) last_success=\(.results[-1].success)"'
test "$(echo "$J" | jq -r '[.[] | select(.results[-1].success == true) | .name] | sort | join(",")')" = "gatus,nginx-https,ssh"
this-instance_gatus results=8 last_success=true
this-instance_nginx-https results=8 last_success=true
this-instance_ssh results=8 last_success=true
Each endpoint's key is <group>_<name>. The detail of one check shows each condition and its result:
H=$(sudo sed -n 's/^set $gatus_host "\(.*\)";$/\1/p' /etc/nginx/gatus-host.conf)
AUTH() { sudo sed -n 's/^GATUS_PASSWORD=\(.*\)$/user = "admin:\1"/p' /root/gatus-credentials.txt; }
AUTH | curl -K - -sk --resolve "$H:443:127.0.0.1" "https://$H/api/v1/endpoints/this-instance_nginx-https/statuses" \
| jq -e '.results[-1] | {status, success, conditionResults}'
{
"status": 200,
"success": true,
"conditionResults": [
{
"condition": "[STATUS] == 200",
"success": true
},
{
"condition": "[BODY] == pat(ok*)",
"success": true
},
{
"condition": "[CERTIFICATE_EXPIRATION] > 720h",
"success": true
}
]
}
Status badges live under the same API, for example https://<your-host>/api/v1/endpoints/this-instance_ssh/health/badge.svg, and also need the password.
Step 6: Add your own endpoints
The configuration is /etc/gatus/config.yaml, readable only by root and the gatus service account, because alerting tokens you add will live there. The starter file:
sudo stat -c '%a %U:%G %n' /etc/gatus/config.yaml
sudo grep -vE '^\s*#|^$' /etc/gatus/config.yaml
640 root:gatus /etc/gatus/config.yaml
storage:
type: sqlite
path: /var/lib/gatus/data.db
web:
address: 127.0.0.1
port: 8080
metrics: false
ui:
title: "Status | Gatus"
header: "Service Health"
description: "Live health and uptime of the services monitored by this instance."
endpoints:
- name: nginx-https
group: this-instance
url: "https://127.0.0.1/healthz"
interval: 60s
client:
insecure: true
conditions:
- "[STATUS] == 200"
- "[BODY] == pat(ok*)"
- "[CERTIFICATE_EXPIRATION] > 720h"
- name: gatus
group: this-instance
url: "http://127.0.0.1:8080/health"
interval: 60s
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- name: ssh
group: this-instance
url: "tcp://127.0.0.1:22"
interval: 60s
conditions:
- "[CONNECTED] == true"
Edit it with sudoedit (or your editor of choice through sudo) and append your services under endpoints:. A website with a response-time and certificate check, a database port, and a DNS record:
sudoedit /etc/gatus/config.yaml
- name: website
group: production
url: "https://www.example.com/"
interval: 60s
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 800"
- "[CERTIFICATE_EXPIRATION] > 336h"
- name: postgres
group: production
url: "tcp://10.0.1.20:5432"
interval: 30s
conditions:
- "[CONNECTED] == true"
- name: dns-www
group: production
url: "1.1.1.1"
dns:
query-name: "www.example.com"
query-type: "A"
conditions:
- "[DNS_RCODE] == NOERROR"
Gatus checks the file for changes every 30 seconds and reloads it on its own; you do not need to restart it. If an edit does not parse, Gatus logs the error and exits, and systemd keeps restarting it every few seconds, so the dashboard is down until you correct the file; it then comes back by itself. Check the service and the log after each change:
systemctl is-active gatus
sudo journalctl -u gatus -n 20 --no-pager -o cat
To be notified when a check fails, add an alerting: block (for example Slack, email or PagerDuty) and an alerts: list on each endpoint; the Gatus alerting documentation has an example for every provider. Keep the file 0640 root:gatus.
The Gatus log shows each check as it runs:
sudo journalctl -u gatus -n 3 --no-pager -o cat | grep -E 'success=(true|false)'
2026/10/21 13:24:55 [watchdog.executeEndpoint] Monitored group=this-instance; endpoint=nginx-https; key=this-instance_nginx-https; success=true; errors=0; duration=0s
2026/10/21 13:24:55 [watchdog.executeEndpoint] Monitored group=this-instance; endpoint=gatus; key=this-instance_gatus; success=true; errors=0; duration=1ms
2026/10/21 13:24:55 [watchdog.executeEndpoint] Monitored group=this-instance; endpoint=ssh; key=this-instance_ssh; success=true; errors=0; duration=0s
Step 7: Monitoring from a private subnet
Gatus checks run from this instance, so it can watch services that are not on the internet at all: databases, internal APIs and load balancers in the same VPC or in peered VPCs. Make sure the security groups of those services allow traffic from this instance's security group. ICMP (icmp://) checks work too, without giving Gatus any capability: the image allows unprivileged ICMP sockets for the gatus group only (/etc/sysctl.d/60-cloudimg-gatus-ping.conf).
Step 8: HTTPS certificate and host name
The certificate was created on your instance's first boot for the instance's public DNS name, public IP, private IP and localhost. The key, the password hash and the credentials file are readable only by root (nginx's group can read the hash):
sudo openssl x509 -in /etc/ssl/gatus/gatus.crt -noout -subject -dates -ext subjectAltName
sudo stat -c '%a %U:%G %n' /etc/ssl/gatus/gatus.key /etc/ssl/gatus/gatus.crt /etc/nginx/.gatus.htpasswd /root/gatus-credentials.txt
subject=CN = ec2-3-93-242-11.compute-1.amazonaws.com, O = cloudimg
notBefore=Oct 21 13:17:50 2026 GMT
notAfter=Jan 23 13:17:50 2029 GMT
X509v3 Subject Alternative Name:
DNS:ec2-3-93-242-11.compute-1.amazonaws.com, IP Address:3.93.242.11, IP Address:172.31.83.83, IP Address:127.0.0.1, DNS:localhost
600 root:root /etc/ssl/gatus/gatus.key
644 root:root /etc/ssl/gatus/gatus.crt
640 root:www-data /etc/nginx/.gatus.htpasswd
600 root:root /root/gatus-credentials.txt
To serve the dashboard under your own DNS name (for example status.example.com, pointed at this instance), run the helper. It records the name, creates a new self-signed certificate for it, updates nginx and rewrites the URL in the credentials file:
sudo gatus-set-hostname status.example.com
If the instance's public address changes after a stop and start, run the helper again so the certificate names the new address too. To use a certificate from your own certificate authority, replace /etc/ssl/gatus/gatus.key (keep it 0600 root) and /etc/ssl/gatus/gatus.crt, then run sudo systemctl reload nginx.
Step 9: Metadata access is blocked for Gatus
gatus.service and nginx.service carry a systemd IPAddressDeny rule for the instance metadata addresses, so no check can reach the metadata service, even if an endpoint's URL pointed at it. Each entry is a single address, so the Amazon Time Sync Service at 169.254.169.123 keeps working, as chrony shows:
systemctl show -p IPAddressDeny gatus.service nginx.service
chronyc -n sources | grep 169.254
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
IPAddressDeny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128
^* 169.254.169.123 3 4 377 10 -4996ns[-5971ns] +/- 619us
Maintenance
- Password: the per-instance password is in
/root/gatus-credentials.txtand its bcrypt hash in/etc/nginx/.gatus.htpasswd. To change it, runsudo htpasswd -B /etc/nginx/.gatus.htpasswd admin(it prompts for the new password), thensudo systemctl reload nginxand update the credentials file. Add more viewers with the same command and a different user name. - A public status page: the image keeps the whole page behind the password. If you want a page anyone can read, decide which paths to open and add an nginx
locationwithauth_basic off;for them in/etc/nginx/sites-available/cloudimg-gatus, thensudo nginx -t && sudo systemctl reload nginx. Remember that the API and the badges then show your endpoint names and history to anyone. - Keep Gatus on loopback: leave
web.address: 127.0.0.1in the configuration. Gatus has no password of its own, so binding it to a public address would publish your dashboard and API to anyone. - History: results are kept in the SQLite database
/var/lib/gatus/data.db. Back up that file (with Gatus stopped, or withsqlite3 .backup) together with/etc/gatus/config.yaml. - Upgrades: to move to a newer Gatus release, launch the newer version from the listing when it is published and copy your
config.yamlacross. - Security patches: unattended-upgrades remains enabled so the OS keeps receiving security updates.
Security recommendations
- Keep the security group tight. Open 22 only to your management address, and 80/443 only to the people who need the dashboard.
- Keep alerting tokens in the config file only, with its
0640 root:gatuspermissions, and rotate them if the instance is ever shared. - Use your own certificate for the dashboard if other people sign in to it, so browsers trust it without a warning.
Support and licensing
Gatus is developed by TwiN and contributors and distributed under the Apache License 2.0. This image ships a binary that cloudimg compiled from the unmodified 5.36.0 source with Go 1.27.3 and updated Go modules; the build record and licence are in /usr/share/doc/gatus/. cloudimg provides the packaging, the hardening, first-boot HTTPS and per-instance credentials, and 24/7 support.
cloudimg is not affiliated with or endorsed by the Gatus project. Product names are marks of their owners and are used here only to identify the software.
For help with deployment or configuration, email support@cloudimg.co.uk or visit cloudimg support.