Code-Server + Traefik + Kubernetes Ingress
1. Prerequisites
dockercommandkubectlcommandhelmcommand
2. Create the Kubernetes cluster using k3s
- Copy the following content to a file called
docker-compose.ymland rundocker compose up -dservices: server: image: "rancher/k3s:v1.23.12-k3s1" command: server --disable=traefik tmpfs: - /run - /var/run ulimits: nproc: 65535 nofile: soft: 65535 hard: 65535 privileged: true restart: always environment: - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml - K3S_KUBECONFIG_MODE=666 volumes: # This is just so that we get the kubeconfig file out - .:/output ports: - 6443:6443 # Kubernetes API Server - 8080:80 # Ingress controller port 80 - Wait for the cluster to be ready with command
export KUBECONFIG=$(pwd)/kubeconfig.yaml until kubectl get nodes | grep " Ready "; do sleep 1; done
3. Deploy Traefik using Helm
- Add the traefik Helm repository
helm repo add traefik https://helm.traefik.io/traefik helm repo update - Create the values.yaml file
image: tag: "2.9.1" experimental: plugins: enabled: true additionalArguments: - "--experimental.plugins.sablier.moduleName=github.com/sablierapp/sablier-traefik-plugin" # Replace with the latest release from https://github.com/sablierapp/sablier-traefik-plugin/releases - "--experimental.plugins.sablier.version=v1.0.0" providers: kubernetesIngress: enabled: true allowEmptyServices: true - Install the traefik chart with our
values.yamlfilehelm install traefik traefik/traefik -f values.yaml --namespace kube-system
4. Deploy Sablier
Create Sablier Service Account file
sablier-sa.yamland thenkubectl apply -f sablier-sa.yamlapiVersion: v1 kind: ServiceAccount metadata: name: sablier namespace: kube-systemCreate Sablier Cluster Role file
sablier-cr.yamland thenkubectl apply -f sablier-cr.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: sablier namespace: kube-system rules: - apiGroups: - apps - "" resources: - deployments - deployments/scale - statefulsets - statefulsets/scale verbs: - patch # Scale up and down - get # Retrieve info about specific deployment or statefulset - update # Scale up and down - list # Events - watch # EventsCreate Sablier Cluster Role Binding
sablier-crb.yamland thenkubectl apply -f sablier-crb.yamlapiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: sablier namespace: kube-system roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: sablier subjects: - kind: ServiceAccount name: sablier namespace: kube-systemCreate Sablier Deployment
sablier-deployment.yamland thenkubectl apply -f sablier-deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: sablier-deployment namespace: kube-system labels: app: sablier spec: replicas: 1 selector: matchLabels: app: sablier template: metadata: labels: app: sablier spec: serviceAccountName: sablier containers: - name: sablier image: sablierapp/sablier:1.15.0 args: - "start" - "--provider.name=kubernetes" ports: - containerPort: 10000Create Sablier Service
sablier-service.yamland thenkubectl apply -f sablier-service.yamlapiVersion: v1 kind: Service metadata: name: sablier namespace: kube-system spec: selector: app: sablier ports: - protocol: TCP port: 10000 targetPort: 10000Check with the command
kubectl -n kube-system get deployments, you should have the following:NAME READY UP-TO-DATE AVAILABLE AGE local-path-provisioner 1/1 1 1 8m25s coredns 1/1 1 1 8m25s metrics-server 1/1 1 1 8m24s traefik 1/1 1 1 3m25s sablier-deployment 1/1 1 1 90sOr getting the logs
kubectl -n kube-system logs deployments/sablier-deploymenttime=2026-01-01T12:00:00Z level=INFO msg="using provider \"kubernetes\"" time=2026-01-01T12:00:00Z level=INFO msg="initialized storage to /etc/sablier/state.json" time=2026-01-01T12:00:00Z level=INFO msg="server listening on :10000"
The Sablier API is now available at http://sablier:10000 inside your cluster.
5. Deploy Code-Server
- Create Code-Server Deployment
code-server-deployment.yamland thenkubectl apply -f code-server-deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: code-server-deployment namespace: default labels: app: code-server spec: replicas: 1 selector: matchLabels: app: code-server template: metadata: labels: app: code-server spec: containers: - name: code-server image: codercom/code-server:4.8.3 ports: - containerPort: 8080 - Create Code-Server Service
code-server-service.yamland thenkubectl apply -f code-server-service.yamlapiVersion: v1 kind: Service metadata: name: code-server-service namespace: default spec: selector: app: code-server ports: - protocol: TCP port: 8080 targetPort: 8080 - Check with the command
kubectl get deployments, you should have the following:NAME READY UP-TO-DATE AVAILABLE AGE code-server-deployment 1/1 1 1 2m18s
The Code-Server instance is now available inside your cluster.
6. Routing Code-Server through Traefik with the Sablier plugin middleware
- Create Code-Server Ingress
code-server-ingress.yamland thenkubectl apply -f code-server-ingress.yamlapiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: code-server-ingress namespace: default spec: ingressClassName: traefik rules: - host: localhost http: paths: - path: / pathType: Prefix backend: service: name: code-server-service port: number: 8080 - At this point, the Code-Server is reachable at http://localhost:8080.
- Scale down the Deployment with
kubectl scale deployment code-server-deployment --replicas=0 - Now, because we configured
--providers.kubernetesIngress.allowEmptyServices=true, if wecurlagain it should showService Unavailable.This is the key to make it work. Traefik would have evicted the service from its pool showing a 404 otherwise. Now you only need to plug in the middleware.curl http://localhost:8080 Service Unavailable - Create the Traefik Middleware
code-server-sablier-middleware.yamland thenkubectl apply -f code-server-sablier-middleware.yamlapiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: code-server-sablier namespace: default spec: plugin: sablier: names: deployment_default_code-server-deployment_1 sablierUrl: 'http://sablier:10000' sessionDuration: 30m dynamic: displayName: 'Code Server Demo' showDetails: true theme: hacker-terminal refreshFrequency: 5s - Create a patch for Code Server Deployment
code-server-ingress-patch.yamland thenkubectl patch ingress code-server-ingress --patch-file code-server-ingress-patch.yamlmetadata: annotations: traefik.ingress.kubernetes.io/router.middlewares: default-code-server-sablier@kubernetescrd - Now you can browse http://localhost:8080 and you will see the loading screen while Sablier scales your deployment up, and a few seconds later Code-Server is served.
7. Clean up
docker compose down