← Back to blog

Building My Personal Site with Astro and K3s

astrodevopskubernetes

I finally got around to building a proper personal site. Here’s how I did it and why I made the choices I did.

The Stack

  • Astro 5 — Static site generator that ships zero JS by default
  • Tailwind CSS — Utility-first CSS for rapid styling
  • MDX — Markdown with components for blog posts
  • Docker + nginx — Tiny container serving static files
  • K3s + Flux — GitOps deployment on Hetzner Cloud

Why Astro?

For a personal site with a blog, I wanted something that outputs pure HTML and CSS. No client-side framework needed. Astro does exactly that — it builds to static files and only ships JavaScript when you explicitly ask for it.

The content collections API is clean:

const posts = await getCollection('blog');

And MDX support means I can embed components in blog posts when needed.

The Deploy Pipeline

The deployment is fully GitOps:

  1. Push to Gitea
  2. Gitea Actions builds the Docker image
  3. Image gets pushed to Gitea Container Registry
  4. Flux detects the new image and updates the deployment

The entire site runs in a single nginx container using about 5 MB of RAM. Hard to beat that.

Bento Grid Design

I went with a bento grid layout for the home page — asymmetric cards that look modern without being over-designed. Dark mode only, because it’s 2026 and my eyes are tired.

The cards use a simple fade-in animation on scroll:

.fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s ease-out, transform 0.6s ease-out;
}

.fade-in.visible {
  opacity: 1;
  transform: translateY(0);
}

Triggered by an IntersectionObserver — no heavy animation libraries needed.

What’s Next

  • More blog posts about DevOps and infrastructure
  • Project case studies with architecture diagrams
  • Maybe a /uses page for my setup

The source code is available on my Gitea instance.