Why I'm Self-Hosting Everything
A few months ago my nine-year-old asked me why YouTube always seems to know what she wants to watch. I gave her a half-answer about algorithms and changed the subject. That evening, after the kids were in bed, I sat staring at my Google Workspace admin console and felt uneasy for the first time in years. Not because Google had done something wrong exactly, but because I finally sat with the question: why is every email my family sends, every calendar invite, every contact stored on servers I have zero control over, in a jurisdiction that doesn’t answer to us?
That question is what started this project.
The trigger
I’m a software engineer. I’ve run Linux servers for fun since I was a teenager. I’ve provisioned cloud infrastructure professionally for years. And yet every byte of my personal life lived on someone else’s computer, governed by someone else’s terms of service, subject to someone else’s privacy policy changes.
The usual counterargument is convenience. Google Workspace works. It’s cheap. It’s reliable. All true. But the more I thought about it, the less those arguments held up against a few uncomfortable realities:
- A corporate policy change could lock me out of fifteen years of email overnight.
- My children’s data was being collected before they could consent to it.
- Everything was stored under US jurisdiction, which means the CLOUD Act applies regardless of where I physically sit.
- I was paying for the product and still had no real ownership of the data inside it.
None of this was new information. I’d known it for years. What changed was that I stopped being comfortable with knowing and not acting.
What I’m building
The project is called selfhost. The idea is straightforward: phase by phase, move my family off US big tech for our core digital services.
Stalwart Mail Server handles email, calendar, and contacts now, running across two nodes with IMAP, JMAP, SMTP, CalDAV, and CardDAV. This blog is live too, built with Hugo on European infrastructure. Still ahead: monitoring, automated backups with offsite copies, migrating fifteen years of data out of Google, self-hosted Git (Forgejo), and password management (Vaultwarden) for the family.
I’m not trying to replicate Google feature-for-feature. I’ll accept 80% of the polish if it means 100% of the control. Some things will be rougher. I’m fine with that.
Design principles
Everything is infrastructure as code. Servers, DNS records, firewall rules, all of it defined in Terraform and applied through Ansible. If I lost every server tomorrow, I could rebuild from the git repository and an encrypted secrets file.
The stack runs on Exoscale, a Swiss cloud provider under Swiss and EU privacy law. No US jurisdiction, no CLOUD Act exposure.
Encryption happens in layers. LUKS on every data volume, so raw disk access gets you nothing. OpenPGP at the application level for email at rest, so even database access gets you nothing. TLS in transit. Secrets managed through SOPS with GPG, committed to the repository in encrypted form.
Services run in Docker containers behind Traefik, with Tailscale mesh networking between nodes. Let’s Encrypt handles certificates. The mail server runs active-active across two compute instances with DNS round-robin, because email is the one thing that absolutely cannot go down.
What I’ve learned so far
The technology has been the easier part. Decision-making is where I get stuck, because every choice is a tradeoff and there’s no product manager to push back on me.
I initially wanted to run PostgreSQL myself. Full control, right? Managed DBaaS from Exoscale turned out to be the better call. Automated backups, patching, failover, none of which I have to think about. For a platform where I’m the only operator, that tradeoff made sense. I can always migrate later if I want to.
For the blog I looked at Ghost and WriteFreely before going with Hugo. No database, no runtime, no application server to keep patched. Just HTML files served by nginx. The less I have to maintain, the better.
Some things bit me that I didn’t see coming. Terraform state locking on Exoscale’s S3-compatible storage is supposed to use .terraform.lock.info according to the docs. The actual file name? terraform.tfstate.tflock. Lost time on that. SOPS path resolution in Ansible is another one: if your playbook directory structure doesn’t line up with what SOPS expects, lookups fail silently or point to the wrong file. Using absolute paths from playbook_dir fixed it. You only figure these things out by running into them.
Getting the first email through Stalwart felt like a real milestone, mostly because the prerequisite chain is so long. GPG keys, SOPS config, Terraform state bootstrapping, DNS records, TLS certificates, SPF, DKIM, DMARC, MTA-STS. Each piece is fine on its own. Sequencing them all correctly is the actual work. But once that foundation was in place, adding the blog on top took a fraction of the effort. That part felt good.
Why write about it
Part of the reason is selfish: writing forces me to organize what I’ve learned and make sense of the decisions I’ve made. But I also want it to be useful to someone else.
Most self-hosting content online falls into two camps: basic tutorials that stop at docker-compose up, or enterprise docs that assume a full team. I’m trying to write about the middle ground. One person, production-grade infrastructure, a family’s worth of data, real problems.
If you’re thinking about doing something similar, I hope these posts give you an honest picture. It’s more work than paying for a subscription. Less work than you probably think. And sending email from your own server, encrypted end to end, stored in a jurisdiction you chose? I won’t pretend that doesn’t feel good.
Next up: a closer look at the architecture.
You May Also Like
Building a Self-Hosted Platform: Architecture Overview
In the previous post I explained why I’m moving my family off …