Back to Article List

Hermes Agent behind Nginx with HTTPS and basic auth

Hermes Agent behind Nginx with HTTPS and basic auth

Hermes ships a local dashboard on port 9119 that gives you a browser view of your sessions, your skills and the agent's recent activity. By default it listens on all interfaces, which means anyone who finds your IP and tries the port gets the same dashboard you do. That's fine on your laptop. It's not fine on a VPS with a public IP. The fix is to bind Hermes to localhost, put Nginx in front, terminate TLS with Let's Encrypt and add a basic-auth gate so only you (and people you authorise) can reach the UI.

Same pattern works for Caddy if you prefer it; the Nginx config below has rough Caddy equivalents in roughly half the lines, but Nginx is what most people already have on their boxes and the config is more transparent for debugging.

Bind Hermes to localhost first

Before you stand up a proxy, make sure Hermes isn't accepting public connections. The cleanest way is via the config:

hermes config set dashboard.host 127.0.0.1
hermes config set dashboard.port 9119

Restart the gateway. Confirm with sudo ss -tlnp | grep 9119. The output should show the listener on 127.0.0.1:9119, not 0.0.0.0:9119 or :::9119.

If you're running Hermes under Docker, the Docker Compose setup already binds the dashboard to localhost in its port mapping ("127.0.0.1:9119:9119"). Confirm by checking the container's port bindings; if the binding shows the host as 0.0.0.0 rather than 127.0.0.1, fix the Compose file before continuing. If you've been reaching the dashboard via SSH tunnel per the VPS setup guide, this Nginx setup is the next step when you want shared access without the tunnel each time.

The Nginx server block

Save as /etc/nginx/sites-available/hermes:

upstream hermes_dashboard {
    server 127.0.0.1:9119;
    keepalive 32;
}

server {
    listen 80;
    listen [::]:80;
    server_name hermes.example.com;

    # Let's Encrypt HTTP-01 challenge
    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }

    # Everything else: redirect to HTTPS
    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name hermes.example.com;

    ssl_certificate /etc/letsencrypt/live/hermes.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/hermes.example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;

    # HSTS, secure headers
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header Referrer-Policy "no-referrer-when-downgrade" always;

    # Basic auth gate
    auth_basic "Hermes dashboard";
    auth_basic_user_file /etc/nginx/.htpasswd-hermes;

    # WebSocket-aware proxy to the dashboard
    location / {
        proxy_pass http://hermes_dashboard;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        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 https;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

The bits worth explaining.

The upstream block with keepalive 32 is small but matters. Without it, every request to Nginx opens a new connection to Hermes, which adds latency on each request. The keepalive pool keeps 32 idle connections to the dashboard, reused across requests.

The WebSocket upgrade headers (Upgrade, Connection "upgrade") are required because the Hermes dashboard streams session updates over WebSockets. Without them, the chat panel goes silent after the initial page load. The 3600-second timeouts cover long-running sessions where the agent is mid-thought for a while.

proxy_buffering off matters for streamed responses. Without it, Nginx buffers chunks before forwarding, which makes the dashboard feel laggy during streaming agent output.

The HSTS and security headers are belt-and-braces. None of them prevent a determined attack on a misconfigured backend, but they cover the easy footguns (clickjacking, MIME sniffing, downgrade attacks).

Get the certificate

Install certbot if you haven't:

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

Make sure DNS is pointing at your VPS first; certbot validates by reaching the host over HTTP, so the A or AAAA record needs to resolve to your box before you run the command.

Drop the Nginx config into place and reload to enable the HTTP-01 challenge path:

sudo ln -s /etc/nginx/sites-available/hermes /etc/nginx/sites-enabled/
sudo mkdir -p /var/www/letsencrypt
sudo nginx -t
sudo systemctl reload nginx

Then issue the certificate using the webroot plugin (which uses the /.well-known/acme-challenge/ location we already configured):

sudo certbot certonly --webroot \
  -w /var/www/letsencrypt \
  -d hermes.example.com \
  --email [email protected] \
  --agree-tos \
  --non-interactive

Certbot writes the cert to /etc/letsencrypt/live/hermes.example.com/. Reload Nginx one more time to pick it up:

sudo nginx -t && sudo systemctl reload nginx

Visit https://hermes.example.com in a browser. You should get a basic-auth prompt (which you haven't set up yet, so it'll fail). The next step fixes that.

Set up basic auth

Install the apache2-utils package, which gives you htpasswd:

sudo apt install -y apache2-utils

Create the password file:

sudo htpasswd -c /etc/nginx/.htpasswd-hermes alice
# Enter password when prompted
sudo htpasswd /etc/nginx/.htpasswd-hermes bob
# Drop the -c flag for additional users; -c creates a new file

Lock down the file's permissions:

sudo chown www-data:www-data /etc/nginx/.htpasswd-hermes
sudo chmod 640 /etc/nginx/.htpasswd-hermes

Reload Nginx, hit your URL again and you should now see a username/password prompt. Enter what you set; you should see the Hermes dashboard.

Cert renewal

Certbot installs a systemd timer that runs certbot renew twice a day. Renewals are no-ops until the cert is within 30 days of expiry, at which point certbot quietly renews and reloads Nginx. Check it's working with:

sudo systemctl list-timers | grep certbot

If you don't see a timer, your install missed it; sudo systemctl enable --now certbot.timer creates and starts it. Test the renewal flow without renewing using sudo certbot renew --dry-run; if that passes, you're set.

Adding the gateway HTTP endpoint

If you're using Slack or any custom webhook integration, Hermes also opens a public-facing HTTP endpoint for inbound webhooks. The pattern is the same: bind it to localhost, proxy through Nginx, terminate TLS. Add a second server block (or a second location) that proxies /webhook/ paths to 127.0.0.1:8765 (or whichever port you've configured the gateway to listen on).

One thing to know: the gateway HTTP endpoint typically authenticates incoming webhooks via signed headers (Slack's signing secret, GitHub's HMAC, etc.) so you usually don't want basic auth on those paths. Either keep them on a separate hostname (hermes-webhooks.example.com) without basic auth or use Nginx's auth_basic off; directive inside a specific location block to exempt the webhook paths.

Restricting by IP for extra safety

Basic auth is fine. If you want stricter control (say, only allow access from your office IP plus your home VPN), pair basic auth with an allow/deny stanza:

location / {
    allow 203.0.113.0/24;     # office network
    allow 198.51.100.42;       # your home IP
    deny all;
    auth_basic "Hermes dashboard";
    auth_basic_user_file /etc/nginx/.htpasswd-hermes;
    # ...rest of proxy config
}

Both checks have to pass: your IP must be allowed and your password must be correct. If your home IP changes monthly, this gets annoying; the better pattern is to put the dashboard behind a VPN or a Cloudflare Access policy if you're already running CF in front. For most personal setups, basic auth alone is enough.

Common issues

If the page loads over HTTPS but the chat panel is empty or stuck on "connecting", the WebSocket headers aren't getting through. Double-check the Upgrade and Connection directives in the location / block. Some Nginx defaults strip those headers via proxy_set_header Connection ""; make sure you're explicitly setting them.

If certbot fails with "challenge did not pass", the most common cause is your A record pointing somewhere else. Double-check with dig +short hermes.example.com. If the result doesn't match your VPS IP, fix DNS and retry. The second most common cause is a firewall blocking port 80 inbound; certbot's HTTP-01 challenge needs port 80 reachable from the public internet during the validation window.

If you see a redirect loop after enabling HTTPS, the issue is usually that Hermes is generating absolute HTTP URLs internally, which Nginx then redirects back to HTTPS, creating a loop. Set HERMES_DASHBOARD_BASE_URL=https://hermes.example.com in your env so the dashboard generates HTTPS URLs from the start.

If you'd rather not run Nginx yourself

The LumaDock Hermes Agent VPS template ships Hermes pre-installed on Ubuntu 24.04 with Docker CE and a basic firewall config in place, so adding Nginx is a few apt commands away. If you'd rather skip writing the Nginx config by hand, a managed reverse-proxy stack like Caddy or Coolify can sit in front of Hermes and handle the TLS termination for you.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Ciclo de Pagamento

1 GB RAM VPS

$3.99 Save  25 %
$2.99 Mensalmente
  • 1 vCPU AMD EPYC
  • 30 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Gestão de firewall
  • Monitor grátis

2 GB RAM VPS

$5.99 Save  17 %
$4.99 Mensalmente
  • 2 vCPU AMD EPYC
  • 30 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Gestão de firewall
  • Monitor grátis

6 GB RAM VPS

$14.99 Save  33 %
$9.99 Mensalmente
  • 6 vCPU AMD EPYC
  • 70 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P1

$7.99 Save  25 %
$5.99 Mensalmente
  • 2 vCPU AMD EPYC
  • 4 GB memória RAM
  • 40 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P2

$14.99 Save  27 %
$10.99 Mensalmente
  • 2 vCPU AMD EPYC
  • 8 GB memória RAM
  • 80 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P4

$29.99 Save  20 %
$23.99 Mensalmente
  • 4 vCPU AMD EPYC
  • 16 GB memória RAM
  • 160 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P5

$36.49 Save  21 %
$28.99 Mensalmente
  • 8 vCPU AMD EPYC
  • 16 GB memória RAM
  • 180 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P6

$56.99 Save  21 %
$44.99 Mensalmente
  • 8 vCPU AMD EPYC
  • 32 GB memória RAM
  • 200 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

AMD EPYC VPS.P7

$69.99 Save  20 %
$55.99 Mensalmente
  • 16 vCPU AMD EPYC
  • 32 GB memória RAM
  • 240 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

EPYC Genoa VPS.G1

$4.99 Save  20 %
$3.99 Mensalmente
  • 1 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4.ª geração 9xx4 com 3.25 GHz ou equivalente, baseada na arquitetura Zen 4.
  • 1 GB DDR5 memória RAM
  • 25 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

EPYC Genoa VPS.G2

$12.99 Save  23 %
$9.99 Mensalmente
  • 2 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4.ª geração 9xx4 com 3.25 GHz ou equivalente, baseada na arquitetura Zen 4.
  • 4 GB DDR5 memória RAM
  • 50 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

EPYC Genoa VPS.G4

$25.99 Save  27 %
$18.99 Mensalmente
  • 4 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4.ª geração 9xx4 com 3.25 GHz ou equivalente, baseada na arquitetura Zen 4.
  • 8 GB DDR5 memória RAM
  • 100 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

EPYC Genoa VPS.G6

$48.99 Save  31 %
$33.99 Mensalmente
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4.ª geração 9xx4 com 3.25 GHz ou equivalente, baseada na arquitetura Zen 4.
  • 16 GB DDR5 memória RAM
  • 200 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

EPYC Genoa VPS.G7

$74.99 Save  27 %
$54.99 Mensalmente
  • 8 vCPU AMD EPYC Gen4 AMD EPYC Genoa 4.ª geração 9xx4 com 3.25 GHz ou equivalente, baseada na arquitetura Zen 4.
  • 32 GB DDR5 memória RAM
  • 250 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

1 vCPU AMD Ryzen 9

$13.99 Save  29 %
$9.99 Mensalmente
  • CPU dedicada 4.5GHz AMD Ryzen 9 7950X com uma frequência nativa de CPU de 4.5 GHz.
  • 4 GB DDR5 memória RAM
  • 50 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

2 vCPU AMD Ryzen 9

$25.99 Save  19 %
$20.99 Mensalmente
  • CPU dedicada 4.5GHz AMD Ryzen 9 7950X com uma frequência nativa de CPU de 4.5 GHz.
  • 8 GB DDR5 memória RAM
  • 100 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

8 vCPU AMD Ryzen 9

$92.99 Save  30 %
$64.99 Mensalmente
  • CPU dedicada 4.5GHz AMD Ryzen 9 7950X com uma frequência nativa de CPU de 4.5 GHz.
  • 32 GB DDR5 memória RAM
  • 400 GB NVMe disco
  • Ilimitada largura de banda
  • IPv4 e IPv6 incluídos O suporte a IPv6 não está disponível em França, Finlândia ou Países Baixos.
  • 1 Gbps rede
  • Cópia automática incluída
  • Gestão de firewall
  • Monitor grátis

FAQ

How do I add a second user to the basic-auth list without overwriting the first?

The -c flag on htpasswd creates a new file, overwriting any existing one. To add to an existing file, drop the flag: sudo htpasswd /etc/nginx/.htpasswd-hermes bob. Same for password changes for an existing user; htpasswd updates the entry rather than duplicating it. To remove a user, sudo htpasswd -D /etc/nginx/.htpasswd-hermes bob deletes their entry.

Your agent runs wild. Your bill doesn't.

Easily deploy Hermes in one click on Ubuntu 24.04 with AMD EPYC, NVMe storage and unmetered bandwidth. The price stays the same whatever the agent does, no setup fees, no overage charges and no tier traps.

GPU products are in high demand at the moment. Fill the form to get notified as soon as your preferred GPU server is back in stock.