Skip to content

service ​

service is Turna's reverse proxy middleware. It can proxy to one or more upstream servers with weighted round-robin, random or least-connections balancing, health checks, session affinity and traffic mirroring, or select upstreams by request path prefix.

yaml
server:
  http:
    middlewares:
      backend:
        service:
          insecure_skip_verify: false
          pass_host_header: true
          proxy: http://proxy.internal:3128
          loadbalancer:
            servers:
              - url: http://localhost:3000
              - url: http://localhost:3001

Fields ​

FieldDefaultDescription
insecure_skip_verifyfalseSkip upstream TLS certificate verification. Also applies to active health checks and mirror requests.
h2cfalseUse HTTP/2 without TLS for http:// upstreams, required for plain text gRPC servers.
pass_host_headerWhen explicitly false, clear r.Host before proxying.
proxyOptional HTTP or HTTPS forward proxy URL used for upstream requests. Credentials may be included in the URL.
loadbalancer.serversUpstream list; each entry has url (http://, https:// or unix:///path.sock) and optional weight (default 1).
loadbalancer.strategyround_robinround_robin (smooth weighted), random (weighted) or least_conn (active requests / weight).
loadbalancer.sticky.cookieSession affinity cookie, see below.
loadbalancer.health_checkActive health check, see below.
loadbalancer.passive_health_checkPassive health check, see below.
retry0Retries on other upstreams when an upstream is unreachable.
mirrorShadow traffic, see below.
prefixbalancer.prefixesPath-prefix-specific upstream lists. prefix accepts a single string or a list of prefixes.
prefixbalancer.default_serversDefault upstreams when no prefix matches.
timeouts.dial30sTimeout of establishing the upstream TCP connection.
timeouts.tls_handshake10sTimeout of the upstream TLS handshake.
timeouts.response_headerTime to wait for the upstream response headers after the request is sent. Empty means no limit. Exceeding it gives 502 and counts as an upstream failure for retry.
timeouts.idle_conn90sIdle keep-alive connections are closed after this.

timeouts limit the upstream connection only; reading a long response body is not limited. To limit the whole request, put timeout in front of service.

WebSockets and streaming ​

service proxies WebSocket (Connection: Upgrade) and streaming responses (SSE) transparently. WebSocket upgrades are forwarded over the same transport as regular requests, so wss:///https:// upstreams work, honoring insecure_skip_verify. Path rewrites and the pass_host_header setting apply to upgrades too.

WebSocket upgrades use HTTP/1.1 to the upstream; backends that only speak HTTP/2 cannot accept them.

Unix sockets ​

An upstream written as unix:///path/to.sock is reached over that unix socket and spoken to with plain HTTP. The request path is sent unchanged, the socket path is not part of it.

yaml
service:
  loadbalancer:
    servers:
      - url: unix:///var/run/docker.sock

Health checks, retries, h2c and mirror servers work the same way, and proxy is not used for socket upstreams. The socket must be reachable by the Turna process, so mount it into the container. See the Docker socket proxy for a complete example.

Prefix Balancer ​

yaml
service:
  prefixbalancer:
    prefixes:
      - prefix: /api
        servers:
          - url: http://api:3000
      - prefix:
          - /admin
          - /internal
        servers:
          - url: http://admin:3000
    default_servers:
      - url: http://web:3000

If the prefix balancer is configured, it is used instead of the plain loadbalancer.

prefix can be a single string or a list; a group matches when the request path starts with any of its prefixes. Groups are checked in order and the first match wins.

The loadbalancer options (strategy, sticky, health_check, passive_health_check) also apply to every prefix group and the default servers. The same upstream URL used in several groups shares its health and connection state.

Load Balancing ​

yaml
service:
  retry: 1
  loadbalancer:
    strategy: least_conn
    servers:
      - url: http://app-1:3000
        weight: 3
      - url: http://app-2:3000

Unhealthy or ejected upstreams are skipped. When every upstream is unavailable, turna still tries them (fail open) instead of rejecting the request.

Sticky Sessions ​

yaml
loadbalancer:
  sticky:
    cookie:
      name: turna_lb
      max_age: 1h
      secure: true
FieldDefaultDescription
nameturna_lbCookie name. Prefix groups get a _<index> suffix.
path/Cookie path.
domainCookie domain.
max_agesessionCookie lifetime.
securefalseSecure flag.
http_onlytrueHttpOnly flag.
same_sitelaxlax, strict or none.

The cookie holds a hash of the upstream URL. If that upstream is unhealthy, another one is picked and the cookie is updated.

Active Health Check ​

yaml
loadbalancer:
  health_check:
    path: /healthz
    interval: 10s
    timeout: 2s
    status: "200-299"
    unhealthy_threshold: 2
FieldDefaultDescription
path/Probe path, may include a query string.
methodGETProbe method.
hostHost header of the probe.
headersExtra probe headers.
interval10sTime between probes.
timeout5sProbe timeout.
status200-399Accepted status codes, e.g. 200,204,300-399.
healthy_threshold1Consecutive successes to mark healthy.
unhealthy_threshold1Consecutive failures to mark unhealthy.

Passive Health Check ​

yaml
loadbalancer:
  passive_health_check:
    max_fails: 3
    fail_timeout: 30s
    fail_statuses: [502, 503, 504]

An upstream is ejected for fail_timeout after max_fails failures within fail_timeout. Connection errors always count as failures; fail_statuses adds response codes.

Mirror ​

Mirror sends a copy of requests to shadow upstreams. Mirror responses are discarded and never change the client response.

yaml
service:
  loadbalancer:
    servers:
      - url: http://app-v1:3000
  mirror:
    servers:
      - url: http://app-v2:3000
        percent: 10
FieldDefaultDescription
servers[].urlShadow upstream.
servers[].percent100Percent of requests to mirror.
max_body_size1048576Requests with larger bodies are not mirrored.
timeout10sMirror request timeout.
max_in_flight100Concurrent mirror requests; extra ones are dropped.

WebSocket upgrades are not mirrored.