Reverse proxies and multiple instances

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.

Install behind your proxy

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.

Configure your proxy

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>

Trusted proxy hops

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.

Several instances on one server

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.

Running compose by hand

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).