Hanzo

Domains & TLS

Add custom domains to your deployments with automatic TLS certificates from Let's Encrypt via cert-manager.

Every container with ingress enabled gets a platform-generated URL. You can also add custom domains with automatic TLS certificates issued by Let's Encrypt.

Default URLs

When ingress is enabled, the platform assigns a URL based on the ingress type:

Ingress TypeURL Pattern
subdomain<container-slug>.<cluster-domain>
path<cluster-domain>/<container-slug>

Adding a Custom Domain

Register the Domain

Navigate to your cluster's Domains settings and add the domain:

# Platform UI: Cluster → Settings → Domains → Add Domain
# API:
domain.add({ clusterId: "cls-xyz", domain: "api.example.com" })

Only organization Admins and Owners can add or remove domains.

Configure DNS

Point your domain to the cluster's ingress IP address. Create an A record (or CNAME for subdomains) at your DNS provider:

# A record — for apex domains
api.example.com.    A    134.199.141.68

# CNAME — for subdomains pointing to a cluster domain
app.example.com.    CNAME    cluster.platform.hanzo.ai.

For wildcard domains, create a wildcard DNS record:

*.example.com.    A    134.199.141.68

Enable on the Container

Set the custom domain in the container's networking config:

networking:
  containerPort: 8080
  ingress:
    enabled: true
  customDomain:
    enabled: true
    domain: "api.example.com"

TLS Certificate Provisioning

The platform automatically provisions a TLS certificate via cert-manager and Let's Encrypt. No manual steps required.

TLS with cert-manager

The platform uses cert-manager with Let's Encrypt for automatic TLS:

How It Works

Domain added → Ingress created → cert-manager detects →
ACME challenge → Let's Encrypt issues cert → cert stored as K8s Secret

ClusterIssuer

Each cluster is configured with a ClusterIssuer for Let's Encrypt production:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: ops@example.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
      - http01:
          ingress:
            class: nginx

Ingress Annotation

The platform annotates ingress resources to trigger certificate issuance:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-xk8f3m2n
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  tls:
    - hosts:
        - api.example.com
      secretName: api-example-com-tls
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: api-xk8f3m2n
                port:
                  number: 8080

Certificate Renewal

cert-manager automatically renews certificates 30 days before expiry. No intervention required.

Wildcard Certificates

For wildcard domains (*.example.com), cert-manager uses DNS-01 challenges instead of HTTP-01:

solvers:
  - dns01:
      cloudflare:
        email: ops@example.com
        apiTokenSecretRef:
          name: cloudflare-api-token
          key: api-token
    selector:
      dnsZones:
        - "example.com"

Wildcard certificates require a DNS provider integration (Cloudflare, Route53, Cloud DNS, etc.). The platform supports Cloudflare out of the box. For other providers, configure the DNS solver in your cluster's cert-manager settings.

Cloudflare Integration

If your domain uses Cloudflare (recommended), configure SSL mode to Full so Cloudflare trusts the Let's Encrypt certificate on your origin:

Client → Cloudflare (edge TLS) → Origin (Let's Encrypt TLS)
Cloudflare SSL ModeBehavior
OffNo encryption (not recommended)
FlexibleCloudflare terminates TLS, plain HTTP to origin
FullTLS on both legs, origin cert not validated
Full (Strict)TLS on both legs, origin cert must be valid

Use Full or Full (Strict) with Let's Encrypt certificates.

When using Cloudflare proxy (orange cloud), disable PROXY protocol on your ingress controller. The platform's K8s clusters are pre-configured for this.

DNS Configuration Examples

# Using the Cloudflare dashboard or API:
# Type: A
# Name: api
# Content: 134.199.141.68
# Proxy: Proxied (orange cloud)
# TTL: Auto
# AWS CLI:
aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890 \
  --change-batch '{
    "Changes": [{
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "api.example.com",
        "Type": "A",
        "TTL": 300,
        "ResourceRecords": [{"Value": "134.199.141.68"}]
      }
    }]
  }'

Add an A record at your DNS provider:

TypeNameValueTTL
Aapi134.199.141.68300

Or for a CNAME:

TypeNameValueTTL
CNAMEappcluster.platform.hanzo.ai300

Removing a Domain

Remove a domain from a cluster to stop serving traffic on it:

domain.remove({ domainId: "dom-xyz" })

This deletes the ingress rule and TLS certificate. Existing DNS records must be cleaned up separately at your DNS provider.

Troubleshooting

SymptomCauseFix
Certificate pending for > 5 minutesDNS not propagatedVerify A/CNAME record resolves to cluster IP
ERR_TOO_MANY_REDIRECTSCloudflare SSL set to FlexibleChange to Full or Full (Strict)
502 Bad GatewayContainer not running or wrong portCheck container status and containerPort
Certificate invalid in browserUsing self-signed certEnsure letsencrypt-prod issuer is configured (not staging)
Wildcard cert not issuingMissing DNS-01 solverConfigure Cloudflare or Route53 DNS solver in cert-manager

How is this guide?

Last updated on

On this page