Private Remote Deployment
This pattern runs Libre WebUI, Ollama, Cloudflare Tunnel, and Watchtower on one Docker host without publishing the application or Ollama ports. Cloudflare Access is the outer identity boundary; Libre WebUI authentication remains the inner boundary.
Use deploy/private/docker-compose.yml
as the starting point. It defaults to the main image:
LIBRE_WEBUI_IMAGE=ghcr.io/libre-webui/libre-webui:main
The dev tag is suitable for an explicitly opted-in development instance, not
the client default.
Security model
- Cloudflare Access protects the entire hostname, including
/api/*and WebSocket upgrades. Do not add public bypass paths. - Libre WebUI requires a current account for application APIs. Model lifecycle and Work operations require the current database role to be administrator.
- The application, Ollama, and cloudflared only use a private Compose network. The host publishes no application ports.
- The app runs non-root with a read-only root filesystem, no Linux capabilities, no-new-privileges, and CPU, memory, and PID limits.
- Work containers add their own read-only root filesystem, capability drop, resource limits, workspace volume, and default-deny network policy.
The Docker socket remains the largest trust boundary. Anyone who compromises a process that can issue arbitrary Docker API calls can control the host. Remove the socket mount to disable Work when a client does not need it. A read-only socket mount does not make Docker API access read-only.
Bootstrap
- Create a non-root sudo operator and verify key-based SSH login before disabling root SSH.
- Copy
deploy/private/.env.exampleto/opt/libre-webui/.env, set mode0600, and generate unique secrets. - Set
DOCKER_GIDto the numeric group that owns/var/run/docker.sock. - Store the Cloudflare tunnel token in
/opt/libre-webui/secrets/tunnel-tokenwith mode0640or stricter. - Create a Cloudflare Access self-hosted application for the complete hostname and allow only the intended identities. Enable Protect with Access on the tunnel route.
- Set
ENABLE_SIGNUP=trueonly while the Access allowlist protects the initial administrator setup. After the administrator exists, set it tofalseand recreate the app container. Disabled registration is absolute, including on an empty database. - Configure Turnstile hostname restrictions and set
TURNSTILE_EXPECTED_HOSTNAMEto the exact public hostname.
Start and verify:
cd /opt/libre-webui
docker compose config --quiet
docker compose up -d
docker compose ps
curl --fail --silent --show-error \
https://your-hostname.example/api/auth/system-info
An unauthenticated request to a protected application API must return 401:
curl --output /dev/null --write-out '%{http_code}\n' \
https://your-hostname.example/api/work/tasks
Host hardening
The directory includes an sshd drop-in and fail2ban jail. Before applying the
sshd drop-in, verify a separate non-root sudo session in another terminal. Test
configuration with sshd -t before reloading SSH.
Use UFW (or an equivalent firewall) to default-deny inbound traffic and permit only rate-limited SSH. Docker publishes no service ports in this template:
ufw default deny incoming
ufw default allow outgoing
ufw limit OpenSSH
ufw enable
Keep unattended security upgrades enabled. Disable X11, agent, and TCP forwarding unless the deployment has a documented need for them.
Backups and recovery
Install the provided backup script and systemd units, then enable the timer:
install -m 0750 deploy/private/libre-webui-backup \
/usr/local/sbin/libre-webui-backup
install -m 0644 deploy/private/libre-webui-backup.{service,timer} \
/etc/systemd/system/
systemctl daemon-reload
systemctl enable --now libre-webui-backup.timer
The backup briefly stops the application to produce a consistent data-volume
archive. Copy archives off-host using encrypted storage and regularly test a
restore. Back up .env separately in a secrets manager; never place it inside
an unencrypted archive. Ollama models can be pulled again. Work task volumes
need a separate retention policy when their content matters.
Updates
Watchtower checks labelled application and Ollama images every 30 minutes. A
client deployment follows main; a deliberately experimental instance may
override LIBRE_WEBUI_IMAGE with :dev. Keep health checks and an off-host
backup so a bad update can be rolled back to a previously recorded digest.