Installing Gatus on a VPS: website monitoring, SSL alerts, and Telegram notifications
TL;DR
Gatus is a lightweight self-hosted monitoring service that regularly checks the availability of websites, APIs, TCP ports, and SSL certificate expiration dates. In this guide, you will deploy Gatus via Docker Compose on a VPS, place it behind a Caddy HTTPS proxy, and configure Telegram notifications for service failures and recoveries.
- For basic monitoring of 20–100 endpoints, 1 vCPU, 1 GB RAM, and 10 GB SSD are sufficient.
- Gatus will run in Docker Compose with SQLite storage and automatic restart.
- Endpoint configuration is stored in YAML, while the Telegram token is kept in a separate
.envfile. - Caddy will automatically issue and renew a Let’s Encrypt TLS certificate for the Gatus dashboard.
- HTTP checks, API checks, SSL alerts, a healthcheck, and backups will be configured.
- The end includes troubleshooting for common issues: Telegram, TLS, Docker, firewall, and an unavailable dashboard.
What we are configuring and why
Gatus is an open-source availability monitoring tool written in Go. It is suitable for a VPS owner who wants to see the status of their own websites, REST APIs, VPN dashboards, game servers, Git services, mail, or external dependencies without sharing an infrastructure inventory with third-party SaaS monitoring services.
The service performs checks on a schedule. For example, it can send an HTTP request to https://example.com/health, verify that the server returned code 200, check a JSON response field, measure latency, and warn you if an SSL certificate expires in 21 days. For TCP services, Gatus can check whether a port is open, and for ICMP, whether a host is reachable via ping.
As a result, you will have a web status dashboard at an address such as https://status.example.com, a history of checks, response times, availability percentage, and Telegram notifications. Notifications are sent not for a single random timeout, but after a specified number of consecutive failures. Once a service recovers, Gatus will also send a separate message.
What exactly will be deployed
- Ubuntu Server 24.04 LTS or 26.04 LTS on a VPS.
- Docker Engine 27+ or a newer stable version.
- Docker Compose Plugin v2.
- A Gatus container from the official
twinproduction/gatusimage. - A Caddy 2.9+ container for reverse proxying and automatic HTTPS.
- A Gatus SQLite database in a Docker volume.
- Endpoint configuration in YAML and secrets in the
.envfile. - Configuration and SQLite database backups via restic.
What checks Gatus supports
| Check type | Example use case | Success condition |
|---|---|---|
| HTTP/HTTPS | Website homepage or API endpoint | Response code 200, required header, or text in the body |
| JSON API | /health, payment webhook, SaaS backend |
JSONPath value matches the expected value |
| TCP | SSH, PostgreSQL, Redis, Minecraft, SMTP | Port accepts a connection |
| ICMP | Checking availability of a remote host | Host responds to ping |
| SSL/TLS | Website with a Let’s Encrypt or commercial certificate | Certificate is valid and does not expire before the threshold |
Self-hosted Gatus or cloud monitoring
Cloud monitoring services are convenient if you need multiple geographically distributed monitoring locations, SLA reports for clients, and minimal administration. Their downside is a monthly fee, a limited number of checks on low-cost plans, and sharing data about your domains, endpoints, and incidents with an external provider.
Self-hosted Gatus on a VPS is better suited for personal infrastructure, a small SaaS, a development team, or a set of internal services. It does not require a separate database at the start, consumes few resources, and keeps history under your control. It is important to understand the limitation: if Gatus is hosted in the same data center as the monitored website, it will not detect a complete network outage at that location. For critical services, it is useful to maintain a second instance in another location.
What VPS configuration is needed for this task
Gatus does not require a powerful server. The load depends on the number of endpoints, check interval, number of TCP connections, and history retention period. For most personal and small commercial projects, the limiting factor will not be CPU, but sensible SQLite database storage and a stable network.
| Scenario | CPU | RAM | Disk | Network |
|---|---|---|---|---|
| Up to 30 endpoints, 1–5 minute interval | 1 vCPU | 1 GB | 10 GB SSD | 100 Mbps |
| 30–200 endpoints, API checks and history | 1–2 vCPU | 2 GB | 20–30 GB NVMe | 100–300 Mbps |
| 200–1000 endpoints, multiple groups | 2–4 vCPU | 4 GB | 50 GB NVMe | 1 Gbps |
A practical starting option is 1 vCPU, 2 GB RAM, 20 GB NVMe, and a public IPv4 address. This capacity is sufficient for Gatus, Caddy, Docker, fail2ban, configuration backups, and several dozen checks every minute. As one neutral option, you can choose a VPS with the specified characteristics, but prioritize location proximity, network quality, and the availability of regular snapshots.
When you need a dedicated server instead of a VPS
A dedicated server is almost never necessary for Gatus alone. Dedicated hardware makes sense if monitoring is only part of a large observability platform: Prometheus, Grafana, Loki, Uptime Kuma, a CI system, and hundreds of containers run alongside it. It is also useful when isolation is required, log volumes are high, or there are thousands of checks at short intervals.
If you simply monitor 10–200 domains and services, a VPS is simpler, cheaper, and scales faster. As load grows, moving Gatus to a larger VPS usually comes down to restoring configuration files and the SQLite volume from a backup.
How to choose a location
Location affects check latency and which network problems you will see. If your website users are in Europe, it makes sense to place monitoring in a European data center. If the infrastructure is located in one country and you need to see it “from the outside,” choose an independent facility and network.
Do not install Gatus on the same host it monitors: if the server fails, monitoring disappears as well. The minimum sensible setup is a separate VPS. For important projects, use two independent instances in different countries or with different providers and send notifications to different Telegram chats.
Server preparation
The following assumes a clean Ubuntu 24.04 LTS or Ubuntu 26.04 LTS server with a public IPv4 address, the domain status.example.com, and an A DNS record pointing to the VPS IP. Before issuing the certificate, make sure DNS has already propagated: Caddy must be externally accessible on ports 80 and 443.
Update the system
Connect to the server as the user provided during provisioning or as root. First, install security updates and basic utilities.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ca-certificates gnupg ufw fail2ban nano jq unzip
sudo reboot
The commands update packages, install the required utilities, firewall, and brute-force protection, then reboot the server.
Create a separate administrator account
Do not work permanently as root. Create a user, add it to the sudo group, and add an SSH public key in advance. In this example, the username is deploy.
sudo adduser deploy
sudo usermod -aG sudo deploy
sudo install -d -m 700 -o deploy -g deploy /home/deploy/.ssh
sudo nano /home/deploy/.ssh/authorized_keys
sudo chmod 600 /home/deploy/.ssh/authorized_keys
sudo chown deploy:deploy /home/deploy/.ssh/authorized_keys
Insert one line containing your public key into the authorized_keys file, for example, the contents of ~/.ssh/id_ed25519.pub on your local computer. Before closing the current SSH session, be sure to test login in a new terminal.
ssh deploy@SERVER_IP
This command verifies that key-based login works for the new user.
Disable password-based SSH login
After verifying the key, disable password authentication and root login. This significantly reduces the likelihood of server compromise through automated brute-force attacks.
sudo nano /etc/ssh/sshd_config.d/99-hardening.conf
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
X11Forwarding no
MaxAuthTries 3
sudo sshd -t && sudo systemctl restart ssh
The command checks the SSH configuration syntax and restarts SSH only if there are no errors.
Configure UFW and fail2ban
The Gatus dashboard must be available over HTTPS, while HTTP is required by Caddy for initial domain validation and redirection to HTTPS. Keep SSH open only after confirming that you can reconnect.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
The commands enable the firewall and leave only SSH, HTTP, and HTTPS open. There is no need to expose Gatus port 8080 externally: it will be accessible only to the Caddy container on the internal Docker network.
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
This enables fail2ban at system boot and displays the SSH protection status. If you restrict server access to fixed IPs, additionally create UFW rules that allow SSH only from your network.
Software Installation — Step by Step
For deployment, we use Docker Engine and the Compose Plugin from the official Docker repository. As of 2026, it is recommended to use the current stable Docker Engine 27+ branch or a newer available version, rather than the old docker.io package from the standard Ubuntu repository.
Add the Official Docker Repository
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
This block creates a directory for APT keys and adds the signing key for the official Docker repository.
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
The command adds the repository matching the Ubuntu version and server architecture, then updates the package index.
Install Docker Engine and Compose
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker deploy
This block installs Docker, enables it to start automatically, and allows the deploy user to run Docker without sudo. After completing it, exit the SSH session and reconnect to refresh group membership.
exit
ssh deploy@SERVER_IP
docker version
docker compose version
Verify that Docker Engine and the Compose Plugin are available. If the docker ps command returns a socket access error, reconnect once more or run newgrp docker.
Create the Project Structure
All Gatus files will be stored in /opt/gatus. This directory is convenient for backups, migration, and verification through a configuration management system without secrets.
sudo mkdir -p /opt/gatus/{config,caddy,data,backups}
sudo chown -R deploy:deploy /opt/gatus
cd /opt/gatus
umask 077
touch .env
chmod 600 .env
The commands create working directories, assign ownership to the deploy user, and create a protected file for Telegram secrets.
Create a Telegram Bot and Find the Chat ID
Open the official @BotFather bot in Telegram, run the /newbot command, and set a name and username. BotFather will return a token in the format 123456789:AA.... Do not publish it in Git, tickets, screenshots, or configuration accessible to other users.
Create a private chat with the bot and send it any message, such as /start. For a group chat, add the bot to the group and send a message in that group. Retrieve the chat identifier on the server by substituting the token:
curl -s "https://api.telegram.org/botВАШ_ТОКЕН/getUpdates" | jq
In the JSON, find the message.chat.id field. For Telegram groups, the chat ID often starts with a minus sign, for example -1001234567890. Save the token and identifier in /opt/gatus/.env.
cd /opt/gatus
nano .env
TELEGRAM_TOKEN=123456789:REPLACE_WITH_REAL_TOKEN
TELEGRAM_CHAT_ID=-1001234567890
GATUS_DOMAIN=status.example.com
[email protected]
This file contains secrets and environment parameters. It must not be readable by other users and must not be committed to a public repository.
Gatus, Telegram, and HTTPS Configuration
In this setup, Gatus listens on port 8080 only within the Docker network. Caddy accepts external requests on ports 80 and 443, automatically obtains a TLS certificate, and forwards traffic to Gatus. This prevents the service port from being exposed and eliminates manual Let’s Encrypt maintenance.
Create the Gatus Configuration Template
Gatus uses YAML. We store the config.yaml.template template, and Compose substitutes Telegram variables through envsubst at startup. This prevents the token from ending up in a persistent YAML file that could be committed accidentally.
cd /opt/gatus
nano config/config.yaml.template
storage:
type: sqlite
path: /data/gatus.db
caching: true
ui:
title: "Infrastructure Status"
description: "Availability and SSL monitoring"
default-sort-by: group
metrics: true
alerting:
telegram:
token: "${TELEGRAM_TOKEN}"
id: "${TELEGRAM_CHAT_ID}"
endpoints:
- name: Main website
group: Public sites
url: "https://example.com/"
interval: 1m
timeout: 10s
conditions:
- "[STATUS] == 200"
- "[RESPONSE_TIME] < 3000"
alerts:
- type: telegram
failure-threshold: 3
success-threshold: 2
send-on-resolved: true
description: "The main website is unavailable or too slow."
- name: API healthcheck
group: Public sites
url: "https://api.example.com/health"
interval: 1m
timeout: 10s
headers:
Accept: "application/json"
conditions:
- "[STATUS] == 200"
- "[BODY].status == UP"
- "[RESPONSE_TIME] < 2000"
alerts:
- type: telegram
failure-threshold: 2
success-threshold: 2
send-on-resolved: true
description: "API healthcheck did not return status UP."
- name: SSH server
group: Infrastructure
url: "tcp://203.0.113.10:22"
interval: 2m
timeout: 5s
conditions:
- "[CONNECTED] == true"
alerts:
- type: telegram
failure-threshold: 3
success-threshold: 1
send-on-resolved: true
description: "SSH port is unreachable."
- name: External HTTPS certificate
group: SSL certificates
url: "https://example.com/"
interval: 12h
conditions:
- "[STATUS] == 200"
- "[CERTIFICATE_EXPIRATION] > 336h"
alerts:
- type: telegram
failure-threshold: 1
success-threshold: 1
send-on-resolved: true
description: "SSL certificate expires in less than 14 days."
Replace example.com, api.example.com, and the SSH IP address with actual values. The [CERTIFICATE_EXPIRATION] > 336h condition means that more than 14 days must remain before the certificate expires. For critical certificates, you can set 720 hours, or 30 days.
Do not include tokens, passwords, or private query parameters in URLs. If the API requires authorization, use a separate technical token with minimal permissions and pass it through environment variables or a protected template. For sensitive internal endpoints, it is better to restrict access to the Gatus dashboard through Caddy Basic Auth, a VPN, or a firewall.
Create the Caddy Configuration
cd /opt/gatus
nano caddy/Caddyfile
{
email {$ACME_EMAIL}
}
{$GATUS_DOMAIN} {
encode zstd gzip
reverse_proxy gatus:8080
header {
-Server
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "strict-origin-when-cross-origin"
}
}
Caddy will request a certificate automatically. For this, the domain's A record must point to the VPS, and ports 80 and 443 must be accessible externally. If the domain uses CDN proxying, ensure that the TLS mode does not interfere with ACME validation.
Create the Docker Compose File
cd /opt/gatus
nano compose.yaml
services:
gatus:
image: twinproduction/gatus:latest
container_name: gatus
restart: unless-stopped
env_file:
- .env
entrypoint:
- /bin/sh
- -ec
- |
envsubst < /config/config.yaml.template > /tmp/config.yaml
exec /gatus --config-file=/tmp/config.yaml
volumes:
- ./config:/config:ro
- ./data:/data
networks:
- monitoring
healthcheck:
test: ["CMD", "/gatus", "--config-file=/tmp/config.yaml", "--version"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
caddy:
image: caddy:2-alpine
container_name: caddy-gatus
restart: unless-stopped
env_file:
- .env
depends_on:
gatus:
condition: service_started
ports:
- "80:80"
- "443:443"
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- caddy_data:/data
- caddy_config:/config
networks:
- monitoring
networks:
monitoring:
name: monitoring
volumes:
caddy_data:
caddy_config:
Using the latest tag is convenient for the initial launch, but for predictable production updates it is better to pin a tested Gatus version, such as twinproduction/gatus:v5.x.y, after checking the current release. Similarly, you can pin a minor Caddy version. After updating a version, always check configuration format changes in the release notes.
Validate YAML and Start the Containers
cd /opt/gatus
docker compose config
docker compose pull
docker compose up -d
docker compose ps
The first command combines the Compose file and variables while validating syntax. Docker then downloads the images, starts the stack in the background, and displays container status. Both containers must have the Up status.
docker compose logs --tail=100 gatus
docker compose logs --tail=100 caddy
The Gatus logs must not contain YAML errors, and the Caddy logs will show a successful certificate issuance message. If the certificate is not issued, first check DNS, UFW ports, and whether another web server is using port 80 or 443.
Verify the Dashboard and Endpoint
curl -I http://127.0.0.1
curl -I https://status.example.com
curl -s https://status.example.com | head
The first request may return an error because Caddy expects the correct Host header. The main check is the second request: expect status 200 or a redirect from HTTP to HTTPS. Open the domain in a browser and ensure that the interface displays groups, endpoints, check history, and current status.
Test the Telegram Notification
The safest test is to temporarily specify a deliberately unavailable URL in a separate endpoint. After two or three intervals, Gatus should send a Telegram message. Then restore the correct address: after the configured success-threshold, a recovery notification will arrive.
docker compose restart gatus
docker compose logs -f gatus
The first command applies configuration template changes, while the second displays logs in real time. After completing the test, remove the temporary endpoint to avoid receiving false alerts.
Important: Gatus checks services from your VPS IP address. If the monitored resource blocks requests from data centers, uses geographic restrictions, or Cloudflare WAF, add the monitoring IP to the allowlist or configure a separate permitted
/healthendpoint.
Backups and Maintenance
Gatus can be redeployed quickly, but without a backup you will lose check settings, incident history, the SQLite database, and Caddy certificate data. The minimum strategy is to back up the /opt/gatus directory to external storage daily and periodically test restoration on a test server.
What to back up
/opt/gatus/config/— endpoint templates and monitoring logic./opt/gatus/.env— Telegram token, chat ID, domain, and ACME email./opt/gatus/data/gatus.db— SQLite history of checks and events.- Docker volume
caddy_data— certificates, keys, and Caddy state. - The
/opt/gatus/compose.yamlfile and Caddyfile.
Do not keep the only backup copy on the same VPS. Suitable options include S3-compatible storage, a separate server via SFTP, or a second VPS over SSH. If the backup contains .env and a certificate private key, the repository must be encrypted.
Install restic
sudo apt install -y restic
sudo mkdir -p /root/.config/restic
sudo chmod 700 /root/.config/restic
Restic creates encrypted, deduplicated backups. The example below uses S3-compatible storage. Access credentials must be provided by your object storage service.
sudo nano /root/.config/restic/gatus.env
export RESTIC_REPOSITORY="s3:https://s3.example.net/gatus-backups"
export RESTIC_PASSWORD="REPLACE_WITH_LONG_RANDOM_PASSWORD"
export AWS_ACCESS_KEY_ID="REPLACE_WITH_ACCESS_KEY"
export AWS_SECRET_ACCESS_KEY="REPLACE_WITH_SECRET_KEY"
sudo chmod 600 /root/.config/restic/gatus.env
sudo bash -c 'source /root/.config/restic/gatus.env && restic init'
The commands protect the variables file and initialize an empty encrypted repository. Store the RESTIC_PASSWORD separately: restoration is impossible without it.
Create a backup script
sudo nano /usr/local/sbin/backup-gatus.sh
#!/usr/bin/env bash
set -euo pipefail
source /root/.config/restic/gatus.env
cd /opt/gatus
docker compose stop gatus
trap 'docker compose start gatus' EXIT
restic backup \
/opt/gatus/config \
/opt/gatus/caddy \
/opt/gatus/compose.yaml \
/opt/gatus/.env \
/opt/gatus/data \
--tag gatus
docker run --rm \
-v caddy_data:/source:ro \
-v /opt/gatus/backups:/backup \
alpine sh -c 'tar czf /backup/caddy_data.tar.gz -C /source .'
restic backup /opt/gatus/backups/caddy_data.tar.gz --tag caddy
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
The script briefly stops Gatus to obtain a consistent SQLite copy, then starts it again even if an error occurs thanks to trap. Caddy continues running, so the web panel may display the last page, but new checks will stop for a few seconds.
sudo chmod 700 /usr/local/sbin/backup-gatus.sh
sudo /usr/local/sbin/backup-gatus.sh
sudo bash -c 'source /root/.config/restic/gatus.env && restic snapshots'
First, run the backup manually and make sure a new snapshot appears in the output. Only then add automated execution.
sudo crontab -e
15 3 * /usr/local/sbin/backup-gatus.sh >> /var/log/backup-gatus.log 2>&1
The job runs a backup every day at 03:15 server time. Test restoration once a month: download a snapshot to a separate temporary directory, verify that gatus.db is present and that the YAML is valid.
Updates and status monitoring
For a small installation, it is best to perform updates during a maintenance window: alerts may briefly not arrive during this time, and version changes are easier to roll back. Before updating, create a backup, read the changelog, and check whether the Gatus configuration format has changed.
cd /opt/gatus
sudo /usr/local/sbin/backup-gatus.sh
docker compose pull
docker compose up -d
docker image prune -f
docker compose ps
Gatus is not designed for rolling updates as a clustered service with automatic failover. If monitoring is critical, deploy a second instance on another VPS with a separate Telegram channel or different message prefixes. This is more reliable than trying to update a single instance without a brief interruption.
Check df -h, docker system df, container status, and the size of /opt/gatus/data/gatus.db weekly. With a high check frequency, the database will grow. Reduce the history retention period using the capabilities of the current Gatus version, or periodically export metrics to Prometheus and remove old data according to the documented procedure for the version you use.
Troubleshooting and FAQ
Why does Gatus not open over HTTPS while Caddy reports a certificate acquisition error?
First, check DNS: the dig +short status.example.com command must return the VPS public IP. Then make sure UFW allows ports 80 and 443, and Docker publishes them through docker compose ps. Check whether the ports are occupied by another Nginx or Apache instance: sudo ss -ltnp | grep -E ':80|:443'. If a CDN is used, temporarily disable proxying or configure the correct TLS mode for the ACME challenge.
Why are Telegram notifications not arriving?
Check that the bot has received at least one message from the user or has been added to a group. Then run the getUpdates request and make sure that TELEGRAM_CHAT_ID matches message.chat.id. Group IDs are often negative. Check the .env file for extra quotes, spaces, and an incorrect token, then restart Gatus with docker compose restart gatus and review the container logs.
Why does the Gatus container keep restarting?
The most common cause is a YAML error or an unsupported configuration field after an image update. Run docker compose logs --tail=200 gatus: the error line usually contains the YAML line number. Check indentation, use spaces instead of tabs, and ensure special characters in URLs are enclosed in quotes. If the problem appeared after an update, temporarily restore the previously working image tag and compare the configuration with that version's documentation.
Why does an endpoint show an error even though the site opens in a browser?
The browser and VPS may use different DNS, IPv4/IPv6 routes, geolocation, and headers. Check the request directly from the container: docker exec -it gatus wget -S -O /dev/null https://example.com. Possible causes include WAF blocking of data centers, a required Host header, a redirect to another domain, authentication requirements, or a timeout that is too short. Configure an allowlist for monitoring IPs, increase the timeout to 15–20 seconds, and check a dedicated /health endpoint.
Why does the SSL alert arrive too late or not arrive at all?
Check the endpoint interval: with a value of 12h, Gatus detects a certificate change only twice per day. For important domains, use an interval of 1–6 hours. Check the [CERTIFICATE_EXPIRATION] condition: 336 hours equals 14 days, and 720 hours equals 30 days. Also make sure the endpoint actually uses HTTPS rather than HTTP. After changing the condition, restart the container and check the endpoint status in the panel.
What is the minimum suitable VPS configuration?
For 10–30 websites and API endpoints checked every 1–5 minutes, 1 vCPU, 1 GB RAM, 10 GB SSD, and a 100 Mbps connection are sufficient. It is more practical to choose 2 GB RAM and 20 GB NVMe: this leaves capacity for Docker, Caddy, updates, and SQLite history. A public IPv4 address, the ability to open ports 80/443, and a stable network are essential. If hundreds of endpoints are planned, start with 2 vCPU, 4 GB RAM, and 30–50 GB NVMe.
What should you choose for this task: VPS or dedicated server?
A VPS is almost always sufficient for Gatus. It is cheaper, faster to deploy, and easy to scale as the number of checks grows. A dedicated server is needed if Gatus is part of a large monitoring system with Prometheus, Grafana, logs, and thousands of frequent checks, or if the security policy requires physically dedicated hardware. To improve reliability, it is better to spend the budget not on a dedicated server but on a second small VPS in a different network and location.
Can the Gatus panel be closed to public access?
Yes. The simplest option is to leave access public only through a VPN, such as WireGuard, and close ports 80/443 in UFW for everyone except the VPN subnet. If the panel must be accessible to several employees, add Basic Auth to the Caddyfile with a hashed password. Do not block ACME checks if Caddy continues issuing a public certificate. Alternatively, use a DNS challenge or a separate internal domain with corporate TLS.
Conclusions and Next Steps
Gatus is now running on a separate VPS with an HTTPS panel, website and API checks, SSL certificate monitoring, and Telegram notifications. Configuration is separated from secrets, data is stored in SQLite, and backups are sent to external encrypted storage.
- Add endpoints for all public websites, APIs, DNS dependencies, SSH, and critical TCP services.
- Create separate groups for production, staging, and external providers to make Telegram alerts easier to classify.
- For critical systems, deploy a second Gatus instance in another location and compare incidents from independent networks.