Baremetal

All about baremetal configuration and tweaks!

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:

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: admin@example.com
      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
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: letsencrypt
        options: default
providers
providers:
  docker:
    exposedByDefault: false
    network: odoo-router-net
  file:
    directory: /etc/traefik/dynamic/
    watch: true
certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /etc/traefik/acme.json
      httpChallenge:
        entryPoint: web
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 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?

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
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:

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:

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:

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.