Like the elves unto Valinor, my services will embark upon ships (containers) and travel upon the straight road (VPN) to the Undying Lands (cloud). Also there will be some hobbits and a dwarf for no particular reason.
I like having all of my services run declaratively because I find reading a standardized docker-compose
file much easier than trying to remember what all commands I ran to get something working. (Or marginally better, remembering where I put the notes I took when I did.) This repo is basically just a collection of compose files that I pull and run regularly. Each image is pinned and the renovate bot updates them as time goes on. Eventually I might set up some ansible tool to auto-update the machines with this new configuration when I merge an update.
This project started as a migration from a collection of bare-metal systemd
services and screen
s into a containerized system. I learned a lot about docker, spun up way too many containers, filled up my boot drive more than once, accidentally exposed my services to the entire internet for several months, and generally had a nice time. Here are some changes I've made along the way, undoubtedly out of date by the time you read this:
- Authentication: I started with Traefik+Authelia, but eventually settled on Cloudflare Access for authentication. I like Access because I can define a list of external users who can just log in with their Google account to access my services. Everything is consistent between services and I don't have to manage authentication at all.
- External Networking: Cloudflare Tunnel has been my go-to here. I started with just one entrypoint on my VPS but if it went down I couldn't access anything on any other server. To make them independent I now have an instance of
cloudflared
running on every machine, which doubles as a reverse proxy. Since authentication happens with Access before it reaches the server, I can rely on HTTP headers to tell me the authenticated user. - Internal Networking: I tried using docker's overlay network driver for a while but the swarm stuff was so finnicky I ripped it all out and just connect between machines with Tailscale. In order to connect to a container on another host I either have to know what host it's on or set that container up as a Tailscale node.
- Monitoring: I've been using the overengineered but beautiful Prometheus stack, which includes exporters like Node Exporter and cAdvisor what present data to be scraped by Prometheus and queried by Grafana. I also set up Loki as a log driver in docker, which worked well until it started bugging out and taking hours to reload containers.
You can find more detailed (and more recently-updated) documentation about each individual service I use in my notion docs.