Skip to main content
KubeLB integrates with external-dns and cert-manager to provide automatic DNS record management and TLS certificate provisioning for your load balancers and routes. This guide covers setup and configuration.

Overview

KubeLB supports DNS automation through:
  • external-dns: Automatically creates DNS records for your load balancers
  • cert-manager: Provisions and manages TLS certificates
  • Tenant DNS Settings: Per-tenant configuration for DNS and certificates

Architecture

DNS and certificate management works as follows:
  1. Service/Route Creation: You create a LoadBalancer service or HTTP route
  2. DNS Annotations: KubeLB adds external-dns annotations to resources
  3. DNS Record Creation: external-dns watches for annotations and creates DNS records
  4. Certificate Request: cert-manager provisions TLS certificates
  5. Certificate Storage: Certificates are stored as Kubernetes secrets
external-dns and cert-manager must be installed and configured in the management cluster.

DNS Configuration

Tenant DNS Settings

Configure DNS behavior for a tenant:
apiVersion: kubelb.k8c.io/v1alpha1
kind: Tenant
metadata:
  name: tenant-cluster-1
spec:
  dns:
    # Base domain for wildcard DNS records
    wildcardDomain: "apps.example.com"
    
    # Allow explicit hostnames
    allowExplicitHostnames: true
    
    # Add external-dns annotations
    useDNSAnnotations: true
    
    # Add cert-manager annotations
    useCertificateAnnotations: true

Wildcard Domain

When wildcardDomain is set, KubeLB automatically generates hostnames:
apiVersion: v1
kind: Service
metadata:
  name: api-service
  namespace: default
spec:
  type: LoadBalancer
  selector:
    app: api
  ports:
  - port: 80
    targetPort: 8080
With wildcardDomain: "apps.example.com", this creates:
  • Hostname: api-service.default.apps.example.com
  • DNS A record pointing to the load balancer IP

Explicit Hostnames

Use explicit hostnames with LoadBalancer services:
apiVersion: kubelb.k8c.io/v1alpha1
kind: LoadBalancer
metadata:
  name: my-app-lb
  namespace: tenant-cluster-1
spec:
  hostname: "api.example.com"  # Explicit hostname
  endpoints:
  - addressesReference:
      kind: Addresses
      name: default
  ports:
  - port: 80
    protocol: TCP
Explicit hostnames only work if allowExplicitHostnames: true in the Tenant configuration.

external-dns Integration

Setup

1

Install external-dns

Install external-dns in the management cluster:
helm repo add external-dns https://kubernetes-sigs.github.io/external-dns/
helm install external-dns external-dns/external-dns \\
  --namespace external-dns \\
  --create-namespace \\
  --set provider=aws \\
  --set txtOwnerId=kubelb-cluster
See external-dns documentation for provider-specific configuration.
2

Configure DNS Provider

Configure credentials for your DNS provider (AWS Route53, Google Cloud DNS, etc.).For AWS Route53:
# Create secret with AWS credentials
apiVersion: v1
kind: Secret
metadata:
  name: external-dns-aws
  namespace: external-dns
type: Opaque
data:
  aws-access-key-id: <base64-encoded-access-key>
  aws-secret-access-key: <base64-encoded-secret-key>
3

Enable DNS Annotations in Tenant

apiVersion: kubelb.k8c.io/v1alpha1
kind: Tenant
metadata:
  name: tenant-cluster-1
spec:
  dns:
    wildcardDomain: "apps.example.com"
    useDNSAnnotations: true

DNS Annotations

KubeLB propagates external-dns annotations:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-ingress
  namespace: default
  annotations:
    external-dns.alpha.kubernetes.io/hostname: myapp.example.com
    external-dns.alpha.kubernetes.io/ttl: "300"
spec:
  ingressClassName: kubelb
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

Verifying DNS Records

Check that DNS records are created:
# Check external-dns logs
kubectl logs -n external-dns -l app.kubernetes.io/name=external-dns

# Query DNS
dig myapp.example.com
nslookup myapp.example.com

# Check for TXT records (ownership records)
dig TXT myapp.example.com

cert-manager Integration

Setup

1

Install cert-manager

Install cert-manager in the management cluster:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml
2

Create ClusterIssuer

Create a ClusterIssuer for Let’s Encrypt:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
    # HTTP-01 solver
    - http01:
        ingress:
          class: kubelb
    # DNS-01 solver for wildcard certs
    - dns01:
        route53:
          region: us-east-1
          accessKeyID: <aws-access-key-id>
          secretAccessKeySecretRef:
            name: route53-credentials
            key: secret-access-key
3

Configure Tenant

apiVersion: kubelb.k8c.io/v1alpha1
kind: Tenant
metadata:
  name: tenant-cluster-1
spec:
  certificates:
    defaultClusterIssuer: letsencrypt-prod
  dns:
    useCertificateAnnotations: true

TLS with Ingress

Let cert-manager automatically provision a certificate:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: kubelb
  tls:
  - hosts:
    - secure.example.com
    secretName: secure-tls  # cert-manager will create this
  rules:
  - host: secure.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: secure-service
            port:
              number: 80
cert-manager will:
  1. Create a Certificate resource
  2. Request certificate from Let’s Encrypt
  3. Store certificate in secure-tls secret

TLS with Gateway API

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: tls-gateway
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: kubelb
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    hostname: "*.example.com"
    tls:
      mode: Terminate
      certificateRefs:
      - name: wildcard-tls  # cert-manager will provision
    allowedRoutes:
      namespaces:
        from: All

Wildcard Certificates

Use DNS-01 challenge for wildcard certificates:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-cert
  namespace: default
spec:
  secretName: wildcard-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - "*.apps.example.com"
  - "apps.example.com"
Ensure your ClusterIssuer has DNS-01 solver configured.

Certificate Renewal

cert-manager automatically renews certificates:
# Check certificate status
kubectl get certificate

# Get certificate details
kubectl describe certificate secure-cert

# Check cert-manager logs
kubectl logs -n cert-manager -l app=cert-manager
Certificates are renewed 30 days before expiration by default.

Annotation Propagation

Control which annotations are propagated to load balancers:
apiVersion: kubelb.k8c.io/v1alpha1
kind: Tenant
metadata:
  name: tenant-cluster-1
spec:
  propagateAllAnnotations: false
  propagatedAnnotations:
    # External DNS annotations
    external-dns.alpha.kubernetes.io/hostname: ""
    external-dns.alpha.kubernetes.io/ttl: ""
    external-dns.alpha.kubernetes.io/target: ""
    
    # cert-manager annotations
    cert-manager.io/cluster-issuer: ""
    cert-manager.io/issuer: ""
    cert-manager.io/common-name: ""
    
    # Custom annotations
    my-annotation.example.com/key: "allowed-value"
Empty value ("") allows any value for that annotation key.

Complete Example

Here’s a complete example with DNS and TLS:
1

Configure Tenant

apiVersion: kubelb.k8c.io/v1alpha1
kind: Tenant
metadata:
  name: tenant-cluster-1
spec:
  dns:
    wildcardDomain: "apps.example.com"
    useDNSAnnotations: true
    useCertificateAnnotations: true
  certificates:
    defaultClusterIssuer: letsencrypt-prod
  propagatedAnnotations:
    external-dns.alpha.kubernetes.io/hostname: ""
    cert-manager.io/cluster-issuer: ""
2

Create Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  namespace: default
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    external-dns.alpha.kubernetes.io/hostname: myapp.example.com
spec:
  ingressClassName: kubelb
  tls:
  - hosts:
    - myapp.example.com
    secretName: myapp-tls
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-service
            port:
              number: 80
3

Verify DNS and Certificate

# Wait for DNS propagation
dig myapp.example.com

# Check certificate
kubectl get certificate myapp-tls

# Test HTTPS
curl https://myapp.example.com

Troubleshooting

Check external-dns logs:
kubectl logs -n external-dns -l app.kubernetes.io/name=external-dns
Verify annotations:
kubectl get ingress my-app -o yaml
Look for external-dns.alpha.kubernetes.io/hostname annotation.Check propagated annotations: Ensure the annotation is allowed in Tenant propagatedAnnotations.
Check certificate status:
kubectl get certificate
kubectl describe certificate myapp-tls
Check certificate request:
kubectl get certificaterequest
kubectl describe certificaterequest <request-name>
Check cert-manager logs:
kubectl logs -n cert-manager -l app=cert-manager
Common issues:
  • DNS not propagated yet (for DNS-01 challenge)
  • Ingress not accessible (for HTTP-01 challenge)
  • Rate limits from Let’s Encrypt
Verify certificate secret exists:
kubectl get secret myapp-tls
Check certificate validity:
kubectl get secret myapp-tls -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -text -noout
Test TLS connection:
curl -v https://myapp.example.com
openssl s_client -connect myapp.example.com:443 -servername myapp.example.com

Best Practices

Use Staging Issuer First

Test with Let’s Encrypt staging to avoid rate limits:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-staging-key
    solvers:
    - http01:
        ingress:
          class: kubelb

Wildcard Certificates

Use wildcard certificates for multiple subdomains:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-cert
  namespace: default
spec:
  secretName: wildcard-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - "*.example.com"
  - "example.com"

Monitor Certificate Expiration

Set up alerts for certificate expiration:
# Certificates expiring in less than 7 days
certmanager_certificate_expiration_timestamp_seconds - time() < 7 * 24 * 3600

Next Steps

Build docs developers (and LLMs) love