Sticky sessions in Traefik, often referred to as cookie-based affinity, isn’t about keeping a user on the same server forever; it’s about ensuring a user’s requests consistently hit the same instance of your application backend during a single session. This is crucial for stateful applications where losing session data between requests can break functionality.
Let’s see this in action. Imagine a simple backend service whoami that just echoes back its request details, including the hostname of the container it’s running on.
# docker-compose.yml
version: '3.7'
services:
traefik:
image: traefik:v2.10
ports:
- "80:80"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./traefik.yml:/etc/traefik/traefik.yml:ro
- ./dynamic_conf/:/etc/traefik/dynamic_conf/
whoami:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.routers.whoami.service=whoami"
- "traefik.http.routers.whoami.middlewares=sticky-sessions" # Apply sticky session middleware
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.name=my_app_session" # Define cookie name
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.secure=false" # For local testing
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.httponly=true" # Recommended for security
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.maxage=3600" # Cookie lifetime in seconds
whoami-replica:
image: traefik/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami-replica.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami-replica.entrypoints=web"
- "traefik.http.services.whoami-replica.loadbalancer.server.port=80"
- "traefik.http.routers.whoami-replica.service=whoami-replica"
- "traefik.http.routers.whoami-replica.middlewares=sticky-sessions" # Apply sticky session middleware
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.name=my_app_session" # Define cookie name
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.secure=false"
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.httponly=true"
- "traefik.http.middlewares.sticky-sessions.sticky.cookie.maxage=3600"
# traefik.yml
api:
dashboard: true
insecure: true # For local testing, remove in production
log:
level: INFO
entryPoints:
web:
address: ":80"
When you run docker-compose up -d and then access http://whoami.localhost (you might need to add 127.0.0.1 whoami.localhost to your /etc/hosts file), you’ll notice that the first request might hit one whoami container, and subsequent requests, as long as the my_app_session cookie is present and valid in your browser, will consistently hit that same container. If you clear your browser’s cookies for that domain and refresh, you might be sent to the other whoami instance.
The core problem sticky sessions solve is maintaining application state without relying on a shared, external state store for every request. Without it, if your application stores user session data in memory on the backend server, a user could be bounced between servers, each with its own independent memory, leading to lost sessions, logged-out users, or corrupted application states. Traefik’s sticky sessions tackle this by instructing the load balancer to inspect incoming requests for a specific cookie. If the cookie exists and is valid, Traefik directs the request to the backend server that previously set that cookie. If the cookie is absent or expired, Traefik will pick a backend server (using its standard load balancing algorithm) and instruct it to set the cookie in the response.
The configuration is straightforward. You define a router (whoami.routers.whoami) and then associate a middleware (sticky-sessions) with it. This middleware, defined under traefik.http.middlewares.sticky-sessions.sticky, is where the magic happens. You specify the cookie.name (e.g., my_app_session), which is the name of the HTTP cookie Traefik will use for affinity. cookie.secure=false is typically used for local development over HTTP; in production, you’d set this to true for HTTPS-only cookies. cookie.httponly=true is a security measure preventing client-side JavaScript from accessing the cookie. cookie.maxage defines how long the cookie remains valid in seconds (e.g., 3600 seconds = 1 hour).
When Traefik receives a request for whoami.localhost, it checks its routers. It finds the whoami router, which has the sticky-sessions middleware applied. Traefik then checks for the my_app_session cookie in the incoming request.
- Cookie Present and Valid: If the cookie is found and its associated backend server is still healthy, Traefik forwards the request directly to that server. The backend server receives the request and its associated session data.
- Cookie Absent or Invalid: If the cookie is missing, expired, or its target server is unhealthy, Traefik selects a healthy backend server using its configured load balancing strategy (round-robin by default). Traefik then adds a
Set-Cookieheader to its response, instructing the client’s browser to store themy_app_sessioncookie, mapping it to the chosen backend server. Subsequent requests from this client will then follow path 1.
The load balancer itself doesn’t store session state; it merely uses the cookie as a pointer. The actual session state is maintained by the backend application instance that the cookie directs the user to. This is why it’s critical that your application is designed to handle state locally or has a mechanism to synchronize state if it needs to be distributed.
A common pitfall is forgetting to apply the sticky session middleware to all routers that serve the same application or require session affinity. If you have multiple routers pointing to the same set of backend services, and only some have the sticky session middleware, users might still experience session loss when hitting a router that doesn’t enforce affinity. Ensure consistency across your routing configuration for sticky sessions to be effective.
Once you have sticky sessions working, the next natural step is exploring more advanced load balancing strategies within Traefik, such as least connections, or delving into health check configurations to ensure sticky sessions only direct traffic to healthy instances.