Gatus Health Dashboard Installation
Gatus is a lightweight, developer-oriented health monitoring dashboard written in Go that continuously checks endpoints and generates a public status page with configurable alerting via Slack, PagerDuty, email, and more. This guide covers installing Gatus on Linux and Docker, configuring endpoint checks, defining conditions, setting up alerts, and generating a status page.
Prerequisites
- Ubuntu 20.04+ or CentOS 8+ / Rocky Linux 8+
- Docker (recommended) or Go 1.21+
- 256 MB RAM (Gatus is very lightweight)
- Outbound internet access for alert notifications
Installing Gatus with Docker
Docker is the simplest deployment method:
# Create configuration directory
mkdir -p /opt/gatus
# Create a basic configuration file
cat > /opt/gatus/config.yaml << 'EOF'
web:
port: 8080
endpoints:
- name: Website
url: https://example.com
interval: 30s
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 300"
EOF
# Run Gatus
docker run -d \
--name gatus \
--restart unless-stopped \
-p 8080:8080 \
-v /opt/gatus/config.yaml:/config/config.yaml \
twinproduction/gatus:latest
# Check it's running
curl http://localhost:8080/health
# Access the dashboard at http://your-server:8080
With Docker Compose:
# /opt/gatus/docker-compose.yml
version: "3.9"
services:
gatus:
image: twinproduction/gatus:latest
container_name: gatus
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./config.yaml:/config/config.yaml:ro
environment:
- SLACK_WEBHOOK_URL=${SLACK_WEBHOOK_URL} # Pass secrets via env
cd /opt/gatus && docker compose up -d
Installing Gatus as a Binary
# Download the latest release
GATUS_VERSION="5.11.0"
wget https://github.com/TwiN/gatus/releases/download/v${GATUS_VERSION}/gatus_${GATUS_VERSION}_linux_amd64.tar.gz
tar xzf gatus_${GATUS_VERSION}_linux_amd64.tar.gz
sudo mv gatus /usr/local/bin/
gatus --version
# Create directories and user
sudo useradd -r -s /sbin/nologin gatus
sudo mkdir -p /etc/gatus /var/lib/gatus
sudo chown -R gatus:gatus /etc/gatus /var/lib/gatus
# Create systemd service
sudo cat > /etc/systemd/system/gatus.service << 'EOF'
[Unit]
Description=Gatus Health Dashboard
After=network.target
[Service]
User=gatus
Group=gatus
Environment=GATUS_CONFIG_PATH=/etc/gatus/config.yaml
ExecStart=/usr/local/bin/gatus
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now gatus
Configuring Endpoint Checks
The configuration file supports HTTP, TCP, DNS, ICMP, and WebSocket checks:
# /etc/gatus/config.yaml
web:
port: 8080
# Uncomment for basic auth on the status page
# security:
# basic:
# username: admin
# password: password
storage:
type: sqlite # Store check history in SQLite
path: /var/lib/gatus/data.db
# Default settings applied to all endpoints (can be overridden per endpoint)
default-endpoint-group: *default-group
endpoints:
# HTTP check
- name: API Gateway
group: production
url: https://api.example.com/health
method: GET
interval: 30s
conditions:
- "[STATUS] == 200"
- "[BODY] == {\"status\":\"ok\"}"
- "[RESPONSE_TIME] < 500"
headers:
Authorization: Bearer ${API_TOKEN}
alerts:
- type: slack
failure-threshold: 2 # Alert after 2 consecutive failures
success-threshold: 1 # Resolve after 1 success
send-on-resolved: true
# HTTPS certificate check
- name: API TLS Certificate
group: production
url: https://api.example.com
interval: 6h
conditions:
- "[STATUS] == 200"
- "[CERTIFICATE_EXPIRATION] > 720h" # Alert if cert expires within 30 days
# TCP port check
- name: PostgreSQL
group: databases
url: tcp://db.example.com:5432
interval: 30s
conditions:
- "[CONNECTED] == true"
- "[RESPONSE_TIME] < 100"
# DNS resolution check
- name: DNS Resolution
group: infrastructure
url: dns://1.1.1.1
interval: 5m
dns:
query-name: example.com
query-type: A
conditions:
- "[DNS_RCODE] == NOERROR"
- "len([BODY]) > 0"
# ICMP ping
- name: Database Server Ping
group: infrastructure
url: icmp://db.example.com
interval: 30s
conditions:
- "[CONNECTED] == true"
- "[RESPONSE_TIME] < 50"
Condition Definitions
Gatus evaluates conditions as simple expressions. Available placeholders:
| Placeholder | Description | Example |
|---|---|---|
[STATUS] | HTTP status code | [STATUS] == 200 |
[BODY] | Response body | [BODY].user.active == true |
[RESPONSE_TIME] | Response time (ms) | [RESPONSE_TIME] < 500 |
[CERTIFICATE_EXPIRATION] | TLS cert expiry | [CERTIFICATE_EXPIRATION] > 720h |
[CONNECTED] | TCP/ICMP connected | [CONNECTED] == true |
[DNS_RCODE] | DNS response code | [DNS_RCODE] == NOERROR |
JSON body expressions use dot notation:
conditions:
# Check a specific JSON field value
- "[BODY].status == healthy"
# Check array length
- "len([BODY].servers) > 0"
# Numeric comparison on a JSON field
- "[BODY].metrics.error_rate < 0.01"
# Pattern matching
- "[BODY] pat ^.*version.*$"
Operators: ==, !=, >, >=, <, <=, pat (regex), !pat.
Alerting Configuration
Configure alert channels in the top-level alerting section:
# /etc/gatus/config.yaml
alerting:
slack:
webhook-url: "${SLACK_WEBHOOK_URL}"
default-alert:
failure-threshold: 3
success-threshold: 1
send-on-resolved: true
pagerduty:
integration-key: "${PAGERDUTY_INTEGRATION_KEY}"
default-alert:
failure-threshold: 2
success-threshold: 1
send-on-resolved: true
email:
from: [email protected]
username: [email protected]
password: "${EMAIL_PASSWORD}"
host: smtp.gmail.com
port: 587
to: [email protected]
default-alert:
failure-threshold: 3
success-threshold: 1
send-on-resolved: true
telegram:
token: "${TELEGRAM_BOT_TOKEN}"
id: "-1001234567890" # Chat ID (negative for groups)
default-alert:
failure-threshold: 2
send-on-resolved: true
custom: # Generic webhook
url: https://your-webhook.example.com/alert
method: POST
body: |
{
"status": "[ALERT_TRIGGERED_OR_RESOLVED]",
"endpoint": "[ENDPOINT_NAME]",
"url": "[ENDPOINT_URL]",
"description": "[ALERT_DESCRIPTION]",
"conditions": "[ENDPOINT_CONDITIONS]"
}
headers:
Authorization: Bearer ${WEBHOOK_TOKEN}
Content-Type: application/json
default-alert:
failure-threshold: 2
send-on-resolved: true
Set environment variables for secrets:
# In systemd service (or docker-compose env file):
sudo systemctl edit gatus
# Add under [Service]:
# Environment=SLACK_WEBHOOK_URL=https://hooks.slack.com/...
# Environment=PAGERDUTY_INTEGRATION_KEY=your-key
External Endpoints and Connectivity Checks
Monitor third-party services your application depends on:
endpoints:
# Check that your payment provider is up
- name: Stripe API
group: external
url: https://api.stripe.com/v1/
interval: 1m
conditions:
- "[STATUS] != 0" # Any response = up (Stripe returns 401 for unauthenticated)
# Check your CDN
- name: CDN - Static Assets
group: external
url: https://cdn.example.com/images/health-check.png
interval: 5m
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 1000"
# WebSocket check
- name: Real-time Service
group: production
url: wss://realtime.example.com/ws
interval: 30s
conditions:
- "[CONNECTED] == true"
# Check your backup database
- name: Read Replica
group: databases
url: tcp://read-replica.example.com:5432
interval: 1m
conditions:
- "[CONNECTED] == true"
- "[RESPONSE_TIME] < 100"
Status Page and UI
Gatus generates a public status page at http://your-server:8080:
- Shows all endpoints grouped by their
groupfield - Displays uptime badges (last 7 days)
- Shows response time graphs
- Lists recent incidents
Configure a custom header message and logo:
web:
port: 8080
ui:
title: "Acme Inc. Status Page"
description: "Current operational status of all Acme services"
logo: "https://example.com/logo.png"
link: "https://example.com"
buttons:
- name: "Subscribe to updates"
link: "https://status.example.com/subscribe"
Place Gatus behind Nginx with TLS for a public-facing status page:
server {
listen 443 ssl;
server_name status.example.com;
ssl_certificate /etc/letsencrypt/live/status.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/status.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Troubleshooting
Gatus fails to start:
sudo journalctl -u gatus -f
# Common: invalid YAML syntax, missing required fields
Validate configuration syntax:
# Gatus will print errors on startup if config is invalid
gatus --config /etc/gatus/config.yaml
# Look for "error" messages in output
Alerts not firing:
# Check Gatus logs for alert delivery errors
docker logs gatus | grep -i "alert\|error"
# Verify webhook URL is reachable from the Gatus container
# Test Slack webhook directly:
curl -X POST -H 'Content-type: application/json' \
--data '{"text":"Test from Gatus"}' \
"${SLACK_WEBHOOK_URL}"
Endpoint always showing as failed:
# Test the endpoint manually
curl -v https://api.example.com/health
# Check if the condition is correct - use exact JSON body format
High failure count despite endpoint working:
- Increase
failure-thresholdto avoid noise from transient failures - Check if the response time condition
[RESPONSE_TIME] < Xis too tight - Verify network connectivity from the Gatus host to the endpoint
Conclusion
Gatus provides a clean, self-hosted status page and uptime monitoring solution with minimal infrastructure requirements. Its declarative YAML configuration covers HTTP, TCP, DNS, ICMP, and WebSocket checks, while the flexible condition system handles everything from simple status code checks to JSON body assertions and TLS certificate expiration monitoring. With Slack, PagerDuty, and email alerting built in, Gatus delivers production-ready monitoring that's appropriate for teams of any size.


