Putting DB Agent Behind a Reverse Proxy
A reverse proxy can only front DB Agent's management API, the HTTP connection the Panel uses to manage the agent. The database proxies on ports 5432, 3306, 27017 and 6379 speak the databases' own protocols, not HTTP, so they stay on their own ports; encrypt them with DB Agent's built-in TLS instead. For how a proxy fits into the request path in general, see Setting up a Reverse Proxy.
Most agents don't need a proxy at all: DB Agent can serve HTTPS itself. Use a proxy when one already runs on the machine, or you want one place to manage certificates.
Prerequisites
- DB Agent installed, reachable at
http://<agent-ip>:8090. - A domain for the agent (
<agent-domain>below) with anArecord, plusAAAAfor IPv6, pointing at it. - Ports
80and443open, and forwarded on your router if the machine is at home. - A TLS certificate for the domain from a public CA. Caddy issues its own.
- The proxy installed on the same machine.
WARNING
A broken proxy configuration makes the agent unreachable for the Panel until it's fixed, so keep a terminal open. Existing database connections are not affected, since they don't go through the proxy. Step 1 restarts DB Agent, which does drop them.
Step 1: Prepare DB Agent
Everything in this step happens on the agent's machine. The Panel never pushes api.bind, api.tls or api.trusted_proxies, so they can only be set there.
Merge the config.yml snippets below into the existing api: block. DB Agent won't start if api: appears twice.
Pick the tab that matches how DB Agent is installed:
The changes go into config.yml in the DB Agent compose directory, plus one change in compose.yml.
Turn Off DB Agent's Own TLS
The proxy terminates TLS and forwards to DB Agent over plain HTTP. With api.tls still enabled, DB Agent rejects plain HTTP and every request fails. In config.yml:
api:
tls:
enabled: falseThis is the default on a fresh install, so it only needs changing on an agent that ran with its own certificate before.
Stop Exposing Port 8090
The compose file publishes port 8090 on every interface of the host, so it stays reachable from the internet even after the proxy is in place. Restrict it to the loopback interface in compose.yml, and leave the database ports alone. api.bind stays 0.0.0.0:8090 inside the container:
services:
db-agent:
ports:
- 5432:5432
- 3306:3306
- 27017:27017
- 6379:6379
- 127.0.0.1:8090:8090Trust the Proxy's Address
Connections from the proxy arrive from the gateway of the DB Agent container's Docker network. Print that address inside the DB Agent compose directory:
sudo docker inspect -f '{{range .NetworkSettings.Networks}}{{println .Gateway}}{{end}}' $(sudo docker compose ps -q db-agent)Then set api.trusted_proxies to it in config.yml:
api:
trusted_proxies:
- 172.20.0.1Apply the Changes
sudo docker compose up -d --force-recreateWhen a request arrives from a trusted address, DB Agent reads the client's IP from X-Forwarded-For (walking the list from the right and skipping every trusted address) and falls back to X-Real-IP. From anywhere else, both headers are ignored. Only list addresses you control.
Then confirm the API answers on the loopback address, and only there:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8090/api/statusA 401 is the expected answer, since the request carries no token. It proves DB Agent is listening and the proxy will be able to reach it. A 000 means DB Agent isn't running or api.tls.enabled is still true. From another machine, http://<agent-ip>:8090 should now time out or be refused.
Step 2: Configure the Proxy
Every configuration below does the same things. If you use a proxy that isn't listed, these are the settings to replicate:
| Setting | Why DB Agent needs it |
|---|---|
Forward to http://127.0.0.1:8090 | The address the management API listens on after Step 1 |
Pass Upgrade and Connection through | The Panel streams instance logs and statistics over WebSockets |
| No request body limit | A database import arrives as one streamed request carrying the whole dump |
| Request and response buffering off | Imports and exports stream; with buffering on, the proxy spools them to disk first |
| Read and write timeouts above 60s | A large import or export holds one connection open far longer than the defaults allow |
X-Forwarded-For or X-Real-IP | The client IPs from Step 1 |
1. Add the WebSocket map to /etc/nginx/nginx.conf, inside http { ... } and outside any server { ... } block, if this machine doesn't have it yet:
map $http_upgrade $connection_upgrade {
default upgrade;
'' "";
}2. Create the site as /etc/nginx/sites-available/calagopus-db-agent.conf. With nginx.org packages, use /etc/nginx/conf.d/calagopus-db-agent.conf instead and skip the ln line in step 3.
# Replace <agent-domain> with the agent's domain everywhere below.
# Change the address in the upstream if DB Agent doesn't listen on 127.0.0.1:8090.
upstream calagopus-db-agent {
server 127.0.0.1:8090;
keepalive 16;
}
server {
listen 80;
listen [::]:80;
server_name <agent-domain>;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
http2 on;
server_name <agent-domain>;
access_log /var/log/nginx/calagopus-db-agent.access.log;
error_log /var/log/nginx/calagopus-db-agent.error.log error;
# The paths certbot creates. Change them if your certificate lives elsewhere.
ssl_certificate /etc/letsencrypt/live/<agent-domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<agent-domain>/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Leave at 0: database imports arrive as one streamed request.
client_max_body_size 0;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "same-origin" always;
location / {
proxy_http_version 1.1;
# WebSocket support (uses the map from step 1)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
# Tell DB Agent who the client is
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;
# Stream imports and exports instead of spooling them to disk
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
send_timeout 3600s;
proxy_redirect off;
proxy_pass http://calagopus-db-agent;
}
}3. Enable it and reload. Link the site into sites-enabled.
sudo ln -s /etc/nginx/sites-available/calagopus-db-agent.conf /etc/nginx/sites-enabled/calagopus-db-agent.conf
sudo nginx -t
sudo systemctl reload nginxNginx older than 1.25.1
The standalone http2 on; directive was added in 1.25.1. On older releases, drop that line and put the parameter back on the listen directives instead:
listen 443 ssl http2;
listen [::]:443 ssl http2;Step 3: Point the Panel at the Proxied URL
In Admin → Database Agent Hosts → (your host) → General, set URL to https://<agent-domain> without a port. The proxy listens on 443, which is what the URL implies. The form warns that no port was given and offers to add :8090; ignore it here, since :8090 would bypass the proxy.
Then open the host's Configuration tab, click Reveal Configuration, and click Verify Connection; the result appears in the Backend to DB Agent box. The tab's port mismatch warning is expected behind a proxy.
Don't re-apply the generated configuration
It sets api.bind back to 0.0.0.0:8090, which undoes Step 1 on a standalone install, and pasting it over config.yml removes your other settings. After Reset Token, change only the token:
- Standalone: run
sudo calagopus-db-agent configure --token <TOKEN>, then restart the service. - Docker: set
api.tokeninconfig.yml, then runsudo docker compose restart db-agent.
Troubleshooting
502 Bad Gateway, or the proxy's own error page
The proxy can't reach DB Agent. Check that it's running with sudo systemctl status db-agent or sudo docker compose ps, and that curl -I http://127.0.0.1:8090/api/status answers on the machine. If every request fails immediately, api.tls.enabled is probably still true while the proxy forwards plain HTTP.
Permission denied in the proxy's error log means SELinux is blocking it; see the RHEL tab in Step 2.
DB Agent exits on startup with duplicate mapping key: api
config.yml contains api: more than once. Merge the settings from Step 1 into a single block.
Instance logs and statistics in the Panel stay empty
WebSocket upgrades aren't getting through. On Nginx, confirm the map block exists in nginx.conf and both Upgrade and Connection headers are set. On Apache, check the version note above. If Verify Connection passes, check the certificate: the Panel's WebSocket client only trusts public CAs.
A database import fails with 413 Request Entity Too Large
The proxy still has a body limit. Set client_max_body_size 0 on Nginx or LimitRequestBody 0 on Apache, and remove any request_body block on Caddy.
Large imports or exports die partway through, always after about the same time
A proxy timeout. Raise proxy_read_timeout, proxy_send_timeout and send_timeout on Nginx, or ProxyTimeout on Apache.
Verify Connection fails
The URL still has :8090 or http://, the certificate isn't valid for the hostname, or port 443 is blocked between the Panel and the agent.