Security considerations for deploying obox in production.
obox exposes AI inference and container management over the network. Key risks:
- Unauthorized API access — attackers using your LLMs and consuming GPU/CPU resources
- Data exposure — chat history and uploaded documents in Open WebUI
- Container escape via Portainer — full Docker control if Portainer is compromised
- Model poisoning — malicious models affecting inference output
- Set
WEBUI_ENABLE_SIGNUP=falsein production - Use strong admin credentials
- Create individual accounts for each user rather than sharing credentials
- Enable OAuth/SSO if available in your Open WebUI version (Settings → Authentication)
- Always set a strong
LITELLM_MASTER_KEY(32+ random characters) - Never commit
.envto version control - Rotate the key periodically
- Use per-client keys if you add multiple consumers (configure in
config/litellm/config.yaml)
- Create a strong admin password on first login
- Do not expose Portainer to the public internet without additional protection
- Consider restricting
/portainerto internal IPs via firewall
Only expose ports 80 and 443:
sudo ufw default deny incoming
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableAdd IP allowlisting in the Caddyfile for production:
handle /portainer/* {
@blocked not remote_ip 10.0.0.0/8 192.168.0.0/16 YOUR_OFFICE_IP
respond @blocked "Forbidden" 403
uri strip_prefix /portainer
reverse_proxy portainer:9000
}
Apply the same pattern to /grafana — it exposes infrastructure metrics and should not be public without strong authentication.
For maximum security, do not expose obox publicly. Instead:
- Run obox on a private network
- Access via VPN (WireGuard, Tailscale) or SSH tunnel
- Use
OBOX_DEV=truewith SSH port forwarding
Caddy handles TLS automatically when OBOX_DOMAIN is a real domain. Ensure:
- DNS A record points to your server
- Port 80 is open (required for ACME HTTP-01 challenge)
OBOX_ACME_EMAILis set for certificate expiry notifications
For custom certificates, mount them into the Caddy container and update the Caddyfile.
# WebUI secret key
openssl rand -hex 32
# LiteLLM API key
echo "sk-obox-$(openssl rand -hex 16)"
# Strong password
openssl rand -base64 24- Commit
.envto git (it is in.gitignore) - Use default passwords from
.env.example - Share API keys in chat logs or issue trackers
- Expose Ollama port (11434) directly to the internet
The current images run as root inside containers. For hardened deployments, consider:
- Custom Dockerfiles with non-root users
- Read-only root filesystems where supported
- Dropping unnecessary capabilities
Portainer mounts /var/run/docker.sock, which grants full Docker control. This is necessary for Portainer's functionality but represents a significant privilege. Mitigations:
- Restrict Portainer access (see above)
- Use Portainer's RBAC to limit what team members can do
- Consider Portainer Agent on a separate management host
The compose file uses :latest tags for convenience. For production, pin to specific versions:
image: ollama/ollama:0.5.4
image: ghcr.io/open-webui/open-webui:v0.5.4
image: portainer/portainer-ce:2.21.4- Only pull models from trusted sources (Ollama library)
- Verify model checksums when possible
- Be aware that LLM outputs can contain hallucinations — do not use for safety-critical decisions without verification
- Uploaded documents in Open WebUI are stored in the
webui_datavolume — encrypt at rest if handling sensitive data
# Failed auth attempts in Open WebUI
docker compose logs open-webui | grep -i "auth\|login\|fail"
# API usage in LiteLLM
docker compose logs litellm | grep -i "error\|unauthorized"Use Grafana (/grafana) or Portainer's built-in stats for:
- CPU and memory usage per container
- GPU utilization (with GPU mode enabled)
- API request rates via LiteLLM
/metrics - Service uptime via blackbox HTTP probes
- Disk usage (model weights grow over time)
See monitoring.md for dashboards, alerts, and PromQL queries.
If you suspect compromise:
- Rotate all secrets in
.env(API keys, passwords, secret keys) - Restart the stack:
./scripts/stop.sh && ./scripts/setup.sh - Review Portainer audit logs
- Check for unauthorized models:
docker exec obox-ollama ollama list - Review Open WebUI user accounts
Before going to production:
-
.envconfigured with unique secrets (not defaults) -
WEBUI_ENABLE_SIGNUP=false - Strong admin passwords set
- DNS and TLS working
- Firewall allows only 80, 443, and SSH
- Portainer access restricted or VPN-only
- Grafana admin password set (not default)
-
GRAFANA_ROOT_URLmatches production URL - Image versions pinned
- Backups configured for
webui_dataandollama_datavolumes - Monitoring in place