Pete

The Stack

All posts / DevOps (19)

Ithaca, NY

People ask how The Stack works. Here is the full breakdown.

What it is: A microblog built with Astro. No CMS, no database, no admin panel. Every post is a single markdown file with frontmatter (date, text, tags). The site is static HTML deployed to a self-hosted server.

How posts get published: I type a slash command in Claude Code. That triggers a skill that writes the markdown file, picks tags, runs astro build, rsyncs the output to my server over SSH, and fixes file ownership. One command, zero browser tabs.

The stack:

  • Astro 5 (static site generator)
  • Marked (markdown rendering)
  • rsync over SSH (deploy)
  • Hetzner VPS running Apache (hosting)
  • Claude Code skill (publishing workflow)

How the skill works: Claude Code supports custom skills, which are reusable prompt templates that can be triggered with a slash command. The /stack skill takes my raw text, cleans it up, generates the next sequential filename, writes the markdown, runs the deploy script, then commits and pushes to git. The deploy script builds the Astro site locally, rsyncs the dist/ folder to the server, and sets correct ownership.

Why this approach: I wanted to post without friction. A CMS adds login screens, update prompts, plugin conflicts. A static site with a CLI publishing workflow means I can go from thought to published in under 30 seconds without leaving my terminal.

How to build your own: I open sourced the whole thing as a GitHub template. Clone it, replace the placeholders, deploy. Full setup guide included: server prep, SSH keys, deploy script, Claude Code skill.

No accounts to manage. No tokens expiring. No vendor lock-in. Just markdown, a build step, and a server you control.

github.com/pete-builds/astro-claude-microblog ↗

Spent the afternoon hardening the new production server. SSH lockdown, automated offsite backups with encryption, OS-level security updates on autopilot, WAF verified active, HSTS on all domains, database tuned, and external uptime monitoring watching everything at 5-minute intervals. Also wired up server-side resource alerts. Went from 'Plesk is installed' to 'production-ready' in one session. The old CentOS box is looking more disposable by the day.

Also tonight: spun up a new production server on Hetzner. AlmaLinux 10, Plesk installed, 4 vCPU, 8GB RAM, 160GB NVMe out of Hillsboro, OR. Replacing a 10-year-old CentOS 7 box on GoDaddy that was running on vibes and an expired OS. Going from $86/mo to about $28/mo with better hardware and a supported OS through 2035. Named it Zion.

Tonight we finished staging gotothemill.com. Full site rebuild from scratch on a clean database, WooCommerce configured, ready for the production swap. Just need to verify PayPal checkout and pick a go-live window. Client site, zero downtime tolerance, so we're doing this right.

Side project: built an open source MCP server that connects your Strava data to Claude Code. Ask questions like 'how far did I run this week?' and get formatted stats back. Caches everything in a local SQLite vault so you are not burning API calls on repeat queries. Handles token refresh, bulk sync, and runs as a Docker container. A friend is already forking it and helping improve it. That is what open source is about.

github.com/pete-builds/strava-mcp-vault ↗

Ran a full backup audit today. Found gaps: missing Docker volumes, no retention policy on one backup set, a dead service in the health check. Fixed all three, then wrote a disaster recovery runbook. Six phases, every command copy-pasteable. If the server dies, we rebuild from the NAS in under four hours. Next step: off-site backups to a remote server so a single point of failure does not take out everything.

Built this site today. The Stack is a microblog for documenting what we ship. Static Astro site, dark theme, tag filtering, RSS feed, paginated feed. No CMS, no database. Markdown files, one per post. Build it, rsync it to the server, done.

Started building MCP servers to connect our infrastructure directly to Claude Code. Model Context Protocol turns any API into a tool that the assistant can call natively. Five servers running now: media management, fitness tracking, server monitoring, diagramming, and analytics. Each one is a Python FastMCP container on the same Docker host. SSE transport so they are accessible from any machine on the network.

Shipped Model Arena, a tool for comparing language model outputs side by side. Send the same prompt to multiple models, see the results in real time. Built with Python and FastAPI, streams responses via SSE. Useful for picking the right model for a given task instead of guessing.

Built Phantom Paste, a zero-knowledge pastebin. Written in Go, stores everything in SQLite, encrypts client-side. The server never sees the plaintext. Pastes expire automatically. First app we built from scratch and shipped to production on our own infrastructure.

Deployed n8n as our automation engine. Visual workflow builder, webhook triggers, hundreds of integrations. Connect APIs, move data between systems, schedule tasks. No more writing one-off scripts for every integration. Build it once, let it run.

Added LiteLLM as an API gateway in front of all our language models. One endpoint, multiple providers. Route requests to local Ollama models or external APIs through the same interface. PostgreSQL backend tracks usage and costs. Swap models without changing a single line of application code.

Started running large language models locally with Ollama. No API costs, no data leaving the network. Pull a model, run it, done. Paired it with Open WebUI for a clean chat interface. The same tools the big platforms charge for, running on our own hardware.

Deployed Uptime Kuma to monitor every service. If a container goes down, we know within 60 seconds. Dashboard shows uptime history, response times, and SSL cert expiration. No excuse for a service being down and nobody noticing.

Connected everything with Tailscale. The server, the NAS, the workstation, the phone. Encrypted mesh network, no port forwarding, no exposed services. Access any tool from anywhere like you are sitting on the same LAN. Changed the way we work.

Put Caddy in front of everything as the reverse proxy. Automatic TLS certificates, clean subdomain routing, zero manual cert management. Every internal service gets its own address. One config file controls all the traffic.

Every service gets its own Docker Compose stack. Isolated networks, named volumes, environment files. No package conflicts, no dependency drift. Need to tear something down? One command. Need to rebuild? Same command. 29 containers running and the server barely notices.

Added a Synology NAS as the storage layer. Backups, media, shared volumes. The server handles compute, the NAS handles data. Two machines, clear separation of concerns. If the server dies, the data survives.

Set up a dedicated Ubuntu server as the backbone for everything we build. One box, clean install, SSH access, nothing else. Every service runs in its own container. The server does one job: host Docker and stay out of the way.