---
title: Migrating from Traefik
description: Traefik and Super do the same job, organised a little differently.
---
> ## Documentation Index
> Fetch the complete documentation index at: https://docs.page/luizfonseca/super/llms.txt
> Use this file to discover all available pages before exploring further.

Traefik and Super do the same job, organised a little differently. This page maps one onto the other, for a configuration file, Docker and Kubernetes.

## Concepts

| Traefik | Super |
| --- | --- |
| Entrypoint (`web`, `websecure`) | `server.http_address`, `server.https_address`, or a host's `listen` |
| Router (`Host(...) && PathPrefix(...)`) | A `host` and its `location`s |
| Service (load balancer servers) | `upstream` blocks of a location |
| Middleware | Blocks in a location: `basic_auth`, `rate_limit`, `headers`... |
| Certificate resolver (ACME) | `lets_encrypt`, or `tls { certificate { ... } }` for files |
| TCP router (`HostSNI(...)`) | A `tcp` block, in a host (by SNI) or at the top level (by port) |
| Providers (file, docker, kubernetes) | The configuration file, docker labels, Kubernetes `Ingress`es, and the store, applied together |

A location matches its path and everything below it, and the most specific location wins, so there are no router priorities to set.

## A host, with HTTPS

```yaml
# traefik: dynamic configuration
http:
  routers:
    app:
      rule: "Host(`app.example.com`)"
      entryPoints: [websecure]
      service: app
      tls:
        certResolver: le
  services:
    app:
      loadBalancer:
        servers:
          - url: "http://app:8080"
```

```hcl
# config.hcl
lets_encrypt {
  enabled = true
  staging = false
  email   = "you@example.com"
}

host "app.example.com" {
  location "/" {
    upstream "app:8080" {}
  }
}
```

- Hosts are HTTPS by default, and their HTTP requests are redirected to HTTPS (`308`): the redirect router and `redirectScheme` middleware aren't needed.
- Certificates are renewed 30 days before they expire, and the new one is served without a restart.
- `tls = false` serves a host over plain HTTP instead.

## Middlewares

| Traefik middleware | Super |
| --- | --- |
| `basicAuth` | [`basic_auth { user, pass }`](/plugins/basic-auth) (one user) |
| `rateLimit` | [`rate_limit { requests, per }`](/plugins/rate-limit), per client |
| `buffering.maxRequestBodyBytes` | [`body_limit { max = "10MiB" }`](/plugins/body-limit) |
| `headers.customResponseHeaders` | [`headers { response { add, remove } }`](/routing/headers) |
| `headers.customRequestHeaders` | `headers { add = { ... } }` in an `upstream` |
| `headers.accessControl*` (CORS) | [`cors { origins, methods, headers, credentials, max_age_secs }`](/plugins/cors) |
| `redirectScheme` (to HTTPS) | Built in |
| `forwardAuth` | [`forward_auth { url, response_headers }`](/plugins/forward-auth) (`authResponseHeaders`: `response_headers`, `authRequestHeaders`: `request_headers`) |
| `stripPrefix`, `addPrefix` | [`rewrite`](/routing/upstreams#rewriting-paths) `{ strip_prefix, add_prefix }` in the location |
| `replacePath`, `replacePathRegex` | [`rewrite`](/routing/upstreams#rewriting-paths) `{ match, replace }` |
| `redirectRegex` | [`redirect { to }`](/plugins/redirect) in a location, with `{uri}`, `{path}`, `{query}` and `{host}` |
| `compress` | [`compress { }`](/plugins/compress) |
| `ipAllowList` | [`ip_filter { allow }`](/plugins/ip-filter) (`ipStrategy`: `server.trusted_proxies`) |
| `retry` | [`retry { attempts }`](/routing/upstreams#retries) in the location (also on `502`, `503`, `504` by default) |
| `circuitBreaker` | Not available |

## Docker labels

```yaml
# traefik
labels:
  traefik.enable: "true"
  traefik.http.routers.app.rule: "Host(`app.example.com`)"
  traefik.http.services.app.loadbalancer.server.port: "8080"
```

```yaml
# proksi (docker.enabled = true, and the container on a network proksi shares)
labels:
  proksi.enabled: "true"
  proksi.host: "app.example.com"
  proksi.port: "8080"
```

Containers with the same `proksi.host` are load balanced. See [Docker swarm](/use-cases/docker-swarm) for the other labels. Labels can't set `tcp` blocks, `rate_limit` or `body_limit` yet: define those hosts in the configuration file, with the container's name as the upstream (`upstream "app:8080" {}`), which Super follows when the container is recreated.

## Kubernetes

Super is an ingress controller for standard `Ingress` resources (see [Kubernetes](/installation/kubernetes)). Traefik's `IngressRoute`, `Middleware` and `IngressRouteTCP` resources have no equivalent: they become `Ingress`es with annotations.

| Traefik | Super |
| --- | --- |
| `kubernetes.io/ingress.class: traefik` or `ingressClassName: traefik` | `ingressClassName: proksi` (the annotation works too) |
| `traefik.ingress.kubernetes.io/router.middlewares` | `proksi.ingress/blocks`: written like the inside of a `location` (`rate_limit`, `headers`, `basic_auth`...), see below |
| A `Middleware` with `basicAuth` | `basic_auth {}` in `proksi.ingress/blocks`, and a `Secret` labelled `proksi.ingress/auth`, named in `proksi.ingress/auth-secret` |
| `traefik.ingress.kubernetes.io/router.entrypoints: web` | `proksi.ingress/tls: "false"` (plain HTTP) |
| `IngressRoute` with `redirectScheme` | Built in |
| Servers are the service's pods | The same: pods that are ready, from `EndpointSlice`s. `proksi.ingress/upstream: service` uses the service's address instead |

```yaml
metadata:
  annotations:
    proksi.ingress/blocks: |
      rate_limit {
        requests = 100
        per      = "1m"
      }
```

- **cert-manager** works as before: certificates come from the `tls` secrets of `Ingress`es, and are picked up when renewed. With an HTTP-01 solver, set its class to `proksi` (see [cert-manager](/installation/kubernetes#cert-manager)).
- **Running both side by side**: install Super with a `NodePort` or another `LoadBalancer` address, move `Ingress`es one at a time by changing their class, and check them with `curl --resolve app.example.com:443:<proksi address>`. Then free ports 80 and 443 from Traefik (on k3s: `--disable=traefik`).
- **Metrics**: enable the [admin API](/configuration/admin); it needs a `token` to listen on a non-loopback address, which Prometheus then scrapes with.

## TCP

```yaml
# traefik
tcp:
  routers:
    db:
      rule: "HostSNI(`db.example.com`)"
      service: db
      tls: {}
```

```hcl
host "db.example.com" {
  tcp "postgres" {
    listen = "0.0.0.0:5433"
    upstream "postgres:5432" {}
  }
}
```

TLS is terminated with the host's certificate; `tls = "passthrough"` forwards it untouched. A `tcp` block at the top level forwards plain TCP. Hosts can share port 443 between HTTPS and TCP, chosen by name or ALPN. See [TCP](/routing/tcp).

## Good to know

- **Access logs** are JSON by default, one line per request and per TCP connection.
- **Client IP**: upstreams get `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Host` and `X-Real-IP`. Behind another proxy, list it in `server.trusted_proxies`.
- **Timeouts**: `server.timeouts` (`idle_secs`, `read_secs`, `write_secs`, 60 each by default), and per host or location `read_secs`/`write_secs`, instead of `respondingTimeouts`. Toward upstreams, `timeouts { upstream { connect_secs, read_secs, ... } }` in the server, a host or a location, instead of `forwardingTimeouts` (see [Timeouts](/configuration/hcl#timeouts)).
- **TLS options**: `server.tls { min_version, ciphers }` for every host, instead of `tls.options` (`minVersion`, `cipherSuites`). The defaults refuse DHE, as Traefik's do (see [TLS versions and ciphers](/configuration/hcl#tls-versions-and-ciphers)).
- **Stopping**: open connections get `server.shutdown_timeout_secs` (30) to finish, instead of `lifeCycle.graceTimeOut`. On Linux, settings changes take effect without dropping connections (see [Restarts and upgrades](/configuration/upgrades)).
- **Health checks** are TCP connections to each upstream, every 15 seconds by default (`health_check { interval_secs, enabled }`); there are no HTTP health check paths.
- **Weighted services** (canaries) are [`traffic_split`](/routing/traffic-split) blocks, and **mirroring** services a [`mirror`](/routing/mirror) block, in the location.
- **DNS challenges and wildcard hosts**: `lets_encrypt { dns { provider = "cloudflare" } }`, and `host "*.example.com"` (see [wildcard hosts](/configuration/hcl#hosts-and-locations)). Cloudflare is the only DNS provider for now.
- **Not available**: sticky sessions to one upstream, HTTP/3.
