Skip to main content

Docker Compose Example for Redundancy and Failover with Traefik

The following example shows how Traefik can be configured in Docker Compose. This approach completely eliminates the need for separate configuration files. The required configurations are passed as command-line parameters or defined using labels.

The files used for this project can be found on the page HTTP(S) Failover Concepts for REST, OPC UA Servers, and Web Management in the OPC Router.

As with the Nginx example, this configuration applies only to external failover for the server endpoints. The actual redundancy of the OPC Router pair is configured independently within the OPC Router.

Both OPC Router containers are intentionally assigned the same router and middleware labels. This prevents them from competing with each other: Traefik groups labels with the same name into a single service (opcrouter-service) with two servers. The health check determines which of the two receives a request—it queries the path specified at healthcheck.path and removes the container that does not respond from the load balancing pool. This is exactly what failover is.

warning

The Traefik option --api.insecure=true is used in this example solely to make the dashboard easily accessible for testing and setup purposes. In production environments, the dashboard should not be published unprotected; instead, it should be secured or disabled.

warning

The password and pre-shared key are shown here as placeholders. Do not enter these values in plain text in a Compose file that will end up in version control—they apply to both instances and are the key to the redundancy connection. Instead, use the OPC Router’s Secrets Store. You can generate the pre-shared key by clicking the “Generate” button in the “Pre-Shared Key” section; see “Secure Redundancy.”

docker-compose.yaml
services:
traefik:
image: traefik:v3.0
restart: always
networks:
- opcrouter-net
# Configurations as Command Parameters
command:
- "--api.dashboard=true"
- "--api.insecure=true"
- "--entrypoints.web.address=:80"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--ping"
ports:
- "5080:80"
- "5081:8080" # Traefik Dashboard (optional)
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"

opcrouter-primary:
image: opcrouter/runtime:latest
restart: always
networks:
- opcrouter-net
ports:
- "5085:8080"
environment:
- OR_I_ACCEPT_EULA=true
- INITIAL_USERNAME=admin
- INITIAL_PASSWORD=<your-password>
- OR_REDUNDANCY_MODE=primary
- OR_REDUNDANCY_LOCAL_MANAGEMENT_ADDRESS=http://opcrouter-primary:8080
- OR_REDUNDANCY_SHARED_KEY=<your-pre-shared-key>
- OR_IMPORT_SOURCE=/inray/REST_Primary.rpe
volumes:
# Make the Project Available in a Container
- ../REST_Primary.rpe:/inray/REST_Primary.rpe
# Configurations via Labels
labels:
- "traefik.enable=true"
# Responds to the /api Prefix
- "traefik.http.routers.api-router.rule=PathPrefix(`/api`)"
- "traefik.http.routers.api-router.entrypoints=web"
- "traefik.http.routers.api-router.service=opcrouter-service"
- "traefik.http.routers.api-router.middlewares=api-rewrite"
# Replaces /api/ with /services/MyRestServer/api/
- "traefik.http.middlewares.api-rewrite.replacepathregex.regex=^/api/(.*)"
- "traefik.http.middlewares.api-rewrite.replacepathregex.replacement=/services/MyRestServer/api/$$1"
# Load Balancing Configuration
- "traefik.http.services.opcrouter-service.loadbalancer.server.port=8080"
# Check availability by sending requests to the specified path
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.path=/health/runtime/ready"
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.interval=5s"
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.timeout=2s"

opcrouter-secondary:
image: opcrouter/runtime:latest
restart: always
networks:
- opcrouter-net
ports:
- "5086:8080"
environment:
- OR_I_ACCEPT_EULA=true
- INITIAL_USERNAME=admin
- INITIAL_PASSWORD=<your-password>
- OR_REDUNDANCY_MODE=secondary
- OR_REDUNDANCY_SHARED_KEY=<your-pre-shared-key>
- OR_REDUNDANCY_ADDRESS=http://opcrouter-primary:8080
- OR_REDUNDANCY_LOCAL_MANAGEMENT_ADDRESS=http://opcrouter-secondary:8080
- OR_REDUNDANCY_PRIMARY_TIMEOUT=30
- OR_REDUNDANCY_SECONDARY_HEARTBEAT_INTERVAL=10
- OR_IMPORT_SOURCE=/inray/REST_Secondary.rpe
volumes:
# Make the project available in the container
- ../REST_Secondary.rpe:/inray/REST_Secondary.rpe
# Configurations via labels
labels:
- "traefik.enable=true"
# Responds to the /api prefix
- "traefik.http.routers.api-router.rule=PathPrefix(`/api`)"
- "traefik.http.routers.api-router.entrypoints=web"
- "traefik.http.routers.api-router.service=opcrouter-service"
- "traefik.http.routers.api-router.middlewares=api-rewrite"
# Replaces /api/ with /services/MyRestServer/api/
- "traefik.http.middlewares.api-rewrite.replacepathregex.regex=^/api/(.*)"
- "traefik.http.middlewares.api-rewrite.replacepathregex.replacement=/services/MyRestServer/api/$$1"
# Load Balancing Configuration
- "traefik.http.services.opcrouter-service.loadbalancer.server.port=8080"
# Check availability by sending requests to the specified path
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.path=/health/runtime/ready"
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.interval=5s"
- "traefik.http.services.opcrouter-service.loadbalancer.healthcheck.timeout=2s"

networks:
opcrouter-net: