About This Site

No cloud providers, no open ports β€” just a Mac Mini on a shelf serving static files through a Cloudflare tunnel.

The Stack

This site is a statically exported Next.js 15 application built with React 19 and TypeScript. At build time, every page becomes a plain HTML file β€” no server-side rendering, no API routes, no Node.js runtime in production. Just flat files.

Tailwind CSS 4 handles all styling through utility classes and CSS custom properties. The entire design system β€” colors, typography, spacing β€” lives in a single globals.css file. No component library, no CSS-in-JS.

Typography uses Instrument Serif for display headings and DM Sans for body text, loaded through next/font/google with automatic subsetting and self-hosting. The language switcher and scroll-triggered animations are the only pieces of client-side interactivity.

Next.js 15React 19TypeScriptTailwind CSS 4

The Server

This site runs on a Mac Mini Late 2012, a machine that spent years as a desktop before being retired to a shelf and given a second life as a home server. The hardware is modest: a dual-core Intel i5, upgraded to 16 GB of RAM and a 1 TB SSD. It draws about 10 watts at idle.

The machine runs Ubuntu Server headless β€” no display, no keyboard, managed entirely over SSH. It hosts this site alongside a few other containers and personal projects.

The entire setup costs about $2/month in electricity. No cloud bills, no subscriptions, no usage tiers.

How It Gets to You

The site is packaged as a Docker container using a multi-stage build: Node 22 builds the static files, then just the output gets copied into a tiny Nginx Alpine image (under 25 MB). Deploys are a single command that rebuilds and restarts the container in about 30 seconds.

Caddy sits in front of all containers as a reverse proxy, handling automatic TLS certificates via Let's Encrypt. Each container runs its own web server (Nginx), keeping apps self-contained and portable, while Caddy routes traffic by domain name.

A Cloudflare Tunnel creates an outbound-only connection from the server to Cloudflare's edge β€” no ports are open on the home network, no port forwarding, no dynamic DNS. It even works behind CGNAT, so you don't need a public IP address. Cloudflare handles DNS, DDoS protection, and edge caching on top.

Want to build something like this?

I wrote a detailed step-by-step guide covering everything from scaffolding the Next.js project to setting up the Cloudflare tunnel. It's aimed at beginners β€” if you can open a terminal, you can follow along.

Read the guide β†’