Skip to content
Infrastructure

How I Host My Projects

Each kind of project I build has one place it lives: anything with a backend runs in containers on a self-hosted server deployed by yggdrasil, and everything static is published on free hosting. Here is the decision table, the rule behind it and a map of the series.

Every project eventually has to run somewhere, and deciding where for each one separately is how a hobby turns into a pile of accounts, bills and half- remembered setups. So I decided once, by kind of project, and every new project inherits the answer. This series describes that setup, as it runs today.

The decision table

I’m buildingStackHosting
A marketing or product pageNext.js with shadcn/uiVercel, behind Cloudflare
A static portfolio or brochure siteAstro, or a Next.js static exportCloudflare Pages or GitHub Pages
Technical documentationHugo with the Docsy themeGitHub Pages
An interactive web appFlutter, built for the webMy server: Docker, Traefik, yggdrasil
A backend APIASP.NET CoreMy server: Docker, Traefik, yggdrasil
A reusable packageNuGet, or the language’s own formatnuget.org and GitHub Packages

The rule behind it

Anything with a running backend is self-hosted; everything else is not.

Static pages, documentation and packages are files. Free content delivery networks serve files better than any single server I could rent: closer to the reader, with no machine to patch, and at no cost. Putting them on my own server would only spend its capacity on work others do for free.

APIs and the web apps that call them are different. They need a process that keeps running, a database, secrets, logs and a way to roll back a bad release. Free tiers for that come with sleeping containers, cold starts and limits that change. One server I control, sized for the whole stack, is cheaper than a managed platform after the first two or three applications, and teaches far more.

The self-hosted half

The server runs a small platform that every application shares, and an application is added to it by describing it in one file, not by setting up anything new on the server. That platform is yggdrasil, a deploy manager I built for this: a self-hosted Jenkins that deploys from GitHub, Traefik in front with wildcard certificates, Prometheus and Loki for monitoring, and a console to see it all.

ConcernTool
ServerOne Ubuntu VPS
ContainersDocker and Docker Compose
Reverse proxy and certificatesTraefik, with Let’s Encrypt wildcard certificates
DatabasePostgreSQL, installed on the host
Build and testGitHub Actions
DeployJenkins, driven by yggdrasil’s catalog
Metrics, logs and dashboardsPrometheus, Loki, Alloy and Grafana
DNS, and optionally a CDN in frontCloudflare

Two systems run on it today, each an API and its web front end: Heimdall, for identity and access management, and Fortuna, for personal finance.

The series

The self-hosted platform, from the bottom up:

  1. The Server: the VPS, how it is prepared, and PostgreSQL.
  2. The Platform: Traefik, certificates, DNS, monitoring and the console.
  3. Deploying With Yggdrasil: from a push on GitHub to a container running, and rolling back when it does not start.
  4. Sizing the Server: how much of the server each piece takes, and when to grow.
  5. Web APIs and Web Apps: what an application looks like to the platform.

The projects that need no server:

  1. Landing Pages, Static Sites and Documentation Sites.
  2. Libraries: publishing packages on a version tag.

The way each project starts, before any of this, is in How I Start a Project From Scratch.