Intermediate
    30–45 min
    512 MB+ RAM

    Deploy Gatus on a RamNode VPS

    Developer-oriented health checks and status pages. HTTP, TCP, DNS, ICMP — all configured through a single YAML file, using 10–30 MB of RAM.

    Part of a series: This standalone guide covers Gatus deployment end-to-end. For GitOps workflows and advanced condition syntax, see Part 3 of the VPS Monitoring & Observability Series.

    At a Glance

    ApplicationGatus v5.x (Apache 2.0)
    DeploymentDocker + Docker Compose
    Minimum VPS1 vCPU / 512 MB RAM / 10 GB SSD
    Recommended VPS1 vCPU / 1 GB RAM / 25 GB SSD
    OSUbuntu 22.04 LTS or 24.04 LTS
    Ports8080 (app), 80 (HTTP), 443 (HTTPS)

    Prerequisites

    • A RamNode VPS running Ubuntu 22.04 or 24.04 LTS
    • SSH access with a non-root sudo user
    • Docker and Docker Compose installed (see Step 1 if not)
    • A domain name pointed at your VPS IP (for public status page with HTTPS)
    • Ports 80 and 443 open in your firewall

    Note: If you only need private monitoring, skip the Nginx and SSL sections and access the dashboard over port 8080 directly.

    1

    Install Docker and Docker Compose

    Skip this step if Docker is already installed. To verify:

    Check Docker installation
    docker --version && docker compose version

    If not installed, use the official Docker repository:

    Install Docker
    sudo apt update && sudo apt upgrade -y
    sudo apt install -y ca-certificates curl gnupg
    
    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
    
    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
    sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
    
    sudo usermod -aG docker $USER
    newgrp docker
    2

    Create the Project Directory

    Create directory structure
    mkdir -p ~/gatus/{config,data}
    cd ~/gatus
    Project layout
    gatus/
    ├── docker-compose.yml
    ├── config/
    │   └── config.yaml    # monitoring rules and alert settings
    └── data/
        └── data.db        # SQLite database (auto-created)
    3

    Write the Configuration File

    Gatus is configured entirely through a single YAML file. Customize the endpoints and alert settings for your environment.

    ~/gatus/config/config.yaml
    # --- Storage ---
    # SQLite keeps uptime history across container restarts.
    storage:
      type: sqlite
      path: /data/data.db
    
    # --- Web UI settings ---
    web:
      port: 8080
    
    # --- Optional: Protect the dashboard with a password ---
    # Generate a bcrypt hash: htpasswd -bnBC 10 '' yourpassword | tr -d ':\n' | base64
    # security:
    #   basic:
    #     username: admin
    #     password-bcrypt-base64: "<your-bcrypt-base64-hash>"
    
    # --- Alerting ---
    # Remove or comment out providers you do not need.
    alerting:
      slack:
        webhook-url: "https://hooks.slack.com/services/REPLACE/ME"
        default-alert:
          failure-threshold: 2
          success-threshold: 1
          send-on-resolved: true
    
      discord:
        webhook-url: "https://discord.com/api/webhooks/REPLACE/ME"
        default-alert:
          failure-threshold: 2
          send-on-resolved: true
    
      email:
        from: gatus@example.com
        username: gatus@example.com
        password: "${EMAIL_PASSWORD}"
        host: smtp.example.com
        port: 587
        to: oncall@example.com
    
    # --- Endpoints to monitor ---
    endpoints:
      - name: Main Website
        group: Web
        url: "https://example.com"
        interval: 60s
        conditions:
          - "[STATUS] == 200"
          - "[RESPONSE_TIME] < 1500"
          - "[CERTIFICATE_EXPIRATION] > 168h"   # Alert if cert expires < 7 days
        alerts:
          - type: slack
          - type: email
    
      - name: API Health
        group: API
        url: "https://api.example.com/health"
        interval: 30s
        conditions:
          - "[STATUS] == 200"
          - "[RESPONSE_TIME] < 500"
          - '[BODY] == pat(*"status"*"ok"*)'
        alerts:
          - type: slack
            description: "API health check failed"
    
      - name: SSH Access
        group: Infrastructure
        url: "tcp://yourserver.example.com:22"
        interval: 60s
        conditions:
          - "[CONNECTED] == true"
        alerts:
          - type: slack
    
      - name: DNS Resolution
        group: Infrastructure
        url: "dns://1.1.1.1"
        dns:
          query-name: "example.com"
          query-type: "A"
        interval: 120s
        conditions:
          - "[DNS_RCODE] == NOERROR"
        alerts:
          - type: slack
    4

    Create the Docker Compose File

    ~/gatus/docker-compose.yml
    services:
      gatus:
        image: twinproduction/gatus:stable
        container_name: gatus
        restart: unless-stopped
        ports:
          - "127.0.0.1:8080:8080"   # Bind to localhost only - Nginx handles public traffic
        volumes:
          - ./config:/config
          - ./data:/data
          - /etc/localtime:/etc/localtime:ro
        environment:
          - GATUS_CONFIG_PATH=/config
          # Uncomment and fill in if using email alerting:
          # - EMAIL_PASSWORD=your_smtp_password
        healthcheck:
          test: ["CMD", "wget", "-qO-", "http://localhost:8080/health"]
          interval: 30s
          timeout: 10s
          retries: 3
          start_period: 10s
    Start Gatus
    cd ~/gatus
    docker compose up -d
    
    # Check that the container started correctly
    docker compose ps
    docker compose logs -f

    Gatus should now be accessible locally at http://localhost:8080. If you don't need a public URL, you can stop here and access the dashboard over SSH port-forwarding.

    5

    Configure the Firewall

    Open ports for Nginx
    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp
    sudo ufw allow 443/tcp
    sudo ufw enable
    sudo ufw status
    6

    Install Nginx and Configure a Reverse Proxy

    Install nginx
    sudo apt install -y nginx

    Create a server block. Replace status.example.com with your actual domain:

    /etc/nginx/sites-available/gatus
    server {
        listen 80;
        server_name status.example.com;
    
        location / {
            proxy_pass           http://127.0.0.1:8080;
            proxy_http_version   1.1;
            proxy_set_header     Host              $host;
            proxy_set_header     X-Real-IP         $remote_addr;
            proxy_set_header     X-Forwarded-For   $proxy_add_x_forwarded_for;
            proxy_set_header     X-Forwarded-Proto $scheme;
            proxy_read_timeout   60s;
        }
    }
    Enable and test
    sudo ln -s /etc/nginx/sites-available/gatus /etc/nginx/sites-enabled/
    sudo nginx -t && sudo systemctl reload nginx
    7

    Enable HTTPS with Let's Encrypt

    Install Certbot and obtain certificate
    sudo apt install -y certbot python3-certbot-nginx
    sudo certbot --nginx -d status.example.com

    Certbot will automatically update your Nginx config with SSL directives and set up a cron job for auto-renewal. Verify renewal works:

    Test renewal
    sudo certbot renew --dry-run
    8

    Protect the Dashboard (Optional)

    If your status page is public-facing but you want to prevent unauthorized access, Gatus supports basic authentication with bcrypt-hashed passwords.

    Generate a bcrypt hash
    sudo apt install -y apache2-utils
    # Generate a bcrypt hash (cost factor 10)
    htpasswd -bnBC 10 '' yourpassword | tr -d ':\n' | base64

    Paste the output into the security block in your config.yaml:

    Security block in config.yaml
    security:
      basic:
        username: admin
        password-bcrypt-base64: "JDJ5JDEwJHh4eHh4..."  # paste your hash here
    Restart to apply
    docker compose restart
    9

    Verify Uptime History and Persistence

    The SQLite configuration from Step 3 preserves endpoint history across container restarts. Verify it's working:

    Verify persistence
    # Confirm the database file exists after a few minutes
    ls -lh ~/gatus/data/
    
    # Restart and confirm history is preserved
    docker compose restart
    10

    Test Alerting

    Add a temporary endpoint that will intentionally fail to verify your alerting integration:

    Add to endpoints in config.yaml temporarily
      - name: Intentional Failure Test
        group: Testing
        url: "https://httpstat.us/500"
        interval: 30s
        conditions:
          - "[STATUS] == 200"
        alerts:
          - type: slack
            failure-threshold: 1
            description: "This is an intentional test alert"

    Within 30–60 seconds you should receive an alert on Slack (or whichever provider you configured). Remove the test endpoint once verified.

    Useful Management Commands

    TaskCommand
    View logsdocker compose logs -f
    Restartdocker compose restart
    Stopdocker compose down
    Pull latest imagedocker compose pull && docker compose up -d
    Reload config (hot)docker compose kill -s SIGHUP gatus
    Backup databasecp ~/gatus/data/data.db ~/gatus-backup-$(date +%F).db
    Check disk usagedu -sh ~/gatus/data/

    Troubleshooting

    Container exits immediately after starting

    This almost always means a YAML syntax error in config.yaml. Run docker compose logs gatus to see the exact error. YAML is whitespace-sensitive — ensure you are using spaces, not tabs.

    Dashboard is not accessible over HTTPS

    • Confirm the Nginx config is symlinked in sites-enabled
    • sudo nginx -t should return no errors
    • Confirm ports 80 and 443 are open in ufw and in the RamNode control panel firewall
    • Check that the domain DNS A record points to your VPS IP

    Alerts not being delivered

    • Verify the webhook URL or SMTP credentials in config.yaml
    • Check container logs for alert delivery errors
    • Ensure failure-threshold is set to a low value (1 or 2) when testing

    High disk usage over time

    Gatus retains all historical check data in SQLite. For long-running instances with many endpoints and short intervals, the data.db file can grow. Monitor the size with du -sh ~/gatus/data/ and back it up before it grows too large.

    Upgrading Gatus

    Pull latest and restart
    cd ~/gatus
    docker compose pull
    docker compose up -d
    
    # Verify the new version is running
    docker compose logs --tail=20