DNS Entries
The dns resource type configures DNS records for your environment services. Teabar automatically manages DNS records and cleans them up when environments are destroyed.
Basic Usage
spec:
resources:
dns:
- name: app
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}" This creates an A record at app.<environment-domain> pointing to your cluster’s ingress IP.
Schema Reference
dns:
- name: string # Required: Record name (subdomain)
type: string # Required: Record type (A, AAAA, CNAME, TXT, MX)
target: string # Required: Record value (supports templating)
enabled: boolean # Optional: Enable/disable this record (default: true)
ttl: integer # Optional: TTL in seconds (default: 300)
priority: integer # Optional: Priority for MX records
proxied: boolean # Optional: Enable Cloudflare proxy (Cloudflare only)
count: integer # Optional: Create multiple records with {{ .Index }}
dependsOn: # Optional: Resource dependencies
- string Record Types
A Records
Point a hostname to an IPv4 address:
dns:
- name: app
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
ttl: 300
- name: api
type: A
target: "{{ .Resources.vms.api-server.public_ip }}" AAAA Records
Point a hostname to an IPv6 address:
dns:
- name: app
type: AAAA
target: "{{ .Resources.clusters.main.ingress_ipv6 }}" CNAME Records
Create an alias to another hostname:
dns:
- name: www
type: CNAME
target: app
- name: docs
type: CNAME
target: "{{ .Environment.Domain }}" Note
Wildcard Records
Route all subdomains to a single target:
dns:
- name: "*"
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
# Or as CNAME
- name: "*.apps"
type: CNAME
target: app TXT Records
Add text records for verification or configuration:
dns:
- name: _dmarc
type: TXT
target: "v=DMARC1; p=none; rua=mailto:[email protected]"
- name: "@"
type: TXT
target: "v=spf1 include:_spf.google.com ~all" MX Records
Configure mail servers:
dns:
- name: "@"
type: MX
target: mail.example.com
priority: 10
- name: "@"
type: MX
target: mail-backup.example.com
priority: 20 Environment Domain
Every Teabar environment gets a unique domain under teabar.dev:
<environment-id>.teabar.dev Your DNS records create subdomains under this:
dns:
- name: app
type: A
target: "1.2.3.4"
# Creates: app.<environment-id>.teabar.dev
- name: gitlab
type: A
target: "1.2.3.5"
# Creates: gitlab.<environment-id>.teabar.dev Access the domain in templates:
# Reference in other resources
helm:
- name: gitlab
values:
global:
hosts:
domain: "{{ .Environment.Domain }}"
gitlab:
name: "gitlab.{{ .Environment.Domain }}" Dynamic DNS
Reference Other Resources
Create DNS records pointing to dynamically provisioned resources:
spec:
resources:
clusters:
- name: main
provider: hetzner
type: talos
features:
- ingress-nginx
vms:
- name: bastion
provider: hetzner
type: cx21
dns:
# Cluster ingress
- name: app
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
# Wildcard for all ingress routes
- name: "*.app"
type: CNAME
target: app
# VM access
- name: bastion
type: A
target: "{{ .Resources.vms.bastion.public_ip }}" Per-Participant Records
Create DNS records for each participant:
spec:
variables:
- name: participant_count
type: integer
default: 10
resources:
vms:
- name: "workstation-{{ .Index }}"
count: "{{ .Variables.participant_count }}"
provider: hetzner
type: cx21
dns:
- name: "p{{ .Index }}"
type: A
count: "{{ .Variables.participant_count }}"
target: "{{ (index .Resources.vms (printf "workstation-%d" .Index)).public_ip }}" This creates:
p0.<env-domain>→ workstation-0 IPp1.<env-domain>→ workstation-1 IP- …
Common Patterns
GitLab with Subdomains
GitLab requires multiple DNS entries for full functionality:
dns:
# Main GitLab
- name: gitlab
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
# GitLab Pages
- name: "*.pages"
type: CNAME
target: gitlab
# Container Registry
- name: registry
type: CNAME
target: gitlab
# Mattermost (if enabled)
- name: mattermost
type: CNAME
target: gitlab Multi-Service Application
dns:
# API gateway
- name: api
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
# Frontend
- name: www
type: CNAME
target: api
# Root domain redirect
- name: "@"
type: CNAME
target: www
# Documentation
- name: docs
type: CNAME
target: api
# Admin panel
- name: admin
type: CNAME
target: api Separate Services on Different Clusters
spec:
resources:
clusters:
- name: frontend
provider: hetzner
type: talos
features:
- ingress-nginx
- name: backend
provider: hetzner
type: talos
features:
- ingress-nginx
dns:
- name: app
type: A
target: "{{ .Resources.clusters.frontend.ingress_ip }}"
- name: api
type: A
target: "{{ .Resources.clusters.backend.ingress_ip }}" TTL Configuration
Set appropriate TTL (Time To Live) values:
dns:
# Short TTL for frequently changing services
- name: dynamic
type: A
target: "{{ .Resources.vms.dynamic.public_ip }}"
ttl: 60
# Standard TTL for stable services
- name: app
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
ttl: 300
# Long TTL for static records
- name: docs
type: CNAME
target: docs.example.com
ttl: 3600 Tip
Provider Support
Teabar-Managed DNS
By default, Teabar manages DNS under the teabar.dev domain:
dns:
- name: app
type: A
target: "1.2.3.4"
# Result: app.<env-id>.teabar.dev Hetzner DNS
Use your own domain managed by Hetzner DNS:
spec:
environment:
dns:
provider: hetzner
zone: training.example.com
resources:
dns:
- name: app
type: A
target: "1.2.3.4"
# Result: app.training.example.com AWS Route 53
spec:
environment:
dns:
provider: aws
zone: Z1234567890ABC # Hosted zone ID
domain: training.example.com
resources:
dns:
- name: app
type: A
target: "1.2.3.4" Azure DNS
spec:
environment:
dns:
provider: azure
resourceGroup: my-dns-rg
zone: training.example.com
resources:
dns:
- name: app
type: A
target: "1.2.3.4" Cloudflare
spec:
environment:
dns:
provider: cloudflare
zone: example.com
resources:
dns:
- name: app
type: A
target: "1.2.3.4"
proxied: true # Enable Cloudflare proxy/CDN DNS Outputs
Reference DNS records in other resources:
spec:
resources:
dns:
- name: app
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
manifests:
- name: app-config
cluster: main
template: |
apiVersion: v1
kind: ConfigMap
metadata:
name: app-urls
data:
app_url: "https://{{ .Resources.dns.app.fqdn }}"
# Result: https://app.<env-id>.teabar.dev Available DNS outputs:
| Output | Description |
|---|---|
.fqdn | Fully qualified domain name |
.name | Record name (subdomain) |
.type | Record type |
.target | Record value |
.ttl | TTL in seconds |
Best Practices
Use CNAMEs for Flexibility
Prefer CNAME records when possible:
dns:
# Single A record
- name: ingress
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}"
# Multiple CNAMEs pointing to it
- name: app
type: CNAME
target: ingress
- name: api
type: CNAME
target: ingress
- name: admin
type: CNAME
target: ingress If the IP changes, you only update one record.
Wildcard for Ingress
Use wildcards for Kubernetes ingress:
dns:
- name: "*"
type: A
target: "{{ .Resources.clusters.main.ingress_ip }}" This allows any ingress hostname to work without additional DNS configuration.
Document Your DNS
Use consistent naming conventions:
dns:
# Primary services
- name: app # Main application
- name: api # API endpoint
- name: admin # Admin interface
# Infrastructure
- name: gitlab # Git/CI
- name: grafana # Monitoring
- name: argocd # GitOps
# Participant access
- name: "p{{ .Index }}" # Participant workstations Related Resources
- Clusters - Kubernetes clusters with ingress
- VMs - Virtual machines
- Helm Releases - Deploy applications with DNS
- Templating - Dynamic DNS configuration