mckiernan.ai Website - Case Study

mckiernan.ai Website - Case Study

Moving this site off Wix to a Django site on a single Hetzner server, from plan to live in four days, without breaking a link.

Overview

This site used to run on Wix. It now runs as a small Django application on one Hetzner Cloud server that I own, deployed by pushing to GitHub. The content, images and every old URL came across, and the parts Wix handled badly, per-page SEO in particular, are now fixed in the templates rather than page by page.

Built for My own consulting site
Runs on One Hetzner Cloud server, Docker Compose
Stack Django 6.1, SQLite, Gunicorn, Caddy, Docker, GitHub Actions, GHCR, Umami, uv
Timeline Plan on 12 October 2026, live on 16 October
Role Planning, architecture and review; implementation with Claude Code

The problem

Wix made the first version quick to put together, but it was a poor fit for a technical consultant's site:

  • SEO was the same on every page. Every project page reused the home page's meta description, and the logo as its share image, so a link to the TopThis page previewed as the home page.
  • Content was locked in page designs. The site used no Wix CMS collections, so Wix's CSV export had nothing to export. The text and images only existed inside the published pages.
  • No path to grow. Project pages lived at the root (/topthis, /aviva), where new pages would eventually collide with them, and there was no structured data such as dates or a tech stack to show on listing cards.
  • Leftovers. A Wix placeholder mailto:info@mysite.com link was still wrapped around a sentence on two pages.

Architecture

GoDaddy DNS ──► Caddy (HTTPS, www → bare domain, /media/ from disk)
                  ├──► Django + Gunicorn ──► SQLite + uploaded images (one Docker volume)
                  └──► Umami analytics at stats.mckiernan.ai

git push main ──► GitHub Actions: lint, tests, build image ──► GHCR ──► SSH: restart web

Every detail page, work history and personal project alike, is one Experience model rendered by one template. Content is edited in the Django admin; the site has no build step for content changes.

Engineering decisions

Plan before code. A one-page migration plan came first: what each page is made of, one model with its fields, the URL changes and the content fixes. The scaffold was built straight from it.

Import, don't retype. A management command reads the live Wix pages, converts the text to Markdown, and downloads the original images by stripping Wix's resize parameters from each image URL. It fixed the known typos and dropped the placeholder link on the way in, and it can be run again safely, so content could be refreshed from Wix up to the day of the switch.

Keep every old link working. Old root-level URLs such as /topthis return a permanent redirect to /experience/topthis/, www redirects to the bare domain, and a missing trailing slash is added. Links indexed by Google and shared on LinkedIn still land on the right page.

SEO from the data, not by hand. Each page's description and share image come from its own summary and cover image, so a new page is correct the moment it's saved. The site also generates a sitemap, canonical links and schema.org structured data for the person and the services.

Small and boring to run. SQLite and uploaded images share one Docker volume on one server, which is plenty for a site like this and simple to back up: a nightly SQLite snapshot plus Hetzner's server backups.

A push is a deploy. GitHub Actions runs the linter and the 37 tests, builds an image, pushes it to GitHub's container registry and restarts the app on the server over SSH. A change goes live a few minutes after the commit.

Analytics without a cookie banner. Self-hosted Umami runs beside the site and stores no cookies, so there's no consent banner and no visitor data passed to a third party.

DNS cut over without touching email. Only the web records moved from Wix to the server; the Proton Mail MX and TXT records at GoDaddy stayed as they were.

Status and next

Live since 16 October 2026. The Wix plan is cancelled and the sitemap is submitted to Google Search Console. Since launch the site has gained a Services section with a page per service, this Projects section, and a WhatsApp contact link. Next: write up each service and build backlinks so the new pages get found.