
Gatus is a health dashboard that gives you the ability to monitor your services using HTTP, ICMP, TCP, and even DNS queries as well as evaluate the result of said queries by using a list of conditions on values like the status code, the response time, the certificate expiration, the body and many others. The icing on top is that each of these health checks can be paired with alerting via Slack, PagerDuty, Discord and even Twilio.
I personally deploy it in my Kubernetes cluster and let it monitor the status of my core applications: https://status.twinnation.org/
Before getting into the specifics, I want to address the most common question:
Why would I use Gatus when I can just use Prometheus’ Alertmanager, Cloudwatch or even Splunk?
Neither of these can tell you that there’s a problem if there are no clients actively calling the endpoint. In other words, it's because monitoring metrics mostly rely on existing traffic, which effectively means that unless your clients are already experiencing a problem, you won't be notified.
Gatus, on the other hand, allows you to configure health checks for each of your features, which in turn allows it to monitor these features and potentially alert you before any clients are impacted.
A sign you may want to look into Gatus is by simply asking yourself whether you'd receive an alert if your load balancer was to go down right now. Will any of your existing alerts by triggered? Your metrics won’t report an increase in errors if there’s no traffic that makes it to your applications. This puts you in a situation where your clients are the ones that will notify you about the degradation of your services rather than you reassuring them that you're working on fixing the issue before they even know about it.

The main features of Gatus are:
By default, the configuration file is expected to be at config/config.yaml.
You can specify a custom path by setting the GATUS_CONFIG_FILE environment variable.
Here's a simple example:
metrics: true # Whether to expose metrics at /metrics
services:
- name: twinnation # Name of your service, can be anything
url: "https://twinnation.org/health"
interval: 30s # Duration to wait between every status check (default: 60s)
conditions:
- "[STATUS] == 200" # Status must be 200
- "[BODY].status == UP" # The json path "$.status" must be equal to UP
- "[RESPONSE_TIME] < 300" # Response time must be under 300ms
- name: example
url: "https://example.org/"
interval: 5m
conditions:
- "[STATUS] == 200"
This example would look like this:

Note that you can also use environment variables in the configuration file (e.g. $DOMAIN, ${DOMAIN})
If you want to test it locally, see Docker.
| Parameter | Description | Default |
|---|---|---|
debug | Whether to enable debug logs | false |
metrics | Whether to expose metrics at /metrics | false |
storage | Storage configuration | {} |
storage.file | File to persist the data in. If not set, storage is in-memory only. | "" |
services | List of services to monitor | Required [] |
services[].name | Name of the service. Can be anything. | Required "" |
services[].group | Group name. Used to group multiple services together on the dashboard. See Service groups. | "" |
services[].url | URL to send the request to | Required "" |
services[].method | Request method | GET |
services[].insecure | Whether to skip verifying the server's certificate chain and host name | false |
services[].conditions | Conditions used to determine the health of the service. See Conditions. | [] |
services[].interval | Duration to wait between every status check | 60s |
services[].graphql | Whether to wrap the body in a query param ({"query":"$body"}) | false |
services[].body | Request body | "" |
services[].headers | Request headers | {} |
services[].dns | Configuration for a service of type DNS. See Monitoring a service using DNS queries. | "" |
services[].dns.query-type | Query type for DNS service | "" |
services[].dns.query-name | Query name for DNS service | "" |
services[].alerts[].type | Type of alert. Valid types: slack, discord, pagerduty, twilio, mattermost, messagebird, custom | Required "" |
services[].alerts[].enabled | Whether to enable the alert | false |
services[].alerts[].failure-threshold | Number of failures in a row needed before triggering the alert | 3 |
services[].alerts[].success-threshold | Number of successes in a row before an ongoing incident is marked as resolved | 2 |
services[].alerts[].send-on-resolved | Whether to send a notification once a triggered alert is marked as resolved | false |
services[].alerts[].description | Description of the alert. Will be included in the alert sent | "" |
alerting | Configuration for alerting. See Alerting. | {} |
security | Security configuration | {} |
security.basic | Basic authentication security configuration | {} |
security.basic.username | Username for Basic authentication | Required "" |
security.basic.password-sha512 | Password's SHA512 hash for Basic authentication | Required "" |
disable-monitoring-lock | Whether to disable the monitoring lock | false |
skip-invalid-config-update | Whether to ignore invalid configuration update. See Reloading configuration on the fly. | |
web | Web configuration | {} |
web.address | Address to listen on | 0.0.0.0 |
web.port | Port to listen on | 8080 |
Here are some examples of conditions you can use:
| Condition | Description | Passing values | Failing values |
|---|---|---|---|
[STATUS] == 200 | Status must be equal to 200 | 200 | 201, 404, ... |
[STATUS] < 300 | Status must lower than 300 | 200, 201, 299 | 301, 302, ... |
[STATUS] <= 299 | Status must be less than or equal to 299 | 200, 201, 299 | 301, 302, ... |
[STATUS] > 400 | Status must be greater than 400 | 401, 402, 403, 404 | 400, 200, ... |
[STATUS] == any(200, 429) | Status must be either 200 or 429 | 200, 429 | 201, 400, ... |
[CONNECTED] == true | Connection to host must've been successful | true, false | |
[RESPONSE_TIME] < 500 | Response time must be below 500ms | 100ms, 200ms, 300ms | 500ms, 501ms |
[IP] == 127.0.0.1 | Target IP must be 127.0.0.1 | 127.0.0.1 | 0.0.0.0 |
[BODY] == 1 | The body must be equal to 1 | 1 | {}, 2, ... |
[BODY].user.name == john | JSONPath value of $.user.name is equal to john | {"user":{"name":"john"}} | |
[BODY].data[0].id == 1 | JSONPath value of $.data[0].id is equal to 1 | {"data":[{"id":1}]} | |
[BODY].age == [BODY].id | JSONPath value of $.age is equal JSONPath $.id | {"age":1,"id":1} | |
len([BODY].data) < 5 | Array at JSONPath $.data has less than 5 elements | {"data":[{"id":1}]} | |
len([BODY].name) == 8 | String at JSONPath $.name has a length of 8 | {"name":"john.doe"} | {"name":"bob"} |
has([BODY].errors) == false | JSONPath $.errors does not exist | {"name":"john.doe"} | {"errors":[]} |
has([BODY].users) == true | JSONPath $.users exists | {"users":[]} | {} |
[BODY].name == pat(john*) | String at JSONPath $.name matches pattern john* | {"name":"john.doe"} | {"name":"bob"} |
[BODY].id == any(1, 2) | Value at JSONPath $.id is equal to 1 or 2 | 1, 2 | 3, 4, 5 |
[CERTIFICATE_EXPIRATION] > 48h | Certificate expiration is more than 48h away | 49h, 50h, 123h | 1h, 24h, ... |
| Placeholder | Description | Example of resolved value |
|---|---|---|
[STATUS] | Resolves into the HTTP status of the request | 404 |
[RESPONSE_TIME] | Resolves into the response time the request took, in ms | 10 |
[IP] | Resolves into the IP of the target host | 192.168.0.232 |
[BODY] | Resolves into the response body. Supports JSONPath. | {"name":"john.doe"} |
[CONNECTED] | Resolves into whether a connection could be established | true |
[CERTIFICATE_EXPIRATION] | Resolves into the duration before certificate expiration | 24h, 48h, 0 (if not using HTTPS) |
[DNS_RCODE] | Resolves into the DNS status of the response | NOERROR |
| Function | Description | Example |
|---|---|---|
len | Returns the length of the object/slice. Works only with the [BODY] placeholder. | len([BODY].username) > 8 |
has | Returns true or false based on whether a given path is valid. Works only with the [BODY] placeholder. | has([BODY].errors) == false |
pat | Specifies that the string passed as parameter should be evaluated as a pattern. Works only with == and !=. | [IP] == pat(192.168.*) |
any | Specifies that any one of the values passed as parameters is a valid value. Works only with == and !=. | [BODY].ip == any(127.0.0.1, ::1) |
NOTE: Use pat only when you need to. [STATUS] == pat(2*) is a lot more expensive than [STATUS] < 300.
Gatus supports multiple alerting providers, such as Slack and PagerDuty, and supports different alerts for each individual services with configurable descriptions and thresholds.
Note that if an alerting provider is not configured properly, all alerts configured with the provider's type will be ignored.
| Parameter | Description | Default |
|---|---|---|
alerting.slack | Configuration for alerts of type slack | {} |
alerting.slack.webhook-url | Slack Webhook URL | Required "" |
alerting.discord | Configuration for alerts of type discord | {} |
alerting.discord.webhook-url | Discord Webhook URL | Required "" |
alerting.pagerduty | Configuration for alerts of type pagerduty | {} |
alerting.pagerduty.integration-key | PagerDuty Events API v2 integration key. | Required "" |
alerting.twilio | Settings for alerts of type twilio | {} |
alerting.twilio.sid | Twilio account SID | Required "" |
alerting.twilio.token | Twilio auth token | Required "" |
alerting.twilio.from | Number to send Twilio alerts from | Required "" |
alerting.twilio.to | Number to send twilio alerts to | Required "" |
alerting.mattermost | Configuration for alerts of type mattermost | {} |
alerting.mattermost.webhook-url | Mattermost Webhook URL | Required "" |
alerting.mattermost.insecure | Whether to skip verifying the server's certificate chain and host name | false |
alerting.messagebird | Settings for alerts of type messagebird | {} |
alerting.messagebird.access-key | Messagebird access key | Required "" |
alerting.messagebird.originator | The sender of the message | Required "" |
alerting.messagebird.recipients | The recipients of the message | Required "" |
alerting.telegram | Configuration for alerts of type telegram | {} |
alerting.telegram.token | Telegram Bot Token | Required "" |
alerting.telegram.id | Telegram User ID | Required "" |
alerting.custom | Configuration for custom actions on failure or alerts | {} |
alerting.custom.url | Custom alerting request url | Required "" |
alerting.custom.method | Request method | GET |
alerting.custom.insecure | Whether to skip verifying the server's certificate chain and host name | false |
alerting.custom.body | Custom alerting request body. | "" |
alerting.custom.headers | Custom alerting request headers | {} |
alerting.*.default-alert.enabled | Whether to enable the alert | N/A |
alerting.*.default-alert.failure-threshold | Number of failures in a row needed before triggering the alert | N/A |
alerting.*.default-alert.success-threshold | Number of successes in a row before an ongoing incident is marked as resolved | N/A |
alerting.*.default-alert.send-on-resolved | Whether to send a notification once a triggered alert is marked as resolved | N/A |
alerting.*.default-alert.description | Description of the alert. Will be included in the alert sent | N/A |
alerting:
slack:
webhook-url: "https://hooks.slack.com/services/**********/**********/**********"
services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
alerts:
- type: slack
enabled: true
description: "healthcheck failed 3 times in a row"
send-on-resolved: true
- type: slack
enabled: true
failure-threshold: 5
description: "healthcheck failed 5 times in a row"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
Here's an example of what the notifications look like:

alerting:
discord:
webhook-url: "https://discord.com/api/webhooks/**********/**********"
services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
alerts:
- type: discord
enabled: true
description: "healthcheck failed"
send-on-resolved: true
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 300"
It is highly recommended to set services[].alerts[].send-on-resolved to true for alerts
of type pagerduty, because unlike other alerts, the operation resulting from setting said
parameter to true will not create another incident, but mark the incident as resolved on
PagerDuty instead.
alerting:
pagerduty:
integration-key: "********************************"
services:
- name: twinnation
url: "https://twinnation.org/health"
interval: 30s
alerts:
- type: pagerduty
enabled: true
failure-threshold: 3
Content type
Image
Digest
Size
16.5 MB
Last updated
about 5 years ago
docker pull gitopsorg/docker-devops-http-monitor:1.0