Skip to content

DNS Configuration

Giving the allocator a hostname instead of a bare IP, using AWS Route53.

This page covers the DNS setup around LabLink — hosted zones, NS delegation, and which record OpenTofu creates. For the dns: and ssl: config keys themselves, their validation rules, and copy-paste config examples, see Configuration. For diagnosing a deployment that is already broken, see Troubleshooting.

DNS is optional. With dns.enabled: false the allocator is reachable at its Elastic IP over HTTP, and none of this page applies.

If dns.enabled: true and dns.terraform_managed: true are already in your config, lablink setup does the Route53 work for you:

lablink setup

It finds or creates a hosted zone using the last two labels of dns.domain (example.com for test.example.com), writes the resulting dns.zone_id back into your config file, and — if it created the zone — prints the four AWS nameservers to hand to your registrar.

Your config file is ~/.lablink/config.yaml for CLI deployments, or lablink-infrastructure/config/config.yaml in a lablink-template checkout.

Not for delegated subdomains

lablink setup uses the last two labels, which is not always the registrable domain (for example, lab.example.ac.uk becomes ac.uk). For test.lablink.example.com it targets example.com, not lablink.example.com. If you only control a delegated subdomain, use the setup below and set zone_id yourself.

Delegated subdomain (parent domain managed elsewhere)

The usual situation for a lab: example.com lives in Cloudflare or with university IT, and you want lablink.example.com under your own control. Hand that one subdomain to Route53 with an NS delegation.

Why bother — OpenTofu can then create and destroy the allocator's A record on its own, with no third-party DNS credentials in your deployment, and Let's Encrypt can validate the domain without anyone touching the parent zone.

1. Create the subdomain zone in Route53:

aws route53 create-hosted-zone \
  --name lablink.example.com \
  --caller-reference "lablink-$(date +%s)"

# Note the zone ID and the four nameservers from the response
aws route53 get-hosted-zone --id <zone-id> \
  --query 'DelegationSet.NameServers'

2. Delegate from the parent zone. At whoever hosts example.com, add four NS records:

Field Value
Type NS
Name lablink
Content the four awsdns nameservers from step 1 (one record each)
TTL 300

3. Verify, after 5–15 minutes:

dig NS lablink.example.com     # must return the four awsdns nameservers

If it still returns the parent's nameservers, the delegation has not taken effect — see Troubleshooting for the NS delegation checks.

4. Point your config at that zone explicitly:

dns:
  enabled: true
  terraform_managed: true
  domain: "test.lablink.example.com"
  zone_id: "Z0123456789ABCDEFGHIJ"    # the subdomain zone from step 1

Always set zone_id

When zone_id is empty, OpenTofu looks the zone up by name — and it strips exactly one label off dns.domain to guess the zone. So test.lablink.example.com is looked up as lablink.example.com, and the deploy fails outright if that intermediate zone does not exist. Setting zone_id skips the lookup and removes the guess.

# Find it for an existing zone
aws route53 list-hosted-zones \
  --query "HostedZones[?Name=='lablink.example.com.'].Id" --output text

What OpenTofu creates

With dns.enabled: true and dns.terraform_managed: true, the template creates one record in zone_id, named dns.domain:

ssl.provider Record
none, letsencrypt, cloudflare A record, TTL 300, pointing at the allocator's Elastic IP
acm A alias record pointing at the ALB (no EIP record is created)

With terraform_managed: false no record is created at all — you are expected to point the hostname at the allocator yourself, in whatever DNS you use. This is required for ssl.provider: "cloudflare".

SSL

The host installs Caddy for none, letsencrypt, and cloudflare; only acm omits it. With ssl.provider: "letsencrypt", Caddy requests a certificate on first boot and renews it automatically. It needs the DNS record resolving publicly and ports 80 and 443 open.

For Let's Encrypt certificate limits during repeated deployments, see Configuration.

Certificate problems show up in Caddy's log on the allocator:

ssh -i ~/lablink-key.pem ubuntu@<allocator-ip>
sudo journalctl -u caddy -f

The provider comparison — none, letsencrypt, cloudflare, acm — is in Configuration.

Verifying a deployment

The template repo ships a script that checks DNS, HTTP, and SSL together:

./scripts/verify-deployment.sh prod            # reads config.yaml + tofu outputs
./scripts/verify-deployment.sh <domain> <ip>   # or pass them explicitly

References