Infrastructure

Everything runs on a single VPS, self-hosted, no managed services.

Traefik
Reverse proxy and automatic Let’s Encrypt TLS for every subdomain. Services publish no ports of their own — they join a shared Docker network and Traefik routes to them.
Docker Compose
One compose project per app. Pinned image tags, healthchecks, named volumes so data survives restarts.
PostgreSQL 16
The shared relational database, reachable internally as infrastructure-postgres.
Redis 7
Cache, sessions, queues and pub/sub, with AOF + RDB persistence.
MinIO
S3-compatible object storage — blog post images and project assets live here.

This blog

Astro in SSR mode, rendering markdown straight off disk.

Astro 7
Server-rendered via the Node adapter. Nothing is prerendered: posts are markdown folders on a mounted volume, so publishing is a file move and the change is live on the next request.
markdown-it + Shiki
Markdown rendering with server-side syntax highlighting — dual light/dark themes, zero client-side highlighting JavaScript.
sanitize-html
Defence in depth on rendered output; raw HTML in markdown is escaped rather than trusted.
SQLite (better-sqlite3)
Subscribers, newsletter list and magic-link tokens. Small, file-backed, trivially backed up.
Satori + resvg
Per-post Open Graph cards generated on demand at /og/<slug>.

Services

Self-hosted replacements for the usual third-party SaaS.

Umami
Privacy-friendly analytics instead of Google Analytics — no cookie banner needed.
Remark42
Comments, self-hosted, anonymous-friendly.
Stripe
The one deliberate exception to self-hosting: subscriptions and billing, with the customer portal for cancellations.
Internal mail service
A small internal gateway for transactional email (magic links, newsletter).

Workflow

Neovim / VS Code
Editing.
Claude Code
Pair programming, refactors, and most of the writing scaffolding for this blog.
git + GitHub
Private repos for infrastructure and content, public where it makes sense.
cron
Nightly encrypted backups pushed offsite, IndexNow pings, newsletter sends, and a watchdog for stuck payment webhooks.