Skip to main content
Crusoe Support Help Center home page
Crusoe

How-To Create a Crusoe Cloud Load Balancer for a CMK Cluster

Alejandro Alvaro Gonzalez
Alejandro Alvaro Gonzalez
Updated

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 

  1. 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
  1. 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-system namespace. This namespace contains crusoe-secrets Kubernetes 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-controller deployed:

    # 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-controller pod 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
  1. 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 helloWorld service 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-world Deployment:

    # 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-world Pod 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.
  1. Expose the backend via a LoadBalancer service
  • We'll expose the newly created hello-world backend 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 helloworld LoadBalancer 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-world Pod we created thanks to the given selector, 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-IP of the service is the one Crusoe Cloud assigned to the external Load Balancer. This can be checked in Crusoe Cloud GUI > Networking > Load Balancers:
  1. 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


  1. Verify the exposure of the backend service
  • Now that the hello-world backend has been exposed and Firewall Rules set up, it should be reachable via the Load Balancer's external IP which can be copied from Crusoe Cloud GUI > Networking > Load Balancers or 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 show Connection 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 nodes to check the state of your nodes. Only those in the Ready state 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 with crusoe networking load-balancers list command.

      • 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

 

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.