For the last couple of weeks I have been sorting out my home server configuration. I currently have 2 servers running at home, which I call Jarvis and Friday.
- Jarvis — This is a Dell Wyze 5070 thin client which I have on 24/7. It has 128 GB hard drive and 8 GB of RAM and uses between 5W and 15W of power.
- Friday — This is my NAS server made up of recycled parts. It runs Unraid and currently has a 4 TB array with an additional 1.5 TB for cache. I wrote about this server the other month. This uses between 50W and 120W of power, so I only turn it on when needed.
These both run various services in docker containers such as these to name a few:
- LinkDing — saving bookmarks
- Calibre & Calibre Web — managing my e-book collection
- Home Assistant — controls my smart home and bridges to Apple HomeKit
- VaultWarden — managing my passwords with BitWarden.
- SyncThing — syncing my notes to my phone
- ForgeJo — for private Git repositories
- JellyFin — home media server
- Actual Budget — personal finance, net worth and budgeting.
All of these services are mapped to a port other than port 80, to access them I would need to go to http://jarvis:9090 for example.
Given the amount of services I am running it is not practical to access them this way. Occasionally I would try to host a service that wanted to use a port I was already using, and I would then have to map it to something else, being careful not to cause another conflict.
This is where a reverse proxy comes in. A reverse proxy allows you to access services using a subdomain or a subfolder structure. So I might access ForgeJo via http://jarvis/git.
I have been using Traefik to do this for a few years, recently however it has been using large amounts of RAM (> 800 MB) and crashing my server. Overall it is probably overkill for my little home setup. I also never got around to setting up subdomains with Traefik, but I assume in theory I could have done something like git.jarvis.lan.
Not all services cope well with being set up as a subfolder structure as it requires a way of setting a base path which not all services offer.
A couple of my services (Actual Budget and VaultWarden) require an SSL connection to access them. This was done using a service called caddy. Caddy handles all the reverse project magic and sets up SSL certificates for you with LetsEncrypt. I already had a domain name that I use for my personal email, so I used this to set up subdomains for my services.
I have my DNS records set up with Cloudflare, so all I needed to do was set up a wildcard domain *.example.com that pointed to the local IP address of my server 192.168.1.20.
With caddy set up, I could then access my services using a subdomain such as https://vault.example.com.
Due to the issues I was having with Traefik, I decided to move all my services over to caddy with their own subdomain.
Caddy can do a lot more than just acting as a reverse proxy, but let's have a look at how this is set up.
Caddy reverse proxy setup #
As I am using Cloudflare for my DNS, I need to use caddy with the Cloudflare plugin installed.
This required setting up a custom Dockerfile that pulled down the Cloudflare plugin:
FROM caddy:2-builder AS builder
RUN xcaddy build \
--with github.com/caddy-dns/cloudflare
FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
My docker-compose file would then look like this:
version: "3.7"
services:
caddy:
build: .
restart: always
ports:
- 80:80
- 443:443
env_file:
- .env
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./data:/data
- ./config:/config
- $PWD/Caddyfile:/etc/caddy/Caddyfile
extra_hosts:
- "host.docker.internal:host-gateway"
networks:
- caddy
networks:
caddy:
driver: overlay
external: true
In order for caddy to see your other services they all need to be on the same network. This either means putting them all in the same docker-compose file or setting up an external network (I set up a network called caddy) that they all share.
You then need to set up a .env file with your email and Cloudflare credentials:
ACME_EMAIL="alex@example.com"
CLOUDFLARE_API_TOKEN=sDSJShds9s9hjs9s9hff_sus8euwh
ACME_AGREE=true
TZ='Europe/London'
The magic happens in the Caddyfile:
{
email {$ACME_EMAIL}
acme_dns cloudflare {$CLOUDFLARE_API_TOKEN}
}
# ActualBudget
budget.example.com {
encode gzip
reverse_proxy actual:5006
tls {
protocols tls1.3
}
}
As all the docker containers are on the same network you can reference them by name.
So actual in this case is the name of the service in my docker-compose file for Actual Budget. It runs on port 5006 but as I am accessing it using the docker network I don't need to expose this port on the actual container (remove the ports lines).
I have a couple of other services that are set up slightly differently.
Home Assistant for example is using host networking so that it can access all the devices on my network. This therefore needs to use the IP address of my server instead of the service name:
# HomeAssistant
home.example.com {
encode gzip
reverse_proxy 192.168.1.20:8123
tls {
protocols tls1.3
}
}
Jellyfin I have running on my Friday server so again needs to use an IP address:
# Jellyfin
videos.example.com {
encode gzip
reverse_proxy 192.168.1.30:8096
tls {
protocols tls1.3
}
}
Now all my services are running under their own subdomain with SSL.
❤️ Picks of the Week #
📝 Article — Vanilla JSX — Many of us are reliant on frameworks to be able to build anything these days, effectively “Standing on the shoulders of giants”. We forget that it is possible to build many of these things with just vanilla JavaScript, this is a great example.
📚 Book — Super Human by Dave Asprey — I have been reading this book for the past couple of weeks. Dave Asprey plans on living to 180! To be honest, he tries so many experimental treatments that I think one of them will likely be the end of him sooner than 180. Such as “He Shou Wu” (also known as “Fo-Ti”) a Chinese herb which is supposed to treat hair loss and reduce grey hairs, he doesn't mention in the book that it also causes liver damage! There are some studies backed with scientific data in the book and I plan to try some to see if it has any noticeable impact on my health.
📝 Article — Design flaw has Microsoft Authenticator overwriting MFA accounts, locking users out — Something to be aware of if you use Microsoft Authenticator.
📝 Article — Leaving Neovim for Zed — I am still fairly reliant on VS Code extensions for most of my work. I think if my main language wasn't C# I would try out another IDE. If you are a C# developer, and you use Zed let me know your experience with it. It is supposed to be a lot faster that VS Code, although I suspect the speed differences on an M3 Mac would be negligible.
🛠️ Tool — PgQueuer is a Python library to turn PostgreSQL into a job queue — I am big fan of PostgreSQL since I have been using it extensively at work. This looks like an interesting project. I am not sure if it is production ready yet, but it would be good for some personal projects.
🛠️ Tool — 13ft , self-hosted 12ft.io replacement — I am not a fan of paywalls for articles, but luckily there are services out there that can get rid of them. Being able to self-host one is even better if it works.
📝 Article — Avoiding CDN supply-chain attacks with Subresource Integrity (SRI) — I only have a script for Google Fonts on my website but if you are remote loading any resources this is worth looking into. Especially after a Chinese company injected malicious behaviour into Pollyfill.io.
🛠️ Tool — Zen Browser — this browser looks nice and minimalist and based on Firefox too. We are in dire need of a privacy focused browser especially after Mozilla seem to have sold out (see below).
📝 Article — make-firefox-private-again.com — Mozilla recently bought advertising company Anonym. It seems like Mozilla is taking the same path as Google and putting users needs second. In short go to about:config and set dom.private-attribution.submission.enabled to false.
📝 Article — Being on The Semantic Web is easy, and, frankly, well worth the bother — Something else to add to my website to-do list!
💬 Quote of the Week #
“Happiness is simply the absence of desire… Happiness is not about the achievement of pleasure (which is joy or satisfaction), but about the lack of desire. It arrives when you have no urge to feel differently. Happiness is the state you enter when you no longer want to change your state.”
From the book "Atomic Habits" by James Clear.
Top comments (0)