Skip to main content

Reverse Proxy

A reverse proxy should be placed in front of the OpenVLE backend and frontend. It handles SSL termination, routes requests to the correct services, and can optionally provide load balancing.

Prerequisites

  • Three services must be accessible through the reverse proxy:
    • Backend API (Port 8000) — Path /v1/
    • Frontend (Port 80) — all other paths
    • Apache Guacamole (Port 8080) — separate subdomain (e.g., desktop.example.com) (optional, only if Guacamole should be accessible through this reverse proxy)
  • Two hostnames must be configured (e.g., openvle.example.com and desktop.example.com) — without Guacamole, a single hostname is sufficient
  • SSL certificates for both hostnames (e.g., via Let's Encrypt)
  • WebSocket support for Guacamole (required for remote desktop connections) (only when connecting Guacamole)
Guacamole optional

The Guacamole configuration in the following examples is optional. If you are not using Guacamole or are running Guacamole behind its own reverse proxy on a separate server, you can omit the Guacamole-related sections (subdomain desktop.example.com, port 8080).

Configuration

We recommend Traefik, as it runs natively as a Docker container and can be dynamically configured via Docker labels — ideal for a Docker Compose-based setup like OpenVLE. Alternatively, HAProxy, Caddy, Nginx, or Apache are also possible.

Traefik can be operated as an additional service directly within the Docker Compose stack. Configuration is done via labels on individual services — no separate configuration files are needed.

note

This is a docker-compose.override.yml — the actual docker-compose.yml for the backend and frontend is still required.

---
services:

traefik:
image: traefik:v3.1
container_name: traefik
restart: unless-stopped
ports:
- 0.0.0.0:80:80
- 0.0.0.0:443:443
command:
# Provider
- --providers.docker=true
- --providers.docker.exposedbydefault=false
# EntryPoints
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# HTTP → HTTPS Redirect
- --entrypoints.web.http.redirections.entrypoint.to=websecure
- --entrypoints.web.http.redirections.entrypoint.scheme=https
# ACME (Let's Encrypt)
- --certificatesresolvers.letsencrypt.acme.email=admin@example.com
- --certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json
- --certificatesresolvers.letsencrypt.acme.httpchallenge=true
- --certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./certs:/letsencrypt
labels:
- traefik.enable=true

backend:
labels:
- traefik.enable=true
- traefik.http.routers.backend.rule=Host(`openvle.example.com`) && PathPrefix(`/v1/`)
- traefik.http.routers.backend.entrypoints=websecure
- traefik.http.routers.backend.tls=true
- traefik.http.routers.backend.tls.certresolver=letsencrypt
- traefik.http.routers.backend.priority=200
- traefik.http.services.backend.loadbalancer.server.port=8000

frontend:
labels:
- traefik.enable=true
- traefik.http.routers.frontend.rule=Host(`openvle.example.com`) && PathPrefix(`/`)
- traefik.http.routers.frontend.entrypoints=websecure
- traefik.http.routers.frontend.tls=true
- traefik.http.routers.frontend.tls.certresolver=letsencrypt
- traefik.http.routers.frontend.priority=100
- traefik.http.services.frontend.loadbalancer.server.port=80

# Optional: Only required if Guacamole should be accessible through this reverse proxy
guacamole:
labels:
- traefik.enable=true
- traefik.http.routers.guacamole.rule=Host(`desktop.example.com`)
- traefik.http.routers.guacamole.entrypoints=websecure
- traefik.http.routers.guacamole.tls=true
- traefik.http.routers.guacamole.tls.certresolver=letsencrypt
- traefik.http.services.guacamole.loadbalancer.server.port=8080
Adjustments
  • Replace openvle.example.com and desktop.example.com with your hostnames
  • Replace admin@example.com with your email address for Let's Encrypt
  • The priority of the backend router must be higher than the frontend's, so that /v1/ requests are routed correctly

Verification

After configuring the reverse proxy:

  1. Check HTTPS accesshttps://openvle.example.com should display the frontend
  2. Check API accesshttps://openvle.example.com/v1/system/settings should return a JSON response with the current system settings. An empty or erroneous response indicates an incorrect routing configuration.
  3. Check Guacamole access (if configured)https://desktop.example.com should display the Guacamole login page
  4. Check HTTP redirecthttp://openvle.example.com should redirect to HTTPS

Troubleshooting

ProblemSolution
SSL certificate errorMake sure the certificate paths are correctly configured and the certificates are valid. For Let's Encrypt: check whether port 80 is reachable from the outside.
502 Bad GatewayThe target service is not reachable. Check whether the containers are running (docker ps -a) and the IP addresses/ports in the proxy configuration are correct.
WebSocket error with GuacamoleMake sure the WebSocket headers (Upgrade, Connection) are set in the proxy configuration (see Nginx example).
Frontend reachable, API calls failCheck whether the routing rules for /v1/ correctly forward to the backend and not to the frontend. For Traefik: the priority of the backend router must be higher than the frontend's.