Source code powering kyswtn.com and my public personal infrastructure.
Feel free to use this as a reference, but not a template. Copies about myself are about me, so you should rewrite them. Most if not all illustrations and assets are licensed and paid products, you may not use them. Some credentials are hard-coded, you must replace them.
The entire monorepo is massively reproducible and entirely declarative to the point with proper environment variables, just by renaming occurrences of my domain name, anyone could get the exact same personal infrastructure setup.
In the beginning, there was a Nix flake.
In ./flake.nix
is a devShell, entered immediately upon cd
via direnv. The shell loads environment variables and installs tools—among them, Opentofu. Opentofu, a fork of Terraform, provisions infrastructure.
In the ./terraform
directory are a set of files, holding instructions to:
- provision a Cloudflare zone,
- update Porkbun nameservers to the new Cloudflare zone,
- create a Vercel project linked to this repository, and
- configure DNS records to route traffic to Vercel.
Terraform state file—holding sensitive data—is stored in a Cloudflare R2 bucket via the S3 backend. To sidestep chicken-egg paradox, this R2 bucket is created manually, by a wrangler script. All Terraform providers used are officially supported, except for Porkbun. I wrote terraform-provider-porkbun.
Once provisioned, Vercel deploys the website from ./sites/www
. This repository, being a Bun monorepo, manages all of ./sites/**
. During installation, a script clones ./notes
submodule—a private Obsidian vault repository. This submodule is then loaded through Astro's content collections by the website. The website collects analytics through Umami, self-hosted with Docker Compose on a VPS.
The VPS is hosted on Hetzner Cloud and entirely configured via NixOS. HCloud lacks support for standard NixOS images therefore Packer is used to create a NixOS golden image as a snapshot. I wrote create-hcloud-nixos-snapshot for that. Generated hardware-configuration.nix
is kept alongside other system configurations in ./nixos
.
Terraform provisions the VPS behind a firewall, only allowing Cloudflare's proxied traffic. This use Cloudflare's SSL/TLS (from Google's CA) as edge certificates. Let's Encrypt is then configured as origin certificates (required for to Full(Strict) mode on Cloudflare). The VPS's IPv4/6 are then added to Cloudflare's DNS. These steps can be listed as:
- make a firewall and allow only Cloudflare IPs,
- provision a server placed behind the firewall, and
- configure DNS records to point to VPS's IP.
Once provisioned, the VPS is then configured remotely via nixos-rebuild
. On the first SSH session, Tailscale is setup and SSH is promptly turned off. Tailscale’s MagicDNS binds the server to a hostname, avoiding the problem of having to remember IPs.
NixOS configurations setup Docker and Traefik. Traefik handles routing, SSL/TLS certificates, and load balancing with it's Docker Provider. Its dashboard is made accessible at traefik
subdomain only within Tailscale’s private network.
Many other services such as Linkding for bookmarks, N8n for automations etc. are implemented as Docker Compose configuration files within ./sites
. Each service is configured either with named external volumes and handles it's own database backup or with bind mounts within /srv
. /srv
is then backed up to Cloudflare R2 on a daily basis using Restic.