In development, the Streamlit frontend (port 8501) and FastAPI backend (port 8080) are accessed directly. In production you almost always want a single public-facing domain, HTTPS termination, and correct handling of Streamlit’s persistent WebSocket connection — all of which Nginx handles cleanly as a reverse proxy. This guide walks through copying the bundled config template, customising it for your domain and ports, and verifying the deployment.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/EllisYuan/ChatAgents/llms.txt
Use this file to discover all available pages before exploring further.
Prerequisites
- Docker containers already running (see the Docker deployment guide)
- Nginx installed on the host (
sudo apt install nginxon Ubuntu/Debian) - A domain name with its DNS
Arecord pointing to the server’s IP address - An SSL certificate — Let’s Encrypt / Certbot is recommended for free automated certificates
Setup
Start the Docker containers
Build images and start both services in detached mode:Confirm both containers are healthy before proceeding:
Copy the Nginx config template
The repository ships a ready-to-use example at BaoTa Panel users — copy to the panel’s vhost directory instead:
docs/nginx.conf.example:Edit the config
Open the file and update the three required values:
If you changed
| Placeholder | Replace with |
|---|---|
your-domain.com | Your actual domain name |
/path/to/fullchain.pem | Path to your SSL certificate |
/path/to/privkey.pem | Path to your SSL private key |
BACKEND_PORT or FRONTEND_PORT in .env (defaults are 8080 / 8501), update the proxy_pass port numbers in every location block accordingly.Enable the config
Create a symlink so Nginx loads the config on startup:BaoTa Panel users can skip this step — the panel manages symlinks automatically.
Test and reload Nginx
Always test the config syntax before reloading to avoid downtime:A successful test outputs:
Nginx configuration explained
The bundleddocs/nginx.conf.example contains three groups of location blocks. Here is the full file for reference:
| Location block | Proxies to | Purpose |
|---|---|---|
/health | FastAPI port 8080 | Backend health check used by Docker and monitoring |
/api/ | FastAPI port 8080 | All session management REST endpoints |
/stream_agent | FastAPI port 8080 | Server-Sent Events streaming with buffering disabled |
/_stcore/stream | Streamlit port 8501 | Streamlit WebSocket — requires HTTP upgrade headers |
/_stcore/health | Streamlit port 8501 | Streamlit internal health check |
/ | Streamlit port 8501 | All other Streamlit traffic (pages, static assets) |
WebSocket configuration
Streamlit uses a persistent WebSocket connection on/_stcore/stream to push UI updates to the browser. Without the upgrade headers, the browser falls back to polling and the page either hangs or shows a connection error.
The required location block — taken directly from docs/nginx.conf.example:
proxy_http_version 1.1— required; HTTP/1.0 does not support persistent connections.Upgrade $http_upgrade+Connection "upgrade"— signals to Nginx to pass the WebSocket handshake through to Streamlit.proxy_read_timeout 86400— 24-hour timeout keeps the WebSocket open for long-lived browser sessions.
Critical: proxy_pass trailing slash
The most common misconfiguration when setting up the/api/ route is adding a trailing slash to proxy_pass. Nginx strips the matched prefix when a trailing slash is present, breaking every API path.
Streaming response timeout
ChatAgents streams long agent responses over SSE via/stream_agent. The default Nginx proxy_read_timeout is 60 seconds, which is too short for deep-thinking mode queries that may run for several minutes.
The bundled config already sets this to 3600s for the /stream_agent location. If you are customising the config, ensure the streaming location includes:
300 (5 minutes) is a reasonable minimum.
Verification commands
If you use non-default ports by setting
BACKEND_PORT and FRONTEND_PORT in .env, update the proxy_pass target port numbers in chatbot.conf to match. For example, if BACKEND_PORT=9080 then every proxy_pass http://127.0.0.1:8080 for backend routes becomes proxy_pass http://127.0.0.1:9080.