Skip to main content

Configuring Traefik as a Modern Reverse Proxy for Odoo

As modern web applications become increasingly containerized, managing traffic across multiple services, domains, and versions can quickly spiral out of control. That’s where Traefik, a dynamic reverse proxy and load balancer, becomes a game-changer.

Over the past few weeks, I’ve been working on deploying multi-tenant Odoo environments (v15 to v18) using Docker. I needed a solution that could:

  • Dynamically route traffic based on subdomains
  • Handle HTTPS via Let’s Encrypt automatically
  • Support longpolling and websocket traffic (used by Odoo's real-time features)
  • Scale with minimal maintenance

The answer? Traefik.

What is Traefik?

Traefik (pronounced traffic) is a modern reverse proxy and load balancer designed for microservices. It automatically discovers services via Docker, Kubernetes, Consul, and more, and configures itself on the fly.

Unlike Nginx or HAProxy, Traefik is truly dynamic — it adapts to your containers and services as they spin up or down. It also integrates tightly with Let’s Encrypt for automatic TLS/SSL.

Case: Odoo Instances and Clients

Imagine this scenario: you manage multiple clients, each using different versions of Odoo (e.g., 15, 16, 17, 18). You need to ensure high availability and secure access for all of them. While Nginx is a solid reverse proxy, Traefik offers a more dynamic, automated approach that can significantly reduce configuration time and ongoing maintenance.

So lets start with the basic configuration!

Folder Structure

Organize your project like this:

project-root/
├── traefik.yml
├── acme.json              ← should exist and have chmod 600
└── dynamic/
    ├── dynamic_routes.yml
    └── middlewares.yml

Create acme.json before you start:

touch acme.json
chmod 600 acme.json

traefik.yml

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt
        options: default

providers:
  docker:
    exposedByDefault: false
    network: odoo-router-net
  file:
    directory: /etc/traefik/dynamic/
    watch: true

certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web

Breakdown of traefik.yml

This file defines the static configuration of Traefik — the part that sets up how Traefik listens for requests, discovers services, handles SSL certificates, and loads dynamic config files.

entryPoints
entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  • web listens on port 80 (HTTP).
  • It automatically redirects all HTTP requests to HTTPS, ensuring secure connections by default.
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt
        options: default
  • websecure listens on port 443 (HTTPS).
  • TLS is enabled using Let’s Encrypt and a default security profile.
  • This is where secure traffic lands, including browser requests to your Odoo sites.
providers
providers:
  docker:
    exposedByDefault: false
    network: odoo-router-net
  • Docker provider automatically detects containers and routes based on their labels.
  • exposedByDefault: false means services must opt-in via labels, increasing security.
  • network: odoo-router-net ensures Traefik can reach services in that Docker network.
  file:
    directory: /etc/traefik/dynamic/
    watch: true
  • The file provider loads routing rules and middleware from dynamic .yml files in /etc/traefik/dynamic/.
  • watch: true makes Traefik reload config automatically when you edit those files (e.g. dynamic_routes.yml, middlewares.yml).
certificatesResolvers:
  letsencrypt:
    acme:
      email: [email protected]
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web
  • Sets up Let's Encrypt integration.
  • Automatically requests and renews SSL certificates for your domains.
  • Certificates are saved to acme.json (should be chmod 600).
  • Uses the HTTP challenge to verify domain ownership (Traefik listens on port 80 to complete the verification).
TLS Options

You reference options: default, so you should also define:

tls:
  options:
    default:
      minVersion: VersionTLS12
      cipherSuites:
        - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305

This enforces modern, secure encryption protocols and disables older TLS versions (like TLS 1.0/1.1), reducing risk from outdated clients.

dynamic/dynamic_routes.yml

http:
  routers:
    client1-http:
      rule: "Host(`client1.example.com`)"
      service: odoo-v15-http
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
      middlewares:
        - secure-headers
        - rate-limit

    client1-longpolling:
      rule: "Host(`client1.example.com`) && PathPrefix(`/longpolling`)"
      service: odoo-v15-longpolling
      entryPoints:
        - websecure
      tls:
        certResolver: letsencrypt
      middlewares:
        - secure-headers
        - rate-limit
  services:
    odoo-v15-http:
      loadBalancer:
        servers:
          - url: "http://odoo15:8069"

    odoo-v15-longpolling:
      loadBalancer:
        servers:
          - url: "http://odoo15:8072"

dynamic_routes.yml: Dynamic Routing Configuration

This file tells Traefik how to match incoming requests to services running in your Docker environment. It defines:

  • Routers: Match incoming requests based on domain and path
  • Services: Define where those requests should be forwarded (to which container + port)
  • Middlewares: Apply security headers, rate limits, etc.
routers Section
client1-http:
  rule: "Host(`client1.example.com`)"

This router catches HTTPS requests to client1.example.com and forwards them to odoo-v15-http.

client1-longpolling:
  rule: "Host(`client1.example.com`) && PathPrefix(`/longpolling`)"

This router specifically handles Odoo’s real-time longpolling requests (used for chat, live updates). This ensures that /longpolling is routed to port 8072, which Odoo uses for push communication.

entryPoints:
  - websecure

Specifies that both routers accept HTTPS traffic.

tls:
  certResolver: letsencrypt

Tells Traefik to automatically issue and manage SSL certificates for this domain using Let's Encrypt.

middlewares:
  - secure-headers
  - rate-limit

Adds security headers and request throttling for each route.

services Section
odoo-v15-http:
  loadBalancer:
    servers:
      - url: "http://odoo15:8069"

Routes the main UI/API traffic to Odoo’s port 8069.

odoo-v15-longpolling:
  loadBalancer:
    servers:
      - url: "http://odoo15:8072"

Routes longpolling (bus) requests to port 8072 — essential for features like live chat, POS sync, or notifications.

Why Separate Routers?

  • It improves performance and clarity by letting Traefik route longpolling traffic separately.
  • Enables better control: you could assign different middleware, rate limits, or timeouts for /longpolling.
http:
  middlewares:
    secure-headers:
      headers:
        browserXssFilter: true
        contentTypeNosniff: true
        frameDeny: true
        sslRedirect: true
        stsIncludeSubdomains: true
        stsPreload: true
        stsSeconds: 31536000
        referrerPolicy: "strict-origin-when-cross-origin"
        customResponseHeaders:
          X-Robots-Tag: "none"
          X-Permitted-Cross-Domain-Policies: "none"

    rate-limit:
      rateLimit:
        average: 100
        burst: 50
  • middlewares.yml: Adding Security amp; Traffic Control
  • This file defines middleware rules that Traefik applies to incoming HTTP requests. Middleware allows you to add features like security headers, rate limiting, authentication, and more — without modifying your app code.
secure-headers Middleware
secure-headers:
  headers:
    browserXssFilter: true
    contentTypeNosniff: true
    frameDeny: true
    sslRedirect: true
    stsIncludeSubdomains: true
    stsPreload: true
    stsSeconds: 31536000
    referrerPolicy: "strict-origin-when-cross-origin"
    customResponseHeaders:
      X-Robots-Tag: "none"
      X-Permitted-Cross-Domain-Policies: "none"

This adds a suite of HTTP headers to improve security:

  • XSS protection: Helps block malicious scripts
  • Clickjacking prevention: Denies rendering inside iframes (frameDeny)
  • Content sniffing protection: Prevents MIME-type confusion
  • HSTS: Enforces HTTPS via Strict-Transport-Security
  • Referrer policy: Controls what info gets sent in the Referer header

These headers are especially important when exposing admin panels or user portals.

rate-limit Middleware
rate-limit:
  rateLimit:
    average: 100
    burst: 50

This throttles requests:

  • Allows 100 requests per second on average
  • Bursts up to 50 extra requests during short spikes
  • It’s a simple way to prevent abuse, reduce load, and mitigate brute-force or DDoS-style attempts.
Applying Middleware

In your dynamic_routes.yml, reference these like so:

middlewares:
  - secure-headers
  - rate-limit

This way, all requests go through your defined middleware before reaching Odoo.

Running Traefik with Docker Compose Once you’ve defined your traefik.yml, dynamic_routes.yml, and middlewares.yml, the final step is to bring Traefik to life using Docker Compose. This step launches Traefik and connects it to your Docker network and dynamic config files.

The docker-compose.yml for Traefik
services:
  traefik:
    image: traefik:v3
    container_name: traefik
    restart: unless-stopped
    command:
      - --configFile=/etc/traefik/traefik.yml
    ports:
      - "80:80"       # HTTP
      - "443:443"     # HTTPS
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./acme.json:/etc/traefik/acme.json
      - ./dynamic:/etc/traefik/dynamic:ro
    networks:
      - odoo-router-net

networks:
  odoo-router-net:
    external: true
Running Traefik

Once everything is in place, simply run:

docker compose up -d

This command:

  • Starts Traefik
  • Watches your Docker containers (if configured with labels)
  • Loads routing rules and middleware from the /dynamic folder
  • Automatically issues and renews HTTPS certificates via Let’s Encrypt

Conclusions

I've always enjoyed managing bare-metal servers and exploring ways to automate and streamline my workflow — whether that's writing scripts or using graphical tools when it makes sense. In that journey, I discovered Traefik as a powerful alternative to traditional proxies like Nginx.

What stood out to me most is how maintainable and automation-friendly Traefik is. With dynamic routing, built-in Let's Encrypt integration, and seamless Docker support, it significantly reduces the manual overhead of managing reverse proxies in complex multi-service environments — like multi-version Odoo deployments.