Skip to main content
Crusoe Support Help Center home page
Crusoe

How to Select externalTrafficPolicy Cluster v/s Local in Crusoe Managed Kubernetes

Sanchit Pathak
Sanchit Pathak
Updated

Last Updated: March 30th, 2025

Introduction

This article highlights steps on how users can choose the externalTrafficPolicy based on specific application requirements like source IP preservation in Crusoe Managed Kubernetes (CMK).

Prerequisites

  1. Access to a Crusoe Cloud project with appropriate permissions

  2. Existing Crusoe Managed Kubernetes Cluster 

  3. Access to crusoe and kubectl CLI

Step-by-Step Instructions

  1. Apply sample application deployment and service specification.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-test
spec:
  replicas: 1
  selector:
    matchLabels:
      app: web-test
  template:
    metadata:
      labels:
        app: web-test
    spec:
      containers:
      - name: web-server
        image: python:3.11-slim
        command: ["python", "-u", "-m", "http.server", "8000"]
        workingDir: /tmp
        env:
        - name: PYTHONUNBUFFERED
          value: "1"
        ports:
        - containerPort: 8000
---
apiVersion: v1
kind: Service
metadata:
  name: web-test-svc
spec:
  type: LoadBalancer
  selector:
    app: web-test
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8000

2. By default, the externalTrafficPolicy is set to Cluster. Refer to Crusoe Load Balancer to deploy the required configuration.

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
web-test-<xxxxxx>-<xxxxx>   1/1     Running   0          47m

$ kubectl get svc web-test-svc
NAME           TYPE           CLUSTER-IP      EXTERNAL-IP      PORT(S)        AGE
web-test-svc   LoadBalancer   10.233.24.171   203.0.113.100   80:32444/TCP    67m

$ kubectl get svc web-test-svc -o yaml | grep externalTrafficPolicy
  externalTrafficPolicy: Cluster

Note: Once you create the Kubernetes service object, ensure to create an ingress firewall rule. This rule should allow inbound traffic from any source IP (0.0.0.0/0) to the destination subnet and the nodePort of the service object.

3. Verify Load Balancer details using Crusoe CLI.

$ crusoe networking load-balancers get default-web-test-svc-1ad5720f
name: default-web-test-svc-1ad5720f
virtual_ip: 203.0.113.100
location: eu-iceland1-a
protocol: tcp
listen_ports: Port 80 (backends: 172.27.61.24:32444:offline,172.27.63.68:32444:online)
health_check: Timeout=5,Interval=5,Success=3,Failure=2
...

4. Test endpoint access via curl to ELB IP.

$ LB_IP=$(kubectl get svc web-test-svc -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

$ for i in {1..10}; do
  curl -s $LB_IP | grep "DOCTYPE" && echo "Request $i: SUCCESS" || echo "Request $i: FAILED"
done

<!DOCTYPE HTML>
Request 1: SUCCESS
<!DOCTYPE HTML>
Request 2: SUCCESS
<!DOCTYPE HTML>
Request 3: SUCCESS
<!DOCTYPE HTML>
Request 4: SUCCESS
<!DOCTYPE HTML>
Request 5: SUCCESS
<!DOCTYPE HTML>

Requests show internal cluster IP, not the real client IP for the curl requests. The node's kube-proxy applies SNAT rules, rewriting the source IP to an internal cluster address before the traffic reaches the pod.

$ kubectl logs -l app=web-test
10.234.0.61 - - [30/Mar/2026 22:22:58] "HEAD / HTTP/1.1" 200 -
10.234.0.61 - - [30/Mar/2026 22:23:01] "HEAD / HTTP/1.1" 200 -
10.234.0.61 - - [30/Mar/2026 22:28:39] "GET / HTTP/1.1" 200 -
10.234.0.61 - - [30/Mar/2026 22:28:40] "GET / HTTP/1.1" 200 -
10.234.0.61 - - [30/Mar/2026 22:28:40] "GET / HTTP/1.1" 200 -
10.234.0.61 - - [30/Mar/2026 22:28:40] "GET / HTTP/1.1" 200 -

5. Patch the Service object to set externalTrafficPolicy: Local

$ kubectl patch svc web-test-svc -p '{"spec":{"externalTrafficPolicy":"Local"}}'
service/web-test-svc patched

$ kubectl get svc web-test-svc -o yaml | grep externalTrafficPolicy
  externalTrafficPolicy: Local

6. Test endpoint access again via curl to ELB IP.

$ for i in {1..10}; do
  curl -s $LB_IP | grep "DOCTYPE" && echo "Request $i: SUCCESS" || echo "Request $i: FAILED"
done

=== LOCAL policy (10 requests) ===
<!DOCTYPE HTML>
Request 1: SUCCESS
<!DOCTYPE HTML>
Request 2: SUCCESS
<!DOCTYPE HTML>
Request 3: SUCCESS
<!DOCTYPE HTML>
Request 4: SUCCESS
<!DOCTYPE HTML>
Request 5: SUCCESS
<!DOCTYPE HTML>
All requests show the actual client IP because Local policy disables the kube-proxy SNAT rewrite.
$ kubectl logs -l app=web-test                                                  
67.208.231.219 - - [30/Mar/2026 22:28:41] "GET / HTTP/1.1" 200 -
67.208.231.219 - - [30/Mar/2026 22:33:36] "GET / HTTP/1.1" 200 -
67.208.231.219 - - [30/Mar/2026 22:33:36] "GET / HTTP/1.1" 200 -
67.208.231.219 - - [30/Mar/2026 22:33:37] "GET / HTTP/1.1" 200 -
67.208.231.219 - - [30/Mar/2026 22:33:37] "GET / HTTP/1.1" 200 -
67.208.231.219 - - [30/Mar/2026 22:33:37] "GET / HTTP/1.1" 200 -

Additional Resources

K8s - Preserving Client Source IP

Related to

Was this article helpful?

0 out of 0 found this helpful

Still need help?

Our support team is ready to assist you with any questions.

Have more questions? Submit a request

Recently Viewed

Comments

0 comments

Article is closed for comments.