Domain Swaps: space to the icu

Published: Oct 30, 2025 by Isaac Johnson

I have three domains I tend to keep going: freshbrewed.science, tpw.pw and steeped.space. In the last few months, the “.space” domain has had some issues - it stops resolving, it falls down. The last time the folks at Gandi.net told me it was the domain owner at fault. I also noticed when looking at Ionos.com, they have no discounts for “.space” so it’s treated as a premium domain (you can get at .com for $10 there but a .space is $50).

I was put in a pickle on whether to either renew my steeped.space for $57 a year or let it slide and move to something else. Truthfully, I only really care about the freshbrewed.science primary domain so I think I can let this other one go.

Acquiring a Domain

I decided to give Ionos.com my business on this one since Gandi has been upping prices and taking things back as of late (charing for webmail for instance). However, I like keeping a portion of my business outside the US so having my main domains in Gandi (tpk.pw and freshbrewed.science) will stay.

I found a decent domain of steeped.icu and decided, since it is pretty cheap, to get it for 3y

/content/images/2025/10/steeped-01.png

I had to try twice to reconfirm my email but it did finally work

/content/images/2025/10/steeped-02.png

Their on-boarding page is rather worthless to me: a website builder (no thanks), forwarding it to YouTube or LI, nope. And I’m not looking for a CNAME either

/content/images/2025/10/steeped-03.png

Let’s just get a Zone going in a cloud provider

Google Cloud DNS

I want to use Cloud DNS for this so let’s pop over to GCP and fire that up. For the moment I will not enable DNSSEC nor cloud logging

/content/images/2025/10/steeped-04.png

When created, we will see the current nameservers (NS) that Google Cloud has assigned us

/content/images/2025/10/steeped-05.png

I’ll want to change those in Ionos

/content/images/2025/10/steeped-06.png

I’ll copy those values over

/content/images/2025/10/steeped-07.png

DNS is slow, so you must have patience. When I clicked save, it noted it can take up to 2 days for updates (it is usually faster with new domains though)

/content/images/2025/10/steeped-08.png

I won’t revisit the whole CloudDNS user account setup for our Cluster Issuer, but you can see that detailed blog post here which covers how to set it up.

What I did confirm was that I had not scoped my ClusterIssuer service account (SA) to just one zone, but rather a DNS Administrator which could handle any zone

/content/images/2025/10/steeped-09.png

Syncing out domains

Let us assume we want to pretty much copy all of that which is in steeped.space over to steeped.icu. I would really prefer not to do that manually.

I’ll start with an export of my current domain:

$ gcloud dns record-sets export yournewzonefile.txt --zone "steepedspace" --zone-file-format
Exported record-sets to [yournewzonefile.txt].

I’ll then open it up in vim and replace “.space” with “.icu”

/content/images/2025/10/steeped-10.png

Also, my current steeped.space used the “A” servers (ns-cloud-a1,a2,etc) and then new one uses their “E” servers (ns-cloud-e1,e2,etc). I’ll fix that too

/content/images/2025/10/steeped-11.png

I now have a sync with the new domain and proper NS servers:

$ cat yournewzonefile.txt
steeped.icu. 21600 IN NS ns-cloud-e1.googledomains.com.
steeped.icu. 21600 IN NS ns-cloud-e2.googledomains.com.
steeped.icu. 21600 IN NS ns-cloud-e3.googledomains.com.
steeped.icu. 21600 IN NS ns-cloud-e4.googledomains.com.
steeped.icu. 21600 IN SOA ns-cloud-e1.googledomains.com. cloud-dns-hostmaster.google.com. 1 21600 3600 259200 300
alerthub.steeped.icu. 300 IN A 75.72.233.202
boltdiy.steeped.icu. 300 IN A 75.72.233.202
boltfit.steeped.icu. 300 IN A 75.72.233.202
bskyposter.steeped.icu. 300 IN A 75.72.233.202
bwcafish.steeped.icu. 300 IN A 75.72.233.202
commafeed.steeped.icu. 300 IN A 75.72.233.202
easyappt.steeped.icu. 300 IN A 75.72.233.202
favicon.steeped.icu. 300 IN A 75.72.233.202
fb.steeped.icu. 300 IN CNAME freshbrewed.science.
filegator.steeped.icu. 300 IN A 75.72.233.202
filegator2.steeped.icu. 300 IN A 75.72.233.202
fmr.steeped.icu. 300 IN A 75.72.233.202
fms.steeped.icu. 300 IN A 75.72.233.202
goldilocks.steeped.icu. 300 IN A 75.72.233.202
harbor.steeped.icu. 300 IN CNAME harbor.freshbrewed.science.
medama.steeped.icu. 300 IN A 75.72.233.202
mystatus.steeped.icu. 300 IN CNAME cachetfunction-q5jg7qcghq-uc.a.run.app.
nextterm.steeped.icu. 300 IN A 75.72.233.202
patientsmvc.steeped.icu. 300 IN A 75.72.233.202
pdfding.steeped.icu. 300 IN A 75.72.233.202
pingvin.steeped.icu. 300 IN A 75.72.233.202
rustpad.steeped.icu. 300 IN A 75.72.233.202
rustypaste.steeped.icu. 300 IN A 75.72.233.202
shiori.steeped.icu. 300 IN A 75.72.233.202
spacedeck.steeped.icu. 300 IN A 75.72.233.202
status.steeped.icu. 300 IN A 35.241.26.202
testing123.steeped.icu. 300 IN A 75.72.233.202
testing5.steeped.icu. 300 IN A 34.49.221.120
vikunja.steeped.icu. 300 IN A 75.72.233.202
www.steeped.icu. 300 IN A 34.54.220.77

If I just try and import it, I’ll get an error about existing records:

$ gcloud dns record-sets import yournewzonefile.txt --zone "steepedicu" --zone-file-format
ERROR: (gcloud.dns.record-sets.import) The following records (name type) already exist: ['steeped.icu. NS', 'steeped.icu. SOA']

I could add --delete-all-existing and/or --replace-origin-ns but instead, I’ll just be safe and comment out the top 5 entries from my file (note: these type of files use ; for comment lines)

$ cat yournewzonefile.txt
; steeped.icu. 21600 IN NS ns-cloud-e1.googledomains.com.
; steeped.icu. 21600 IN NS ns-cloud-e2.googledomains.com.
; steeped.icu. 21600 IN NS ns-cloud-e3.googledomains.com.
; steeped.icu. 21600 IN NS ns-cloud-e4.googledomains.com.
; steeped.icu. 21600 IN SOA ns-cloud-e1.googledomains.com. cloud-dns-hostmaster.google.com. 1 21600 3600 259200 300
alerthub.steeped.icu. 300 IN A 75.72.233.202
boltdiy.steeped.icu. 300 IN A 75.72.233.202
boltfit.steeped.icu. 300 IN A 75.72.233.202
bskyposter.steeped.icu. 300 IN A 75.72.233.202
bwcafish.steeped.icu. 300 IN A 75.72.233.202
commafeed.steeped.icu. 300 IN A 75.72.233.202
easyappt.steeped.icu. 300 IN A 75.72.233.202
favicon.steeped.icu. 300 IN A 75.72.233.202
fb.steeped.icu. 300 IN CNAME freshbrewed.science.
filegator.steeped.icu. 300 IN A 75.72.233.202
filegator2.steeped.icu. 300 IN A 75.72.233.202
fmr.steeped.icu. 300 IN A 75.72.233.202
fms.steeped.icu. 300 IN A 75.72.233.202
goldilocks.steeped.icu. 300 IN A 75.72.233.202
harbor.steeped.icu. 300 IN CNAME harbor.freshbrewed.science.
medama.steeped.icu. 300 IN A 75.72.233.202
mystatus.steeped.icu. 300 IN CNAME cachetfunction-q5jg7qcghq-uc.a.run.app.
nextterm.steeped.icu. 300 IN A 75.72.233.202
patientsmvc.steeped.icu. 300 IN A 75.72.233.202
pdfding.steeped.icu. 300 IN A 75.72.233.202
pingvin.steeped.icu. 300 IN A 75.72.233.202
rustpad.steeped.icu. 300 IN A 75.72.233.202
rustypaste.steeped.icu. 300 IN A 75.72.233.202
shiori.steeped.icu. 300 IN A 75.72.233.202
spacedeck.steeped.icu. 300 IN A 75.72.233.202
status.steeped.icu. 300 IN A 35.241.26.202
testing123.steeped.icu. 300 IN A 75.72.233.202
testing5.steeped.icu. 300 IN A 34.49.221.120
vikunja.steeped.icu. 300 IN A 75.72.233.202
www.steeped.icu. 300 IN A 34.54.220.77

Now I can import them

$ gcloud dns record-sets import yournewzonefile.txt --zone "steepedicu" --zone-file-format
Imported record-sets from [yournewzonefile.txt] into managed-zone [steepedicu].
Created [https://dns.googleapis.com/dns/v1/projects/myanthosproject2/managedZones/steepedicu/changes/1].
ID  START_TIME                STATUS
1   2025-10-21T11:29:27.279Z  pending

Almost immediately, I can see results

/content/images/2025/10/steeped-12.png

Testing

I should now be able to create a new entry for Vikunja that would serve vikunja.steeped.icu as it did the .space entry:

$ cat vikunja.icu.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    cert-manager.io/cluster-issuer: gcpleprod2
    ingress.kubernetes.io/proxy-body-size: "0"
    ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "0"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.org/client-max-body-size: "0"
    nginx.org/proxy-connect-timeout: "3600"
    nginx.org/proxy-read-timeout: "3600"
    nginx.org/websocket-services: vikunja-external-ip
  labels:
    app.kubernetes.io/instance: vikunjaingress
  name: vikunjaingress2
spec:
  rules:
  - host: vikunja.steeped.icu
    http:
      paths:
      - backend:
          service:
            name: vikunja-external-ip
            port:
              number: 3456
        path: /
        pathType: ImplementationSpecific
  tls:
  - hosts:
    - vikunja.steeped.icu
    secretName: vikunjaicu-tls

$ kubectl apply -f ./vikunja.icu.yaml
Warning: annotation "kubernetes.io/ingress.class" is deprecated, please use 'spec.ingressClassName' instead
ingress.networking.k8s.io/vikunjaingress2 created

I see the cert was satisified

$ kubectl get cert vikunjaicu-tls
NAME             READY   SECRET           AGE
vikunjaicu-tls   True    vikunjaicu-tls   11m

And I can see it is working gangbusters:

/content/images/2025/10/steeped-13.png

My next fix I really want sorted is my bsky poster which is used with all my blog posting. It handles the post out to Mastodon and Bluesky and any other socials I’ve implemented.

In reminding myself of the last time I updated pybsposter, I did put the helm chart into Harbor as an OCI chart.

The current deploy uses 0.1.4 (the blog writeup above talked about 0.1.1)

$ helm list | grep -i post
pybsposter                      default         14              2025-07-04 14:10:55.57151814 -0500 CDT  deployed        pybsposter-0.1.4                      2.2

I can see 0.1.4 is there in harbor

/content/images/2025/10/steeped-14.png

I should be able to just upgrade the chart to set the new values taking into account the current image tag:

$ helm get values pybsposter -o yaml
image:
  tag: 0.2.1
ingress:
  enabled: true

$ helm get values pybsposter --all
COMPUTED VALUES:
image:
  pullPolicy: IfNotPresent
  repository: harbor.freshbrewed.science/library/pybsposter
  tag: 0.2.1
ingress:
  certManagerIssuer: gcpleprod2
  clientMaxBodySize: "0"
  connectTimeout: "3600"
  enabled: true
  host: bskyposter.steeped.space
  ingressClass: nginx
  proxyBodySize: "0"
  readTimeout: "3600"
  sendTimeout: "3600"
  sslRedirect: "true"
  tlsAcme: "true"
  tlsSecretName: pybspostergcp-tls
replicas: 1
resources:
  limits:
    cpu: 250m
    memory: 250Mi
  requests:
    cpu: 15m
    memory: 105Mi
service:
  port: 80
  targetPort: 8000
  type: ClusterIP

$ helm upgrade pybsposter oci://harbor.freshbrewed.science/library/pybsposter --version 0.1.4
--set ingress.enabled=true --set image.tag=0.2.1 --set ingress.host=bskyposter.steeped.icu --set ingress.tlsSecretName=pybspostergcp2-tls
Pulled: harbor.freshbrewed.science/library/pybsposter:0.1.4
Digest: sha256:a0d7592fe0e5b597c0b779e410aec75139ddf02414bb9de02313b776204d1009
Release "pybsposter" has been upgraded. Happy Helming!
NAME: pybsposter
LAST DEPLOYED: Tue Oct 21 07:02:20 2025
NAMESPACE: default
STATUS: deployed
REVISION: 15
TEST SUITE: None

I can see the new one is up

/content/images/2025/10/steeped-15.png

The old one, however, is now not reachable

/content/images/2025/10/steeped-16.png

I have two choices - I could create the old entry until my blog pipelines catch up, or update them now with a hotfix. I want to just pull the bandaid and get it done

builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git checkout -b hotfix-pskyposter
Switched to a new branch 'hotfix-pskyposter'
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git diff
diff --git a/.github/workflows/pr-final.yml b/.github/workflows/pr-final.yml
index 137172a0..6d4cae93 100644
--- a/.github/workflows/pr-final.yml
+++ b/.github/workflows/pr-final.yml
@@ -294,7 +294,7 @@ jobs:
             cat bsky.payload.json
             if [[ $log != *"SKIPSOCIAL"* ]]; then
               echo "================ now posting ================"
-              curl -X POST https://bskyposter.steeped.space/post -H "Content-Type: application/json" -d @bsky.payload.json
+              curl -X POST https://bskyposter.steeped.icu/post -H "Content-Type: application/json" -d @bsky.payload.json
             else
               echo "CHECK-SKIP: Skip Posting To Social (BSky).. would have posted:"
               cat bsky.payload.json
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git add .github/workflows/pr-final.yml
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git commit -m "Update URL for Bluesky poster"
[hotfix-pskyposter c6d0ba8a] Update URL for Bluesky poster
 1 file changed, 1 insertion(+), 1 deletion(-)
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git push --set-upstream origin hotfix-pskyposter

I have a flow that lets me add “SKIPSOCIAL” to the comments to avoid posting to socials when the update doesn’t affect them (otherwise we spam the last known post)

/content/images/2025/10/steeped-17.png

Cleanups

My next big question is how far back do I update blog posts and links? For the most part, my “steeped.space” URLs are all meant to be demos and not to be relied upon. Would I care if someone hijacked the domain when it expires and uses it for evil?

From what I can tell, I have about 32 past blog posts that mention steeped.space (ignoring this post)

builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ find _posts/ -type f -exec grep steeped.space {} \; -print | grep '^_posts' | sort
_posts/2024-07-25-gcp-dns.markdown
_posts/2024-08-08-cachetingcp.markdown
_posts/2024-08-15-dotnetnewrelic.markdown
_posts/2024-08-20-miscapps.markdown
_posts/2024-08-22-miscapps.markdown
_posts/2024-08-29-miscapps.markdown
_posts/2024-10-03-editors2.markdown
_posts/2024-10-14-miscapps.markdown
_posts/2024-10-29-miscapps.markdown
_posts/2024-11-05-miscapps.markdown
_posts/2024-11-07-appointments.markdown
_posts/2024-12-03-bluesky.markdown
_posts/2024-12-10-miscapps.markdown
_posts/2024-12-19-bskyrevisit.markdown
_posts/2024-12-26-miscapps.markdown
_posts/2024-12-29-sshterms.markdown
_posts/2025-01-09-miscapps.markdown
_posts/2025-01-21-boltnew.markdown
_posts/2025-02-06-azureai.markdown
_posts/2025-02-18-openwebui.markdown
_posts/2025-02-20-taskapps.markdown
_posts/2025-03-04-boltdiy.markdown
_posts/2025-04-22-scans.markdown
_posts/2025-05-08-misctools.markdown
_posts/2025-05-15-n8n2.markdown
_posts/2025-06-03-smallos.markdown
_posts/2025-06-17-goldilocks.markdown
_posts/2025-07-02-misctools.markdown
_posts/2025-09-16-clusterfixes.markdown
_posts/2025-09-24-verdentai.markdown
_posts/2025-10-15-vikunjamcp1.markdown
_posts/2025-10-21-vikunjacont.markdown
_posts/2025-10-xx-dnsswap.markdown

I could migrate each and every one over to the new domain to just the ones from this year.

In the end, I decided to make notes, edit and change just a few of the more recent posts

builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ git status
On branch 2025-10-dnsswap
Your branch is up to date with 'origin/2025-10-dnsswap'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   _posts/2025-02-20-taskapps.markdown
        modified:   _posts/2025-05-08-misctools.markdown
        modified:   _posts/2025-05-15-n8n2.markdown
        modified:   _posts/2025-06-17-goldilocks.markdown
        modified:   _posts/2025-07-02-misctools.markdown
        modified:   _posts/2025-10-15-vikunjamcp1.markdown
        modified:   _posts/2025-10-21-vikunjacont.markdown
        modified:   _posts/2025-10-xx-dnsswap.markdown

Feedback Forms and Flows

One of the uses of the Vikunja instance is to recieve Feedback form submissions two ways.

Standard Form

The first would be the link at the top of the site and what looks like an anonymous form which really triggers a Github Workflow. The actions of which are held in the workflowTriggerTest Repo.

I stored the diff in a PR so you could review later.

/content/images/2025/10/steeped-18.png

Agentic Flow

The second is through an n8n agentic flow. Here I need to find the Vikunja steps

/content/images/2025/10/steeped-19.png

and change the URLs

/content/images/2025/10/steeped-20.png

which involves updating the fetch token call and create task call

/content/images/2025/10/steeped-21.png

BWCA fish

Some helm charts, like Vikunja we covered above, just need a different URL.

One of these is the BWCA Fish App

I’ll pull the existing values, which does have secrets like an OTEL Ingest key, and change the URL in the values file

builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ helm get values bwcafish -o yaml > bwcafish.values.yaml
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ helm get values bwcafish -o yaml > bwcafish.values.yaml.bak
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ vi bwcafish.values.yaml
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ diff bwcafish.values.yaml bwcafish.values.yaml.bak
3c3
<   host: bwcafish.steeped.icu
---
>   host: bwcafish.steeped.space

I can then upgrade the helm deployment using the chart at the latest version

builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ helm upgrade bwcafish oci://harbor.freshbrewed.science/library/bwcafish --version 0.1.3 -f bwcafish.values.yaml
Pulled: harbor.freshbrewed.science/library/bwcafish:0.1.3
Digest: sha256:a2055fc980649c0fcb2d0605a8a8d7423b2d44837bf3eb9299c9605fa19f55ee
Release "bwcafish" has been upgraded. Happy Helming!
NAME: bwcafish
LAST DEPLOYED: Wed Oct 22 05:54:16 2025
NAMESPACE: default
STATUS: deployed
REVISION: 3
TEST SUITE: None
builder@DESKTOP-QADGF36:~/Workspaces/jekyll-blog$ helm get values bwcafish -o yaml | grep -i steeped
  host: bwcafish.steeped.icu

I am hoping this will replace the existing cert

$ kubectl get cert bwcafishgcp-tls
NAME              READY   SECRET            AGE
bwcafishgcp-tls   False   bwcafishgcp-tls   64d

which after a moment it did

$ kubectl get cert bwcafishgcp-tls
NAME              READY   SECRET            AGE
bwcafishgcp-tls   True    bwcafishgcp-tls   64d

The web pages were loading so slow and failing on sending images. I thought perhaps my cluster was doing poorly. The memory limits seemed really bad (15m request for CPU). I upped the values

$ diff bwcafish.values.yaml bwcafish.values.yaml.bak
3,10c3
<   host: bwcafish.steeped.icu
< resources:
<   limits:
<     cpu: 1000m
<     memory: 250Mi
<   requests:
<     cpu: 500m
<     memory: 150Mi
---
>   host: bwcafish.steeped.space

and tried again

$ helm upgrade bwcafish oci://harbor.freshbrewed.science/library/bwcafish --version 0.1.3 -f bwcafish.values.yaml
Pulled: harbor.freshbrewed.science/library/bwcafish:0.1.3
Digest: sha256:a2055fc980649c0fcb2d0605a8a8d7423b2d44837bf3eb9299c9605fa19f55ee
Release "bwcafish" has been upgraded. Happy Helming!
NAME: bwcafish
LAST DEPLOYED: Wed Oct 22 06:10:06 2025
NAMESPACE: default
STATUS: deployed
REVISION: 4
TEST SUITE: None

This time the bwcafish.steepe.icu site loaded fine.

Ingress live edits

We can also solve swapping out URLs by just live editing the ingress definitions with kubectl edit

Cleanups

I could go and delete the managed zone now in GCP (only after emptying it)

/content/images/2025/10/steeped-23.png

But the pricing shows its just US$0.20 a zone right now so leaving it a bit might be fine, at least until it expires.

Summary

First, I want to address why Cloud DNS makes sense, at least financially. If you look at AWS pricing for Route53 or Azure DNS, you’ll note both of them are US$0.50 a zone compared to US$0.20 in GCP.

I could have just left it in Ionos for free. In my June post about a Bolt hackathon I even detail out how to setup a ClusterIssuer for Ionos so we can have a functional cert-manager.

However, I like to get one toe in each major cloud provider so having Cloud DNS active is important to me.

dns clouddns gcp ionos

Have something to add? Feedback? You can use the feedback form

Isaac Johnson

Isaac Johnson

Cloud Solutions Architect

Isaac is a CSA and DevOps engineer who focuses on cloud migrations and devops processes. He also is a dad to three wonderful daughters (hence the references to Princess King sprinkled throughout the blog).

Theme built by C.S. Rhymes