Blog / Rainhold, Second Generation Server
- Date
- July 7, 2025
- Tags
Early days
I started self hosting so I could grind coffee for my wife without getting out of bed. Two years later I’m running an eighteen-role Ansible playbook, hosting nearly as many services, and building multiple custom Docker images to meet my specific needs.
Also, I don’t drink coffee.
In my house you’ll find a four bay Synology NAS and a tiny Lenovo workstation plugged directly into a mesh router. The Lenovo runs Proxmox, which hosts VMs for Debian and Windows. Home Assistant hums along on a Home Assistant Green.
I called my first attempt Docker Symphony. It ran on an Ubuntu VM and required a lot of manual care. I did all updates by hand. The entire VM disk image was backed up to the NAS each week. Not fun.

Rainhold
This modern version is called Rainhold because I live in Seattle and daydream about being in Chasm City.
Its best features:
- Users are stored in an LDAP service via the delightfully minimal LLDAP, then synced to Pocket ID
- Jellyfin, Gitea, and Grafana have Pocket ID’s OAuth define its users and groups
- Pocket ID only works with Passkeys. I’m a believer. They’re great.
- TinyAuth, secured with Pocket ID, sits between sites I want exposed on the public Internet but only visible to my users
- Ansible playbooks set up everything. Once you have a Linux user who can
sudo
and have filled out the variables file, you’re good to run the playbook. - All email sent from the server is captured and sent to a local mailbox accessible privately via IMAP
- Backups of all Docker volumes is scheduled to run daily. I have tested the restore script. It works.
Public sites
Public sites are served via:
- Cloudflare Tunnels
- Caddy
- Docker container
Private sites are only available on my Tailnet via:
- Dummy DNS entries
- NextDNS
- Tailscale
- Caddy
- Docker container
While I pay for NextDNS, I don’t rely on any special features in Cloudflare or Tailscale. When they’re ready to turn the screws on us, I’ll move to a cheap VPS and Wireguard.
Ansible
I used ChatGPT and Copilot to learn Ansible and debug the myriad issues as I tested the playbook. Discovering Ansible was a “where have you been all my life” moment. It automates everything about the server setup: unattended upgrades, changing passwords, SSH hardening, setting up Cloudflare, writing all the Docker environment files.
I don’t have to back up the entire server anymore, just the Docker volumes. I can recreate the whole VM with a single YAML file.
Custom Docker images
I ran into a couple scenarios that required building tiny Docker images:
- Caddy and Cloudflare DNS so Caddy can get the SSL certs it needs
- Node and AWS to build and deploy to AWS from one pipeline step
- Starbase 80 is a personal dashboard I built two years ago
- Email Page was an attempt to solve the self-hosted email problem
Now that SendGrid is free with trial rather than free with low usage, I needed a way to get email from my services.
Complete email in a box solution
Nope, way too much trouble to ensure reliable delivery. Plus I didn’t want to get Xfinity mad at me.Postfix to Mailrise to Apprise to Mastodon
Nope, you can only get the first 500 characters of plain text email, even if getting all that working was pretty fun.Bespoke Postfix to Mailrise to Email Page to Mailrise to Apprise to Mastodon
Nope, that’s even more convoluted! To be fair, it worked pretty well, and I had high hopes until I realized there was no way to (a) get theto
andfrom
addresses, and (b) receive HTML email from a POST request.
Finally I landed on the actual simplest option: unauthenticated Postfix that sends all mail to a single mailbox that’s served privately on the Tailnet by Dovecot. I can only check the mail on my desktop mail app, but I’ll take it.
Enjoy
Writing technical documentation is one of my passions. I spent a lot of time figuring out how to get LDAP, OAuth, and GitLab/Gitea’s runners working. AI was helpful, but some things have to be done manually. I hope you find Rainhold’s docs folder useful in your own journey.