I found it rather tricky to set up a Traefik TCP ingress for connecting directly to a PostgreSQL server running inside Kubernetes. There are a few moving parts, and they all need to be right for the ingress to work, but most resources I found on the internet left out one (or more), so here they all are in one place. While I’ve only tried this with PostgreSQL, it’s likely that other database ingresses will work the same way.

Traefik values.yaml

Two additions are needed:

  • We need to add an entryPoint for the port that PostgreSQL listens on so that it will route traffic entering on that port
  • We need to expose that port from the control node so that clients can actually connect to it
additionalArguments:
  - "--entryPoints.postgresql.address=:5432/tcp"
ports:
  postgresql:
    expose: true
    port: 5432
    exposedPort: 5432
    protocol: TCP

On k3s, Traefik is installed in the kube-system namespace via Helm, so if you use Helm to modify the values above, make sure you use the same namespace. I didn’t want to risk changing whatever values Rancher had used by default, so I specified the full set:

rbac:
  enabled: true
ports:
  websecure:
    tls:
      enabled: true
podAnnotations:
  prometheus.io/port: "8082"
  prometheus.io/scrape: "true"
providers:
  kubernetesIngress:
    publishedService:
      enabled: true
priorityClassName: "system-cluster-critical"
image:
  name: "rancher/mirrored-library-traefik"
  tag: "2.6.2"
tolerations:
- key: "CriticalAddonsOnly"
  operator: "Exists"
- key: "node-role.kubernetes.io/control-plane"
  operator: "Exists"
  effect: "NoSchedule"
- key: "node-role.kubernetes.io/master"
  operator: "Exists"
  effect: "NoSchedule"
service:
  ipFamilyPolicy: "PreferDualStack"

I used Terraform to install the Helm release, but if you use the CLI, run:

helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm upgrade --install traefik traefik/traefik \
  -n kube-system -f values.yaml

Traefik Ingress

  • The HostSNI needs to match *, apparently most database protocols - and especially PostgreSQL - don’t support SNI over TLS. Additionally, TLS needs to be disabled since the protocol used by the client and server use their own TLS, so Ingress doesn’t need to handle or terminate it. That’s why there needs to be no tls section in the spec.
  • The namespace needs to be the same as the one in which you installed PostgreSQL and its service.
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRouteTCP
metadata:
  name: postgresql-ingress
  namespace: postgresql
spec:
  entryPoints:
    - postgresql
  routes:
    match: HostSNI(`*`)
    services:
      - name: postgresql
        port: 5432

With both sets of changes, you should be able to connect to the PostgreSQL instance on port 5432 of your control node’s IP, or any hostname that resolves to that IP. If you want to run other databases and expose them on the same host, they’ll need to listen on a different port.