Point www and apex cpressland.io at ingress #15

Merged
cpressland merged 1 commit from www-apex-cname-flatten into main 2026-09-05 00:06:58 +01:00
Collaborator

Hetzner DNS (hcloud provider) does not support CNAME flattening/ALIAS records at the zone apex, so this uses the cleanest equivalent:

  • www gets a CNAME to ingress, matching every other homelab subdomain (audiobooks, immich, git, etc).
  • The apex (@) gets its own A/AAAA pair with ingress's current values (82.39.95.203 / 2a11:2646:1337:1::10) copied directly, since an apex CNAME is invalid alongside the existing MX/TXT/dmarc records at @ anyway.
  • Removes the old GitHub Pages www/apex records (CNAME to cpressland.github.io. + the github_ip_ranges A/AAAA) now that the site is served from the homelab instead of GitHub Pages.

Depends on / pairs with the cpressland/homelab PR adding the site container. Note: these A/AAAA values are a manual copy of ingress's records, not a live reference — if ingress's IPs ever change, www/apex need updating too.

Hetzner DNS (hcloud provider) does not support CNAME flattening/ALIAS records at the zone apex, so this uses the cleanest equivalent: - `www` gets a CNAME to `ingress`, matching every other homelab subdomain (audiobooks, immich, git, etc). - The apex (`@`) gets its own A/AAAA pair with ingress's current values (`82.39.95.203` / `2a11:2646:1337:1::10`) copied directly, since an apex CNAME is invalid alongside the existing MX/TXT/dmarc records at `@` anyway. - Removes the old GitHub Pages www/apex records (CNAME to `cpressland.github.io.` + the `github_ip_ranges` A/AAAA) now that the site is served from the homelab instead of GitHub Pages. Depends on / pairs with the `cpressland/homelab` PR adding the `site` container. Note: these A/AAAA values are a manual copy of `ingress`'s records, not a live reference — if ingress's IPs ever change, `www`/apex need updating too.
Point www and apex cpressland.io at ingress
All checks were successful
OpenTofu Plan / plan (pull_request) Successful in 7s
OpenTofu Apply / apply (push) Successful in 14s
65ea6569e6
Hetzner DNS doesn't support CNAME flattening/ALIAS at the apex, so
www gets a CNAME to ingress (matching the other homelab subdomains)
and the apex gets its own A/AAAA pair mirroring ingress's current
values directly (an apex CNAME isn't valid alongside the existing
MX/TXT records anyway).

Removes the old GitHub Pages www/apex records now that the site is
served from the homelab instead.

📝 OpenTofu plan: changes detected

Plan: 2 to add, 1 to change, 2 to destroy.

Show plan output
data.github_ip_ranges.i: Reading...
hcloud_ssh_key.i: Refreshing state... [id=111080408]
hcloud_zone.cpressland_io: Refreshing state... [name=cpressland.io]
hcloud_zone.cpressland_com: Refreshing state... [name=cpressland.com]
hcloud_zone.bakacat_com: Refreshing state... [name=bakacat.com]
hcloud_storage_box.backups: Refreshing state... [name=backups]
hcloud_zone.binky_casa: Refreshing state... [name=binky.casa]
hcloud_zone_rrset.cpressland_com["jellyfin"]: Refreshing state... [id=jellyfin/CNAME]
hcloud_zone_rrset.cpressland_com["git"]: Refreshing state... [id=git/CNAME]
hcloud_zone_rrset.cpressland_com["navidrome"]: Refreshing state... [id=navidrome/CNAME]
hcloud_zone_rrset.cpressland_io["cname_git"]: Refreshing state... [id=git/CNAME]
hcloud_zone_rrset.cpressland_io["txt_dmarc_report_binky_casa"]: Refreshing state... [id=binky.casa._report._dmarc/TXT]
hcloud_zone_rrset.cpressland_io["aaaa_ingress"]: Refreshing state... [id=ingress/AAAA]
hcloud_zone_rrset.cpressland_io["cname_chds"]: Refreshing state... [id=chds/CNAME]
hcloud_zone_rrset.cpressland_io["cname_audiobooks"]: Refreshing state... [id=audiobooks/CNAME]
hcloud_zone_rrset.cpressland_io["txt_bluesky"]: Refreshing state... [id=_atproto/TXT]
hcloud_zone_rrset.cpressland_io["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT]
hcloud_zone_rrset.cpressland_io["cname_meshcore"]: Refreshing state... [id=meshcore/CNAME]
hcloud_zone_rrset.cpressland_io["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME]
hcloud_zone_rrset.cpressland_io["aaaa_vpn"]: Refreshing state... [id=vpn/AAAA]
hcloud_zone_rrset.cpressland_io["cname_homeassistant"]: Refreshing state... [id=homeassistant/CNAME]
hcloud_zone_rrset.cpressland_io["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME]
hcloud_zone_rrset.cpressland_io["cname_brewery"]: Refreshing state... [id=brewery/CNAME]
hcloud_zone_rrset.cpressland_io["cname_seerr"]: Refreshing state... [id=seerr/CNAME]
hcloud_zone_rrset.cpressland_io["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME]
hcloud_zone_rrset.cpressland_io["a_ingress"]: Refreshing state... [id=ingress/A]
hcloud_zone_rrset.cpressland_io["cname_www"]: Refreshing state... [id=www/CNAME]
hcloud_zone_rrset.cpressland_io["a_www"]: Refreshing state... [id=@/A]
hcloud_zone_rrset.cpressland_io["aaaa_www"]: Refreshing state... [id=@/AAAA]
data.github_ip_ranges.i: Read complete after 1s [id=github-ip-ranges]
hcloud_zone_rrset.cpressland_io["a_vpn"]: Refreshing state... [id=vpn/A]
hcloud_zone_rrset.cpressland_io["mx_fastmail"]: Refreshing state... [id=@/MX]
hcloud_zone_rrset.cpressland_io["txt_dmarc_report_bakacat_com"]: Refreshing state... [id=bakacat.com._report._dmarc/TXT]
hcloud_zone_rrset.cpressland_io["cname_navidrome"]: Refreshing state... [id=navidrome/CNAME]
hcloud_zone_rrset.cpressland_io["cname_jellyfin"]: Refreshing state... [id=jellyfin/CNAME]
hcloud_zone_rrset.cpressland_io["txt_root"]: Refreshing state... [id=@/TXT]
hcloud_zone_rrset.cpressland_io["cname_iperf"]: Refreshing state... [id=iperf/CNAME]
hcloud_zone_rrset.cpressland_io["cname_immich"]: Refreshing state... [id=immich/CNAME]
hcloud_zone_rrset.cpressland_io["cname_files"]: Refreshing state... [id=files/CNAME]
hcloud_zone_rrset.bakacat_com["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME]
hcloud_zone_rrset.bakacat_com["txt_spf"]: Refreshing state... [id=@/TXT]
hcloud_zone_rrset.bakacat_com["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME]
hcloud_zone_rrset.bakacat_com["mx_fastmail"]: Refreshing state... [id=@/MX]
hcloud_zone_rrset.bakacat_com["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME]
hcloud_zone_rrset.bakacat_com["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT]
hcloud_zone_rrset.binky_casa["mx_fastmail"]: Refreshing state... [id=@/MX]
hcloud_zone_rrset.binky_casa["aaaa_www"]: Refreshing state... [id=@/AAAA]
hcloud_zone_rrset.binky_casa["cname_www"]: Refreshing state... [id=www/CNAME]
hcloud_zone_rrset.binky_casa["txt"]: Refreshing state... [id=@/TXT]
hcloud_zone_rrset.binky_casa["txt_bluesky"]: Refreshing state... [id=_atproto/TXT]
hcloud_zone_rrset.binky_casa["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME]
hcloud_zone_rrset.binky_casa["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT]
hcloud_zone_rrset.binky_casa["a_www"]: Refreshing state... [id=@/A]
hcloud_zone_rrset.binky_casa["cname_db"]: Refreshing state... [id=db/CNAME]
hcloud_zone_rrset.binky_casa["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME]
hcloud_zone_rrset.binky_casa["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME]

OpenTofu used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place (current -> planned)
  - destroy

OpenTofu will perform the following actions:

  # hcloud_zone_rrset.cpressland_io["a_root"] will be created
  + resource "hcloud_zone_rrset" "cpressland_io" {
      + change_protection = (known after apply)
      + id                = (known after apply)
      + labels            = {}
      + name              = "@"
      + records           = [
          + {
              + value = "82.39.95.203"
            },
        ]
      + ttl               = 3600
      + type              = "A"
      + zone              = "cpressland.io"
    }

  # hcloud_zone_rrset.cpressland_io["a_www"] will be destroyed
  # (because key ["a_www"] is not in for_each map)
  - resource "hcloud_zone_rrset" "cpressland_io" {
      - change_protection = false -> null
      - id                = "@/A" -> null
      - labels            = {} -> null
      - name              = "@" -> null
      - records           = [
          - {
              - value = "185.199.108.153" -> null
            },
          - {
              - value = "185.199.109.153" -> null
            },
          - {
              - value = "185.199.110.153" -> null
            },
          - {
              - value = "185.199.111.153" -> null
            },
          - {
              - value = "192.30.252.153" -> null
            },
          - {
              - value = "192.30.252.154" -> null
            },
        ] -> null
      - ttl               = 3600 -> null
      - type              = "A" -> null
      - zone              = "cpressland.io" -> null
    }

  # hcloud_zone_rrset.cpressland_io["aaaa_root"] will be created
  + resource "hcloud_zone_rrset" "cpressland_io" {
      + change_protection = (known after apply)
      + id                = (known after apply)
      + labels            = {}
      + name              = "@"
      + records           = [
          + {
              + value = "2a11:2646:1337:1::10"
            },
        ]
      + ttl               = 3600
      + type              = "AAAA"
      + zone              = "cpressland.io"
    }

  # hcloud_zone_rrset.cpressland_io["aaaa_www"] will be destroyed
  # (because key ["aaaa_www"] is not in for_each map)
  - resource "hcloud_zone_rrset" "cpressland_io" {
      - change_protection = false -> null
      - id                = "@/AAAA" -> null
      - labels            = {} -> null
      - name              = "@" -> null
      - records           = [
          - {
              - value = "2606:50c0:8000::153" -> null
            },
          - {
              - value = "2606:50c0:8001::153" -> null
            },
          - {
              - value = "2606:50c0:8002::153" -> null
            },
          - {
              - value = "2606:50c0:8003::153" -> null
            },
        ] -> null
      - ttl               = 3600 -> null
      - type              = "AAAA" -> null
      - zone              = "cpressland.io" -> null
    }

  # hcloud_zone_rrset.cpressland_io["cname_www"] will be updated in-place
  ~ resource "hcloud_zone_rrset" "cpressland_io" {
      ~ change_protection = false -> (known after apply)
        id                = "www/CNAME"
        name              = "www"
      ~ records           = [
          - {
              - value = "cpressland.github.io." -> null
            },
          + {
              + value = "ingress"
            },
        ]
        # (4 unchanged attributes hidden)
    }

Plan: 2 to add, 1 to change, 2 to destroy.

─────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so OpenTofu can't
guarantee to take exactly these actions if you run "tofu apply" now.

Workflow run: 646

<!-- tofu-plan-comment --> ## 📝 OpenTofu plan: changes detected Plan: 2 to add, 1 to change, 2 to destroy. <details> <summary>Show plan output</summary> ``` data.github_ip_ranges.i: Reading... hcloud_ssh_key.i: Refreshing state... [id=111080408] hcloud_zone.cpressland_io: Refreshing state... [name=cpressland.io] hcloud_zone.cpressland_com: Refreshing state... [name=cpressland.com] hcloud_zone.bakacat_com: Refreshing state... [name=bakacat.com] hcloud_storage_box.backups: Refreshing state... [name=backups] hcloud_zone.binky_casa: Refreshing state... [name=binky.casa] hcloud_zone_rrset.cpressland_com["jellyfin"]: Refreshing state... [id=jellyfin/CNAME] hcloud_zone_rrset.cpressland_com["git"]: Refreshing state... [id=git/CNAME] hcloud_zone_rrset.cpressland_com["navidrome"]: Refreshing state... [id=navidrome/CNAME] hcloud_zone_rrset.cpressland_io["cname_git"]: Refreshing state... [id=git/CNAME] hcloud_zone_rrset.cpressland_io["txt_dmarc_report_binky_casa"]: Refreshing state... [id=binky.casa._report._dmarc/TXT] hcloud_zone_rrset.cpressland_io["aaaa_ingress"]: Refreshing state... [id=ingress/AAAA] hcloud_zone_rrset.cpressland_io["cname_chds"]: Refreshing state... [id=chds/CNAME] hcloud_zone_rrset.cpressland_io["cname_audiobooks"]: Refreshing state... [id=audiobooks/CNAME] hcloud_zone_rrset.cpressland_io["txt_bluesky"]: Refreshing state... [id=_atproto/TXT] hcloud_zone_rrset.cpressland_io["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT] hcloud_zone_rrset.cpressland_io["cname_meshcore"]: Refreshing state... [id=meshcore/CNAME] hcloud_zone_rrset.cpressland_io["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME] hcloud_zone_rrset.cpressland_io["aaaa_vpn"]: Refreshing state... [id=vpn/AAAA] hcloud_zone_rrset.cpressland_io["cname_homeassistant"]: Refreshing state... [id=homeassistant/CNAME] hcloud_zone_rrset.cpressland_io["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME] hcloud_zone_rrset.cpressland_io["cname_brewery"]: Refreshing state... [id=brewery/CNAME] hcloud_zone_rrset.cpressland_io["cname_seerr"]: Refreshing state... [id=seerr/CNAME] hcloud_zone_rrset.cpressland_io["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME] hcloud_zone_rrset.cpressland_io["a_ingress"]: Refreshing state... [id=ingress/A] hcloud_zone_rrset.cpressland_io["cname_www"]: Refreshing state... [id=www/CNAME] hcloud_zone_rrset.cpressland_io["a_www"]: Refreshing state... [id=@/A] hcloud_zone_rrset.cpressland_io["aaaa_www"]: Refreshing state... [id=@/AAAA] data.github_ip_ranges.i: Read complete after 1s [id=github-ip-ranges] hcloud_zone_rrset.cpressland_io["a_vpn"]: Refreshing state... [id=vpn/A] hcloud_zone_rrset.cpressland_io["mx_fastmail"]: Refreshing state... [id=@/MX] hcloud_zone_rrset.cpressland_io["txt_dmarc_report_bakacat_com"]: Refreshing state... [id=bakacat.com._report._dmarc/TXT] hcloud_zone_rrset.cpressland_io["cname_navidrome"]: Refreshing state... [id=navidrome/CNAME] hcloud_zone_rrset.cpressland_io["cname_jellyfin"]: Refreshing state... [id=jellyfin/CNAME] hcloud_zone_rrset.cpressland_io["txt_root"]: Refreshing state... [id=@/TXT] hcloud_zone_rrset.cpressland_io["cname_iperf"]: Refreshing state... [id=iperf/CNAME] hcloud_zone_rrset.cpressland_io["cname_immich"]: Refreshing state... [id=immich/CNAME] hcloud_zone_rrset.cpressland_io["cname_files"]: Refreshing state... [id=files/CNAME] hcloud_zone_rrset.bakacat_com["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME] hcloud_zone_rrset.bakacat_com["txt_spf"]: Refreshing state... [id=@/TXT] hcloud_zone_rrset.bakacat_com["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME] hcloud_zone_rrset.bakacat_com["mx_fastmail"]: Refreshing state... [id=@/MX] hcloud_zone_rrset.bakacat_com["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME] hcloud_zone_rrset.bakacat_com["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT] hcloud_zone_rrset.binky_casa["mx_fastmail"]: Refreshing state... [id=@/MX] hcloud_zone_rrset.binky_casa["aaaa_www"]: Refreshing state... [id=@/AAAA] hcloud_zone_rrset.binky_casa["cname_www"]: Refreshing state... [id=www/CNAME] hcloud_zone_rrset.binky_casa["txt"]: Refreshing state... [id=@/TXT] hcloud_zone_rrset.binky_casa["txt_bluesky"]: Refreshing state... [id=_atproto/TXT] hcloud_zone_rrset.binky_casa["cname_fastmail_3"]: Refreshing state... [id=fm3._domainkey/CNAME] hcloud_zone_rrset.binky_casa["txt_dmarc"]: Refreshing state... [id=_dmarc/TXT] hcloud_zone_rrset.binky_casa["a_www"]: Refreshing state... [id=@/A] hcloud_zone_rrset.binky_casa["cname_db"]: Refreshing state... [id=db/CNAME] hcloud_zone_rrset.binky_casa["cname_fastmail_2"]: Refreshing state... [id=fm2._domainkey/CNAME] hcloud_zone_rrset.binky_casa["cname_fastmail_1"]: Refreshing state... [id=fm1._domainkey/CNAME] OpenTofu used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols: + create ~ update in-place (current -> planned) - destroy OpenTofu will perform the following actions: # hcloud_zone_rrset.cpressland_io["a_root"] will be created + resource "hcloud_zone_rrset" "cpressland_io" { + change_protection = (known after apply) + id = (known after apply) + labels = {} + name = "@" + records = [ + { + value = "82.39.95.203" }, ] + ttl = 3600 + type = "A" + zone = "cpressland.io" } # hcloud_zone_rrset.cpressland_io["a_www"] will be destroyed # (because key ["a_www"] is not in for_each map) - resource "hcloud_zone_rrset" "cpressland_io" { - change_protection = false -> null - id = "@/A" -> null - labels = {} -> null - name = "@" -> null - records = [ - { - value = "185.199.108.153" -> null }, - { - value = "185.199.109.153" -> null }, - { - value = "185.199.110.153" -> null }, - { - value = "185.199.111.153" -> null }, - { - value = "192.30.252.153" -> null }, - { - value = "192.30.252.154" -> null }, ] -> null - ttl = 3600 -> null - type = "A" -> null - zone = "cpressland.io" -> null } # hcloud_zone_rrset.cpressland_io["aaaa_root"] will be created + resource "hcloud_zone_rrset" "cpressland_io" { + change_protection = (known after apply) + id = (known after apply) + labels = {} + name = "@" + records = [ + { + value = "2a11:2646:1337:1::10" }, ] + ttl = 3600 + type = "AAAA" + zone = "cpressland.io" } # hcloud_zone_rrset.cpressland_io["aaaa_www"] will be destroyed # (because key ["aaaa_www"] is not in for_each map) - resource "hcloud_zone_rrset" "cpressland_io" { - change_protection = false -> null - id = "@/AAAA" -> null - labels = {} -> null - name = "@" -> null - records = [ - { - value = "2606:50c0:8000::153" -> null }, - { - value = "2606:50c0:8001::153" -> null }, - { - value = "2606:50c0:8002::153" -> null }, - { - value = "2606:50c0:8003::153" -> null }, ] -> null - ttl = 3600 -> null - type = "AAAA" -> null - zone = "cpressland.io" -> null } # hcloud_zone_rrset.cpressland_io["cname_www"] will be updated in-place ~ resource "hcloud_zone_rrset" "cpressland_io" { ~ change_protection = false -> (known after apply) id = "www/CNAME" name = "www" ~ records = [ - { - value = "cpressland.github.io." -> null }, + { + value = "ingress" }, ] # (4 unchanged attributes hidden) } Plan: 2 to add, 1 to change, 2 to destroy. ───────────────────────────────────────────────────────────────────────────── Note: You didn't use the -out option to save this plan, so OpenTofu can't guarantee to take exactly these actions if you run "tofu apply" now. ``` </details> *Workflow run: [646](https://git.cpressland.io/cpressland/tofu/actions/runs/646)*
cpressland deleted branch www-apex-cname-flatten 2026-09-05 00:06:58 +01:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
cpressland/tofu!15
No description provided.