Reverse proxy & TLS¶
Axium does not terminate TLS. In anything beyond a laptop, put a reverse proxy or a load balancer in front of both the core and the ui and let it hold the certificates.
What the core needs from the proxy¶
| Setting | Value | Why |
|---|---|---|
TRUST_PROXY |
true |
So the core reads the real client IP and protocol from X-Forwarded-For / X-Forwarded-Proto. Without it the firewall policy and the client_ip trace column see the proxy's address. |
PUBLIC_BASE_URL |
https://axium-api.your-domain.com |
Only matters for Gemini resumable uploads; otherwise the upload URL handed to the client would be http://. |
| Request body size | ≥ MAX_UPLOAD_SIZE (default 50 MB) |
Otherwise the proxy rejects uploads before Axium sees them. |
| Read/response timeout | several minutes | LLM generations can take minutes; streaming keeps the connection open for the whole answer. |
| Response buffering | off | Streaming (text/event-stream) must reach the client chunk by chunk. The core already sends X-Accel-Buffering: no and Cache-Control: no-cache. |
| HTTP/1.1 keep-alive to upstream | on | Cheaper connections, correct SSE behaviour. |
The ui needs nothing special; it serves static files. What matters is that
its API_URL is the public HTTPS URL of the core, see
Web console.
Example: nginx¶
server {
listen 443 ssl http2;
server_name axium-api.your-domain.com;
ssl_certificate /etc/ssl/axium/fullchain.pem;
ssl_certificate_key /etc/ssl/axium/privkey.pem;
client_max_body_size 50m; # match MAX_UPLOAD_SIZE
location / {
proxy_pass http://axium-core:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_buffering off; # streaming
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
# Lock the installer down once installed (see Security)
location /v1/install {
allow 10.0.0.0/8;
deny all;
proxy_pass http://axium-core:3000;
}
}
server {
listen 443 ssl http2;
server_name axium.your-domain.com;
ssl_certificate /etc/ssl/axium/fullchain.pem;
ssl_certificate_key /etc/ssl/axium/privkey.pem;
location / { proxy_pass http://axium-ui:3001; }
}
Example: Caddy¶
axium-api.your-domain.com {
request_body { max_size 50MB }
reverse_proxy axium-core:3000 {
flush_interval -1 # streaming
transport http { response_header_timeout 10m }
}
}
axium.your-domain.com {
reverse_proxy axium-ui:3001
}
Caddy sets X-Forwarded-For and X-Forwarded-Proto by default and obtains
certificates automatically.
Example: Traefik (Docker labels)¶
services:
core:
image: quay.io/axiumlab/core:v0.1.0
environment:
TRUST_PROXY: "true"
PUBLIC_BASE_URL: https://axium-api.your-domain.com
labels:
traefik.enable: "true"
traefik.http.routers.axium-core.rule: Host(`axium-api.your-domain.com`)
traefik.http.routers.axium-core.tls.certresolver: letsencrypt
traefik.http.services.axium-core.loadbalancer.server.port: "3000"
traefik.http.middlewares.axium-body.buffering.maxRequestBodyBytes: "52428800"
traefik.http.routers.axium-core.middlewares: axium-body
Traefik does not buffer responses by default, which is what streaming needs.
Cloud Run¶
The Google Cloud installer already sets TRUST_PROXY=true because Cloud Run
terminates TLS and always rewrites the X-Forwarded-* headers. Cloud Run's
request timeout defaults to 5 minutes; raise it with
gcloud run services update axium-core --timeout=900 if you expect longer
generations.
Mixed content¶
If the ui is served over https://, API_URL must be https:// too. The
browser blocks an http:// API from an https:// page and the console shows
a generic network error. See Web console.