Custom Domains

Every service gets a platform hostname automatically on deploy ({service}.tawa.pro, {service}.sandbox.tawa.pro, {service}.uat.tawa.pro). To put your own domain in front of a service, use tawa domain. There are three DNS modes, picked by how the domain's DNS is managed and whether Cloudflare can be used.

Mode comparison

--cloudflare--external--direct (BYO-DNS)
Customer DNS recordCF auto-creates CNAMECNAME → platform hostA record → ingress LB IP
TLS terminated atCloudflare edgeCloudflare edgeIn-cluster (cert-manager / Let's Encrypt)
Apex/root domainsCF flatteninghard (apex CNAME)native (A record is legal at apex)
CDN / WAF / DDoSyesyesno (origin hit directly)
verify checks forn/a (auto)CNAME → targetA → ingress LB IP
Needs CF zone / NS on CFyesyes (for TLS)no

Default to --cloudflare. Use --direct only when the customer cannot move their nameservers to Cloudflare (e.g. DNS stuck at HostGator/cPanel) but can still set an A record.

Commands

# Cloudflare-managed (auto DNS + TLS) — preferred
tawa domain add portal.example.com --cloudflare

# External CNAME (you add the CNAME; TLS still at Cloudflare)
tawa domain add portal.example.com --external

# Direct A record (BYO-DNS, TLS issued in-cluster) — for when Cloudflare is not an option
tawa domain add example.com --direct
tawa domain verify example.com
tawa deploy --prod

tawa domain list
tawa domain status example.com
tawa domain remove example.com

Direct mode (BYO-DNS) — how it works

  1. tawa domain add <domain> --direct registers the domain on the service with dnsProvider: 'direct'. The expectedTarget is the cluster ingress LoadBalancer IP (INGRESS_TARGET on the builder). The CLI prints the A record to add.
  2. The customer adds an A record for the domain (and www, registered separately) pointing at that IP. Apex domains work natively — no CNAME flattening needed.
  3. tawa domain verify <domain> checks that the A record resolves to the ingress LB IP (not a CNAME). On success the record is marked dnsVerified.
  4. tawa deploy injects the verified direct-mode hosts into the service's builder-managed ingress, adds a tls block (secretName: {service}-custom-tls) and the annotation cert-manager.io/cluster-issuer: {CERT_MANAGER_CLUSTER_ISSUER} (default letsencrypt-prod).
  5. cert-manager solves a Let's Encrypt HTTP-01 challenge over port 80 (which works precisely because the A record already lands on the ingress), issues the cert into the TLS secret, and auto-renews it (~30 days before expiry). No Cloudflare, no manual cert work.

Cloudflare/external domains are not given an in-cluster TLS block — Cloudflare terminates their TLS. Only direct domains get cert-manager TLS.

Platform requirements for direct mode

  • cert-manager must be installed in the cluster with a ClusterIssuer named to match CERT_MANAGER_CLUSTER_ISSUER (default letsencrypt-prod, HTTP-01 solver on ingressClassName: nginx).
  • INGRESS_TARGET on the builder must be set to the ingress LB IP. tawa domain add --direct fails with DIRECT_MODE_UNAVAILABLE if it is unset or not an IP.

Trade-offs / operational notes

  • LB IP coupling. Direct-mode customers hardcode the ingress LB IP in their DNS. If that IP ever changes (cluster migration, LB recreation) every direct-mode domain breaks. Pin a reserved/floating IP for the ingress LoadBalancer before relying on this at scale.
  • No edge protection. Direct-mode traffic hits the origin raw — no Cloudflare CDN, WAF, or DDoS shielding, and the origin IP is public. Fine for static marketing sites; weigh it for anything sensitive.
  • Renewal dependency. HTTP-01 re-validates every ~60 days, so the A record must stay pointed at the platform and port 80 must remain reachable.
  • TLS for non-direct hosts on the same ingress is unaffected — nginx only force-redirects / serves certs for hosts present in a tls block.

Apex + www

Direct mode registers one record per hostname. For apex + www, add both:

tawa domain add example.com --direct
tawa domain add www.example.com --direct
tawa domain verify example.com && tawa domain verify www.example.com
tawa deploy --prod

Both hosts are added to the same {service}-custom-tls secret, so a single Let's Encrypt cert covers them.

Last updated: July 15, 2026