Last Updated: Nov 07, 2025
Introduction
Crusoe Cloud now supports the creation of managed L4 Load Balancers. For more information, see Crusoe Cloud Load Balancers Docs.
These Load Balancers can be leveraged in Crusoe Managed Kubernetes (CMK) clusters.
The aim of this article is to describe how to achieve the creation of a CMK Load Balancer and to give an example of how traffic is distributed to a backend application.
Note: This feature is in early access. To enable it, please contact Crusoe Support.
Prerequisites
- Functional CMK cluster
- Helm client
- Access to Load Balancers feature in Crusoe Cloud
Limitations
Currently, the following limitations apply:
- Max 3 Load Balancers per project.
- Max 10,000 target ports in total.
Step-by-Step Instructions
The general steps can be found in https://github.com/crusoecloud/crusoe-load-balancer-controller-helm-charts
- Clone the Helm Chart repository for the Load Balancer Controller
# git clone https://github.com/crusoecloud/crusoe-load-balancer-controller-helm-charts-
The following subdirectories should be seen:
# ls -lrt crusoe-load-balancer-controller-helm-charts total 32 -rw-r-----@ 1 <user> staff 967 4 Sep 18:31 Makefile -rw-r-----@ 1 <user> staff 2105 4 Sep 18:31 README.md drwxr-x---@ 3 <user> staff 96 4 Sep 18:31 charts -rw-r-----@ 1 <user> staff 158 4 Sep 18:31 cr.yaml drwxr-x---@ 4 <user> staff 128 4 Sep 18:31 examples -rw-r-----@ 1 <user> staff 53 4 Sep 18:31 versions.yml
- Install the Load Balancer Controller with Helm
- First check if you have the cluster credentials stored at
~/.kube/config, if not refer get cluster credentials -
Note: It is strongly recommended to install the Load Balancer Controller in
crusoe-systemnamespace. This namespace containscrusoe-secretsKubernetes secret, with the Access and Secret Keys needed for the Controller to talk with the Crusoe Cloud API.
If you wish to deploy the controller in a different namespace, you must copy the necessary secrets (crusoe-secrets) into that namespace.# cd crusoe-load-balancer-controller-helm-charts/charts # helm install crusoe-lb-controller ./crusoe-lb-controller -n crusoe-system -
You should see the following output:
# helm install crusoe-lb-controller ./crusoe-lb-controller -n crusoe-system NAME: crusoe-lb-controller LAST DEPLOYED: Mon Sep 8 15:45:39 2025 NAMESPACE: crusoe-system STATUS: deployed REVISION: 1 TEST SUITE: None -
Listing the Helm releases should show
crusoe-lb-controllerdeployed:# helm list -n crusoe-system NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION crusoe-lb-controller crusoe-system 1 2025-09-08 15:45:39.676567 +0100 IST deployed crusoe-lb-controller-0.0.23 v0.0.23 -
The
crusoe-lb-controller-crusoe-lb-controllerpod should be up and running:# kubectl get pod -n crusoe-system | grep lb-controller crusoe-lb-controller-crusoe-lb-controller-7669c4779f-7px4g 1/1 Running 0 83s
- Set up a backend service
- At this stage, you can set up a backend service which will be exposed via a LoadBalancer type Kubernetes service.
-
As an example, we'll deploy the
helloWorldservice from the above GitHub repo, exposed on the backend port 8080:# cat examples/helloWorld/helloWorld-svc.yaml apiVersion: apps/v1 kind: Deployment metadata: name: hello-world namespace: default spec: replicas: 1 selector: matchLabels: app: app template: metadata: labels: app: app spec: containers: - name: http-echo image: hashicorp/http-echo:0.2.3 args: - "-listen=:8080" - "-text=You just hit crusoe's loadbalancer! :)" ports: - containerPort: 8080 hostPort: 8080 -
To deploy the
hello-worldDeployment:# cd crusoe-load-balancer-controller-helm-charts # kubectl apply -f examples/helloWorld/helloWorld-svc.yaml deployment.apps/hello-world created -
We should see one
hello-worldPod up and running:# kubectl get po NAME READY STATUS RESTARTS AGE hello-world-65f48fc886-zs78q 1/1 Running 0 18s - This backend is not yet externally accessible because it hasn't been exposed through an external Load Balancer.
- Expose the backend via a LoadBalancer service
- We'll expose the newly created
hello-worldbackend via a LoadBalancer type Kubernetes service.
Once the service is created, the Load Balancer Controller we deployed in step #2 will create a corresponding external Load Balancer in Crusoe Cloud. -
To expose the backend, we'll use the
helloworldLoadBalancer type service given in the above GitHub repository:# cat examples/helloWorld/lb-helloWorld-svc.yaml apiVersion: v1 kind: Service metadata: name: example-lb-svc namespace: default # Optionally set LB health check parameters e.g. 3 failed checks # within 10s will result in loadbalancer backend being marked as failed # and this will be reset in case of 2 successful checks within same duration. # Each health check will have a timeout of 5s annotations: crusoe.ai/health-check-failure-count: "3" crusoe.ai/health-check-interval: "10" crusoe.ai/health-check-success-count: "2" crusoe.ai/health-check-timeout: "5" spec: selector: app: app ports: - name: http port: 80 targetPort: 8080 type: LoadBalancer - The service will be associated to the
hello-worldPod we created thanks to the givenselector, and will expose it on port 80 via HTTP. -
To create the LoadBalancer type service:
# kubectl apply -f examples/helloWorld/lb-helloWorld-svc.yaml service/example-lb-svc created -
In the Kubernetes cluster, we should see the service created, as well as an associated endpoint:
# kubectl get svc,ep NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/example-lb-svc LoadBalancer 10.233.20.111 <External IP> 80:30723/TCP 42s NAME ENDPOINTS AGE endpoints/example-lb-svc 10.234.0.217:8080 42s - The
EXTERNAL-IPof the service is the one Crusoe Cloud assigned to the external Load Balancer. This can be checked inCrusoe Cloud GUI>Networking>Load Balancers:
- Create Firewall Rules to allow ingress traffic to the Load Balancer
- Please refer to Docs: Load Balancers - Overview - Firewall Rules
-
For this example, we need to create a Firewall Rule as follows:
Direction: Ingress Protocol: TCP Source Ports: All Source: All addresses Destination Ports: <LoadBalancer K8s service's NodePort> Destination: Subnet where the backend pods are deployed to
- Verify the exposure of the backend service
-
Now that the
hello-worldbackend has been exposed and Firewall Rules set up, it should be reachable via the Load Balancer's external IP which can be copied fromCrusoe Cloud GUI>Networking>Load Balancersor running the first command from below:# kubectl get svc,ep # curl http://<External IP>:80 You just hit crusoe's loadbalancer! :)
Troubleshooting
-
Why is my Load Balancer unreachable?
-
Check you are using TCP:
You must use TCP - it is the only supported protocol currently (you cannot ‘ping’ your load balancer VIP). To test that you can reach your load balancer, do
nc -zv {public ip} {listen port}. A successful command will showConnection to <external-LB-IP> port 80 [tcp/http] succeeded!
Note: this will only work if there is a service running on your backend.
-
Are your K8s nodes (aka your backend nodes) in Ready state?
Use
kubectl get nodesto check the state of your nodes. Only those in theReadystate will be added to the backend list.
-
Do your backend nodes have an ONLINE status?
Use
crusoe networking load-balancers get <lb-id>to check backend statuses.
The<lb-id>can be obtained withcrusoe networking load-balancers listcommand.If backend status is UNSPECIFIED, make sure backend exists and is running.
-
If backend status is OFFLINE, this means your service is not responding to health checks. Make sure your service is actually running on your backend.
SSH to the backend node and run
netstat -tulpn | grep :<your_backend_port>. You should see that a service is running on that port.If the service actually is running, contact Crusoe Support.
If the service is not running, then most likely the service deployment is incorrect. Check your service deployment file.
Check your Firewall Rules. See above step
5. Create Firewall Rules to allow ingress traffic to the Load Balancer.-
If the above checks don't show anything wrong, but
nc -zv {public ip} {listen port}doesn't reach the target - i.e. the command hangs - then try running this command from another Crusoe VM (doesn't have to be the same VPC).If this works, then this could potentially be an issue with public IP advertisement. Contact Crusoe Support.
If this does not work, this could potentially be an issue with a misconfigured OVN object. Contact Crusoe Support.
-
-
Why is my traffic not being load balanced?
Are you sending traffic from different Source IP + Source Port combinations? OVN load balancing uses a hash of the Source IP and Source Port to determine which backend to send traffic to, so traffic sent by the same IP/Port will be sent to the same backend.
-
What does the status field of the backend indicate?
UNSPECIFIED - the backend does not exist or is not running (or some other error has occurred).
OFFLINE - the backend is not responding to health checks. Check that a service is running on the backend.
ONLINE - the backend is responding to health checks. A service is running on the backend on the specified port.
References