LoadBalancer Services Configuration

Overview

The CCM supports Linode NodeBalancers for LoadBalancer Services.

For implementation examples, see Basic Service Examples.

NodeBalancer Implementation

When using NodeBalancers, the CCM automatically:

  1. Creates and configures a NodeBalancer
  2. Sets up backend nodes
  3. Manages health checks
  4. Handles SSL/TLS configuration

For more details, see Linode NodeBalancer Documentation.

IPv6 Support

NodeBalancers support both IPv4 and IPv6 ingress addresses. By default, the CCM uses only IPv4 address for LoadBalancer services.

You can enable IPv6 addresses globally for all services by setting the enable-ipv6-for-loadbalancers flag:

spec:
  template:
    spec:
      containers:
        - name: ccm-linode
          args:
            - --enable-ipv6-for-loadbalancers=true

Alternatively, you can enable IPv6 addresses for individual services using the annotation:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress: "true"

When IPv6 is enabled (either globally or per-service), both IPv4 and IPv6 addresses will be included in the service’s LoadBalancer status.

IPv6 Backend Support

Backend IPv6 controls which node addresses a NodeBalancer targets.

IPv6 backends require a dual-stack workload cluster. In practice, the cluster networking stack must support IPv6 NodePort traffic, and the Service itself should be created as dual-stack. A single-stack IPv4 LoadBalancer Service can still be annotated for IPv6 backends, but the NodeBalancer health checks and traffic path may fail because the backend NodePort is not exposed over IPv6.

You can enable IPv6 backends globally for NodeBalancer services:

spec:
  template:
    spec:
      containers:
        - name: ccm-linode
          args:
            - --enable-ipv6-for-nodebalancer-backends=true

Or per service:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-backends: "true"

When IPv6 backends are enabled:

  • NodeBalancer backend targets use the node public IPv6 annotation node.k8s.linode.com/public-ipv6
  • IPv6 NodeBalancer backends are currently supported only through node public IPv6 addresses, not VPC IPv6 backend addresses
  • both VPC-backed and non-VPC-backed NodeBalancer services are affected
  • when VPC-backed NodeBalancers are enabled, CCM preserves the NodeBalancer VPC configuration instead of dropping it
  • enabling the global --enable-ipv6-for-nodebalancer-backends flag can migrate existing eligible NodeBalancer services from IPv4 to IPv6 backends during reconcile
  • to keep an existing Service on IPv4 while the global flag is enabled, set service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-backends: "false" on that Service
  • every selected backend node must have a public IPv6 address available
  • if your cluster uses VPC backends, the nodes still need public IPv6 endpoints so CCM can program IPv6 NodeBalancer backends
  • the workload cluster and Service must be configured for dual-stack networking
  • reconciliation fails and CCM logs an error if a selected backend node does not have the required public IPv6 address

Recommended Service configuration for IPv6 backends:

apiVersion: v1
kind: Service
metadata:
  name: my-service
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-backends: "true"
spec:
  type: LoadBalancer
  ipFamilyPolicy: RequireDualStack
  ipFamilies:
    - IPv4
    - IPv6
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: my-app

If your cluster does not provide IPv6-capable NodePort routing, the NodeBalancer may still be created with IPv6 backend addresses, but the backends will not become healthy. Likewise, if your cluster is using VPC backends but the nodes do not also have public IPv6 endpoints, IPv6 backend reconciliation will fail because CCM does not currently program VPC IPv6 backend addresses.

Basic Configuration

Create a LoadBalancer service:

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: my-app

See Advanced Configuration Examples for more complex setups.

NodeBalancer Settings

Protocol Configuration

Available protocols:

  • tcp (default)
  • http
  • https
  • udp

Set the default protocol:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-default-protocol: "http"

See Service Annotations for all protocol options.

Health Checks

Configure health checks using annotations:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-check-type: "http"
    service.beta.kubernetes.io/linode-loadbalancer-check-path: "/healthz"
    service.beta.kubernetes.io/linode-loadbalancer-check-interval: "5"
    service.beta.kubernetes.io/linode-loadbalancer-check-timeout: "3"
    service.beta.kubernetes.io/linode-loadbalancer-check-attempts: "2"

Available check types:

  • none: No health check
  • connection: TCP connection check
  • http: HTTP status check
  • http_body: HTTP response body check

For more details, see Health Check Configuration.

SSL/TLS Configuration

  1. Create a TLS secret:
apiVersion: v1
kind: Secret
metadata:
  name: my-tls-secret
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>
  1. Reference in service annotation:
metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-port-443: |
      {
        "protocol": "https",
        "tls-secret-name": "my-tls-secret"
      }

Connection Throttling

Limit connections from the same client IP:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-throttle: "5"

Proxy Protocol

Enable proxy protocol for client IP preservation:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-default-proxy-protocol: "v2"

Configuring NodeBalancers directly with VPC

NodeBalancers can be configured to have VPC specific ips configured as backend nodes. It requires:

  1. VPC with a subnet and Linodes in VPC
  2. Each NodeBalancer created within that VPC needs a free /30 or bigger subnet from the subnet to which Linodes are connected

Specify NodeBalancer backend ipv4 range when creating service:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-backend-ipv4-range: "10.100.0.0/30"

By default, CCM uses first VPC and Subnet name configured with it to attach NodeBalancers to that VPC subnet. To overwrite those, use:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-backend-ipv4-range: "10.100.0.4/30"
    service.beta.kubernetes.io/linode-loadbalancer-backend-vpc-name: "vpc1"
    service.beta.kubernetes.io/linode-loadbalancer-backend-subnet-name: "subnet1"

Reserving a NodeBalancer backend IPv4 range

--nodebalancer-backend-ipv4-reserved-range marks one /30 inside --nodebalancer-backend-ipv4-subnet as off-limits, so CCM never assigns it to a NodeBalancer. Use it to keep a backend range available for an address block managed outside the cluster.

spec:
  template:
    spec:
      containers:
        - name: ccm-linode
          args:
            - --nodebalancer-backend-ipv4-subnet=10.100.0.0/24
            - --nodebalancer-backend-ipv4-reserved-range=10.100.0.252/30

The equivalent Helm values are nodeBalancerBackendIPv4Subnet and nodeBalancerBackendIPv4ReservedRange.

When nodeBalancerBackendIPv4ReservedRange is set and routeController is not, the Helm chart also passes --configure-cloud-routes=false. The reserved range itself does not require cloud routes to be disabled; the chart treats it as a signal that pods run on an overlay network, where there are no VPC routes to program. If routeController is set, it takes precedence and the chart passes --configure-cloud-routes=true. To override the derived value, pass --configure-cloud-routes in extraArgs, which the chart appends after its own arguments.

CCM validates the reserved range at startup and exits with an error unless:

  • --nodebalancer-backend-ipv4-subnet is also set
  • the reserved range is a /30
  • the reserved range is the highest /30 of the backend subnet (10.100.0.252/30 for 10.100.0.0/24)

When a reserved range is configured, CCM allocates backend ranges itself instead of letting the Linode API auto-assign them. For each new NodeBalancer it picks the lowest /30 in the backend subnet that is not already assigned to a NodeBalancer in the VPC subnet, and never the reserved one. Provisioning fails if:

  • the backend subnet is not within the service’s VPC subnet
  • every /30 other than the reserved one is already assigned
  • the reserved /30 is already assigned to an existing NodeBalancer

The linode-loadbalancer-backend-ipv4-range annotation still takes precedence over this allocation, but a range that overlaps the reserved /30 is rejected:

metadata:
  annotations:
    # rejected when --nodebalancer-backend-ipv4-reserved-range=10.100.0.252/30
    service.beta.kubernetes.io/linode-loadbalancer-backend-ipv4-range: "10.100.0.252/30"

Advanced Configuration

Using Existing NodeBalancers

Specify an existing NodeBalancer:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-nodebalancer-id: "12345"

Reserved IPv4 addresses

Create an new NodeBalancer with an existing Reserved IPv4 Address:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-reserved-ipv4: "100.100.100.100"

The annotation must be present when the Service is created in order to take effect.

Retaining Reserved IPv4 addresses

Retain a reserved IPv4 address when the NodeBalancer is deleted:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-retain-reserved-ipv4: "true"

The default behavior is to retain the reserved IPv4 address when the NodeBalancer is deleted. Setting this annotation to “false” will release the reserved IPv4 address when the NodeBalancer is deleted.

NodeBalancer Preservation

Prevent NodeBalancer deletion when service is deleted:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-preserve: "true"

Port Configuration

Configure individual ports:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-port-443: |
      {
        "protocol": "https",
        "tls-secret-name": "my-tls-secret",
        "proxy-protocol": "v2"
      }

Tags

Add tags to NodeBalancer:

metadata:
  annotations:
    service.beta.kubernetes.io/linode-loadbalancer-tags: "production,web-tier"

Excluding nodes from nodebalancer

Add a label to the node object to exclude

apiVersion: v1
kind: Node
metadata:
  name: node-to-exclude
  labels:
    node.kubernetes.io/exclude-from-external-load-balancers: "true"

This site uses Just the Docs, a documentation theme for Jekyll.