Skip to content

self hosting 101

My Setup

My homeserver is my old laptop running Arch Linux. All the services are categorized in groups and managed one docker-compose.yml file for each group, along with one .env file for all the groups. my setup works for me and I don’t require anything more complicated than this.

Apps

  1. Administration
    1. Caddy
    2. Adguard Home
    3. Beszel (Monitoring Dashboard)
    4. Dozzle (Logs)
    5. Scrutiny (Hard Drive Health Check)
  2. Personal
    1. Immich (Google Photos alternative)
    2. SyncThing
    3. Samba (SMB share for windows/mac/android)
  3. Media
    1. Jellyfin
    2. *Arr Stack

Wiki

My wiki lives as a folder of markdown files, which I edit using Obsidian, Sync to between my PC and homeserver using Syncthing, and have a CRON job which deploys it every hour to Cloudflare Pages.

Resume CI/CD

I Write my Resume as a TOML file, in my Github Repo. I have a Github Action which runs on any push with tags like “v3.1” and compiles a PDF from the TOML file using Typst, uploads it as a Github Release.

So when you visit resume.advik.one, you make a request to a Cloudflare Worker, which pulls the PDF from the latest Github Release and serves it to you.

Is it Overcomplicated? yes. Did I have fun making it? also yes.

Domain

If you don’t have a public IP address, then you would need cloudflare tunnel. If you have a public IPv6, then you would need Dynamic DNS with cloudflare proxy. If you have a public IPv4, then you don’t need anything else.

Cloudflare API Token

Head over to API Tokens and create a new token. Grant the following permissions:

  • Zone > Zone > Read
  • Zone > DNS > Edit Copy & Paste the token in your env file

Security

Right now our home server is accessible from anywhere in the world, thanks to IPv6. But do we really want that? Let’s demonstrate why this is a bad idea by grabbing the public IP address of our machine and looking for open ports.

Exposing any service to the public web has inherent risks no matter how secure the service itself is, so it is wise to block all incoming requests except for the ones from Cloudflare.

Terminal window
# Allow localhost
sudo iptables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -i lo -j ACCEPT
# Allow local network
sudo iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT
sudo ip6tables -A INPUT -s fe80::/64 -j ACCEPT # Link-local IPv6 addresses
# Allow connections established from host
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Allow ICMP
sudo iptables -A INPUT -p icmp -j ACCEPT
sudo ip6tables -A INPUT -p icmpv6 -j ACCEPT
# Allow Cloudflare IP ranges
sudo iptables -A INPUT -s 173.245.48.0/20 -j ACCEPT
sudo iptables -A INPUT -s 103.21.244.0/22 -j ACCEPT
sudo iptables -A INPUT -s 103.22.200.0/22 -j ACCEPT
sudo iptables -A INPUT -s 103.31.4.0/22 -j ACCEPT
sudo iptables -A INPUT -s 141.101.64.0/18 -j ACCEPT
sudo iptables -A INPUT -s 108.162.192.0/18 -j ACCEPT
sudo iptables -A INPUT -s 190.93.240.0/20 -j ACCEPT
sudo iptables -A INPUT -s 188.114.96.0/20 -j ACCEPT
sudo iptables -A INPUT -s 197.234.240.0/22 -j ACCEPT
sudo iptables -A INPUT -s 198.41.128.0/17 -j ACCEPT
sudo iptables -A INPUT -s 162.158.0.0/15 -j ACCEPT
sudo iptables -A INPUT -s 104.16.0.0/13 -j ACCEPT
sudo iptables -A INPUT -s 104.24.0.0/14 -j ACCEPT
sudo iptables -A INPUT -s 172.64.0.0/13 -j ACCEPT
sudo iptables -A INPUT -s 131.0.72.0/22 -j ACCEPT
sudo ip6tables -A INPUT -s 2400:cb00::/32 -j ACCEPT
sudo ip6tables -A INPUT -s 2606:4700::/32 -j ACCEPT
sudo ip6tables -A INPUT -s 2803:f800::/32 -j ACCEPT
sudo ip6tables -A INPUT -s 2405:b500::/32 -j ACCEPT
sudo ip6tables -A INPUT -s 2405:8100::/32 -j ACCEPT
sudo ip6tables -A INPUT -s 2a06:98c0::/29 -j ACCEPT
sudo ip6tables -A INPUT -s 2c0f:f248::/32 -j ACCEPT
# Drop everything else
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP

Future Improvements

Fail2Ban

I should probably have it but I’m lazy to set it up, and v6 address space is big enough that I never get any bots (I have checked the access logs).

Random Issues

WiFi auto reconnect after a disconnect

[Match]
Name=wl*
[Network]
# IPv4 Configuration
Address=192.168.1.100/24
Gateway=192.168.1.1
DNS=127.0.0.1
# IPv6 Configuration
Address=fe80::100/64
Gateway=fe80::1
DNS=::1

Chrome android ignoring local DNS

https://bugdrivendevelopment.net/browser-ignore-internal-dns/ similar issue on android, fixed by manually setting the dns server in the wifi settings. works on android 13 custom rom.