traefik_service_retries_total is climbing for one of your services. Your dashboards show green: clients are getting 200s, the error rate looks flat, and /ping is happy. That is exactly what makes this signal dangerous. Retries mask backend instability from clients while multiplying the load Traefik sends to the backends. By the time client-facing errors appear, the amplification loop may already be running.
This guide covers how to read the retry signal, how to tell a harmless burst (rolling update, instance flapping) from the early stage of a cascade, and what to do before the loop feeds itself.
What this means
The retry middleware sits in Traefik’s middleware chain, between the router and the service load balancer. When a request to a backend fails at the connection level, the middleware re-sends it, possibly to a different backend server. The counter traefik_service_retries_total (label: service) increments per retry attempt.
Three things follow:
- The client may never know. A request that fails twice and succeeds on the third attempt returns 200. Your service-level 5xx rate stays low while the retry counter climbs. If you only alert on error rates, you are blind to this.
- Backend load is amplified. Each retry is a full new request to the backend pool. At 2 retries per request, Traefik is sending up to 3x the client traffic to your backends. If the backends are degraded, that extra load makes the degradation worse.
- Latency grows. Every retry adds a full request cycle. Rising retries and rising latency together are the signature of the amplification loop, not of harmless flapping.
By default, retries fire on connection-level failures only: no response at the TCP level. A backend that returns HTTP 500 has completed the request cycle from Traefik’s point of view and is not retried unless you explicitly configure status-based retries. This is the most common operator misunderstanding: if your backends are returning 500s and your retry counter is flat, that is expected behavior, not a broken metric.
flowchart TD A[Backend degrades: slow or dropping connections] --> B[Retry middleware re-sends failed requests] B --> C[Backend pool receives 2x-3x client traffic] C --> D[Remaining backends overload] D --> E[More timeouts and connection failures] E --> B D --> F[traefik_service_server_up declines] F --> G[503 to all clients]
Common causes
| Cause | What it looks like | First thing to check |
|---|---|---|
| Rolling update or deployment | Retries spike, latency stays flat, pattern matches pod/container churn | Correlate retry onset with deployment events; check traefik_service_server_up for flapping backends |
| Backend partial degradation | Retries rising AND latency rising together | traefik_service_request_duration_seconds p95/p99 for the service; backend CPU/memory/DB pool |
| Network instability between Traefik and backends | Intermittent retry bursts across multiple services at once | Connection-level errors in Traefik logs; TIME_WAIT/CLOSE_WAIT socket counts |
| Aggressive retry config | High retry-to-request ratio even at low error rates | Retry middleware attempts value; whether retries are applied to non-idempotent methods |
| Health checks passing on a dead path | traefik_service_server_up all 1, retries and 5xx still climbing | Compare health check path with the real traffic path; see the false-positive health check guide |
| Autoscaler scale-down | Backends removed mid-request, retries spike, then settle | Correlation with scale-down events; watch for repeats at each scale event |
The decisive split: high retries with flat latency is usually harmless flapping (rolling update, brief network blip). High retries with rising latency is danger. That combination means the retries are not absorbing the failures; they are feeding them.
Quick checks
All read-only. These assume Traefik’s Prometheus metrics endpoint is reachable (commonly on the Traefik dashboard/API port).
# Current retry counters per service
curl -s http://localhost:8080/metrics | grep traefik_service_retries_total
# Request totals per service (for computing the ratio yourself)
curl -s http://localhost:8080/metrics | grep traefik_service_requests_total
# Backend health per server (only present if health checks are enabled)
curl -s http://localhost:8080/metrics | grep traefik_service_server_up
# Latency histogram for the affected service
curl -s http://localhost:8080/metrics | grep traefik_service_request_duration_seconds
Two samples 60 seconds apart give you rates: subtract, divide by 60. Compute the retry-to-request ratio per service, not the raw retry count. A service doing 10,000 req/s with 200 retries/s (2%) is in a different universe from one doing 50 req/s with 10 retries/s (20%).
In the access log, the RetryAttempts field shows retries per request. A request that succeeded only after retries shows RetryAttempts > 0 with a 200 response code:
# Requests that needed retries, from JSON access logs
jq 'select(.RetryAttempts > 0) | {RequestPath, RetryAttempts, DownstreamStatus, time}' /var/log/traefik/access.log | tail -50
This assumes JSON access log format and that the file path matches your deployment; adjust accordingly.
How to diagnose it
Compute the retry-to-request ratio per service. Use
rate(traefik_service_retries_total[5m]) / rate(traefik_service_requests_total[5m])grouped by service. Under 1% is background noise. Over 5% sustained is a real problem. Approaching 100% means every request is being retried and you are in amplification territory.Check the latency pair. Pull p95/p99 from
traefik_service_request_duration_secondsfor the same service and window. Retries up + latency flat = flapping, likely a deployment. Retries up + latency up = the loop is forming.Check backend health. Look at
traefik_service_server_upper URL for the service. A declining count means the healthy pool is shrinking and remaining backends are absorbing original plus retried traffic. If all servers show 1 but retries and 5xx climb, your health checks are lying: the probe path works, the real path does not.Correlate with change. Did a deployment, scale event, or config reload (
traefik_config_reloads_totalincrementing) coincide with the retry onset? Rolling updates produce retry bursts that settle within minutes. A retry climb with no change event points at genuine backend or network degradation.Confirm what is actually failing. Default retries fire on connection-level failure, not HTTP 5xx. If retries are firing, backends are refusing connections, resetting them, or not responding at all. Check backend logs for restarts, OOM kills, connection pool exhaustion, or accept-queue drops. If your 5xx rate is high but retries are flat, the backends are answering with errors and the retry middleware is not involved; that is a different incident (see the 5xx error rate guide).
Check the retry config itself. How many
attemptsare configured? Is the middleware attached to routes carrying POST/PUT traffic? Retrying non-idempotent methods can cause duplicate side effects downstream, which is a correctness problem on top of the load problem.
Metrics and signals to monitor
| Signal | Why it matters | Warning sign |
|---|---|---|
traefik_service_retries_total (rate, per service) | Leading indicator of backend instability, visible before client-facing errors | Ratio to requests > 5% sustained 5m; > 50% means amplification |
| Retry-to-request ratio | Normalizes for traffic volume; raw counts mislead | Sustained climb without a deployment event |
traefik_service_request_duration_seconds | Distinguishes flapping from cascade when paired with retries | p95/p99 rising while retries rise |
traefik_service_server_up | Shows the healthy pool shrinking as the cascade progresses | Any URL at 0; count declining over time |
traefik_service_requests_total{code=~"5.."} | Confirms whether failures are surfacing to clients | 5xx rising alongside retries means retries are failing too |
RetryAttempts in access logs | Per-request ground truth on which paths absorb retries | Successful 200s with RetryAttempts > 0 clustering on one service |
traefik_config_reloads_total | Retry bursts that track config reloads point at churn, not backend failure | Retry spikes tightly correlated with reload increments |
Fixes
If it is a rolling update or known deployment
Do nothing to Traefik. Note the retry ceiling for the deployment so future alerts can suppress or tolerate that window. If the bursts are unreasonably large, the real fix is on the backend side: graceful shutdown (stop accepting, drain in-flight), startup/readiness probes that gate traffic until the app can actually serve, and health check intervals short enough that Traefik stops sending traffic to terminating backends quickly.
If the amplification loop is forming
- Reduce retry pressure first. Lower
attemptson the affected service’s retry middleware, or detach the middleware from the router, via dynamic configuration. Warning: this changes live routing behavior. It is a throttle, not a fix: it stops Traefik from multiplying load on your backend while you find the root cause. Tradeoff: clients now see the errors the retries were masking, so client-facing 5xx will rise. That is honest signal, and it is preferable to a total pool collapse. - Identify the degrading backend. Use
traefik_service_server_upper URL plus backend-side metrics: CPU, memory, DB connection pool, downstream dependency latency. - Fix the root cause at the backend. Roll back the bad deploy, scale the pool, clear the DB lock contention. Retries are never the root cause; they are the accelerant.
- Re-enable retries at a conservative level once the backend is stable.
If retries are flat but 5xx is high
The retry middleware is not firing because backends are answering with HTTP errors, not connection failures. On Traefik v3 you may be able to opt into status-based retries (for example retrying on 503), but do this carefully: retrying on 5xx against an overloaded backend makes amplification far worse. On v2, status-based retry does not exist; the middleware only supports attempts and initialInterval.
If health checks are lying
Fix the health check path so it exercises the same dependencies as real traffic, and cross-reference traefik_service_server_up against actual error rates from now on. A backend that passes /health while its database is down will absorb full traffic share, fail at the connection or application level, and drive retries against the rest of the pool.
Prevention
- Alert on the ratio, not the count. Something like
sum by (service) (rate(traefik_service_retries_total[5m])) / sum by (service) (rate(traefik_service_requests_total[5m])) > 0.1for 5 minutes catches instability early without paging on deployment noise. Tune the threshold per service; batch-heavy or WebSocket-heavy services have different baselines. - Keep
attemptslow. Two to three attempts is the sane range for most services. Higher values convert every backend wobble into a load multiplier. - Do not retry non-idempotent methods unless you have application-level idempotency (idempotency keys, dedup). Duplicate charges and duplicate records are worse than a returned error.
- Use
initialIntervalfor backoff so retries do not land on the backend in a tight burst. Without backoff, N attempts arrive nearly simultaneously, which is the worst possible shape for an already-struggling backend. - Pair the retry alert with latency. A composite condition (retry ratio high AND service p95 rising) is far more page-worthy than either signal alone, and matches the amplification signature.
- Size health checks honestly. Intervals and paths that reflect real dependencies shrink the window where Traefik sends traffic to doomed backends, which reduces retry volume at the source.
How Netdata helps
- Netdata collects
traefik_service_retries_totalper service alongside request rates, so the retry-to-request ratio is one chart, not a hand-computed PromQL query during an incident. - The decisive pair for this symptom, retries rising versus latency rising, is visible side by side:
traefik_service_request_duration_secondspercentiles next to retry rate per service. - Per-URL
traefik_service_server_upshows the healthy pool shrinking in real time, which separates a cascade in progress from transient flapping. - ML anomaly detection on the retry counter flags a service whose retry behavior deviates from its own baseline, catching slow-building amplification that static thresholds miss.
- Because retries hide errors from clients, correlating retry rate with 5xx rate and access-log
RetryAttemptsin one place shortens the path from “retries are up” to “backend X is the cause.”
Related guides
- Traefik cascading backend failure: how a partial outage becomes a total one
- Traefik 5xx error rate: telling Traefik-generated errors from backend errors
- Traefik 502 Bad Gateway: when the backend is unreachable or returns garbage
- Traefik 503 Service Unavailable: no healthy backends left in the pool
- Traefik 504 Gateway Timeout: the backend is alive but too slow
- Traefik health checks pass but requests fail: when the probe lies
- Traefik 404 not found: requests arriving with no matching router
- Traefik config last reload success: monitoring configuration freshness
- Traefik dashboard returns 404: reaching the API and dashboard correctly
- How Traefik actually works in production: a mental model for operators
- Traefik monitoring checklist: the signals every production edge router needs
- Traefik monitoring maturity model: from survival to expert






