By default the installer runs a bundled Caddy that owns ports 80 and 443 and handles HTTPS for you. That's ideal on a clean server. It's the wrong fit when:
- the server already runs nginx, Apache, Traefik, Caddy, or a panel like Plesk or cPanel on 80/443,
- Datalumo sits behind a cloud load balancer, or
- you want to run several instances on one server. They can't all bind 80/443.
For these, install in behind-proxy mode. Datalumo skips its own Caddy and serves plain HTTP on a loopback port. Your proxy terminates TLS and forwards to it.
DATALUMO_BEHIND_PROXY=1 \
DOMAIN=ai.example.com \
bash install.sh
DATALUMO_BEHIND_PROXY=1 leaves out the bundled Caddy.
DOMAIN is still your public hostname. It sets the public URL so links and citations are correct. No DNS or certificate work happens here; your proxy handles that.
DATALUMO_HTTP_PORT is the loopback port to publish (default 8080). Set a unique one per instance.
Datalumo is then reachable at http://127.0.0.1:8080, on loopback only, never exposed to the internet directly. Point your proxy there.
Every proxy needs to do two things. Forward X-Forwarded-For and X-Forwarded-Proto, so Datalumo sees the real visitor IP for rate limiting. And turn off response buffering on the chat path, because chat streams over SSE and a buffering proxy makes answers arrive all at once or hang.
Caddy is simplest. It sets the forwarded headers and streams correctly on its own:
ai.example.com {
reverse_proxy 127.0.0.1:8080
}
nginx:
server {
listen 443 ssl;
server_name ai.example.com;
ssl_certificate /etc/letsencrypt/live/ai.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/ai.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_buffering off; # SSE chat streaming
proxy_read_timeout 3600s; # long-lived chat connections
}
}
Traefik sets the forwarded headers by default:
http:
routers:
datalumo:
rule: "Host(`ai.example.com`)"
service: datalumo
tls: { certResolver: le }
services:
datalumo:
loadBalancer:
servers:
- url: "http://127.0.0.1:8080"
Apache (mod_proxy, mod_proxy_http, mod_headers):
<VirtualHost *:443>
ServerName ai.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/ai.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/ai.example.com/privkey.pem
ProxyPreserveHost On
RequestHeader set X-Forwarded-Proto "https"
ProxyPass / http://127.0.0.1:8080/ flushpackets=on
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
Datalumo reads the visitor IP from the rightmost entries of X-Forwarded-For, the part your own proxies add, so a client can't spoof it. With one proxy, the default, there's nothing to set. If you have a chain, say a cloud load balancer in front of nginx in front of Datalumo, tell it how many hops to trust:
DATALUMO_TRUSTED_PROXY_HOPS=2
If per-IP rate limits ever feel wrong behind a proxy, this is almost always the cause.
Behind-proxy mode makes this easy. Give each instance its own port and install directory, then route by hostname in your single proxy:
DATALUMO_BEHIND_PROXY=1 DATALUMO_HTTP_PORT=8080 DATALUMO_INSTALL_DIR=~/dl-clientA DOMAIN=a.example.com bash install.sh
DATALUMO_BEHIND_PROXY=1 DATALUMO_HTTP_PORT=8081 DATALUMO_INSTALL_DIR=~/dl-clientB DOMAIN=b.example.com bash install.sh
a.example.com -> 127.0.0.1:8080
b.example.com -> 127.0.0.1:8081
Each instance is fully separate: its own data, admin key, and config. Use the SQLite database to keep each one light, or point them at a shared Postgres with a database per instance. Each instance needs its own license.
Behind-proxy mode is chosen at install time. If you run docker compose yourself, set COMPOSE_PROFILES=caddy to use the bundled Caddy, or leave it unset for behind-proxy mode (Caddy stays off and the loopback port serves your proxy).