← Back to projects

Home Server: Self-Hosted Apps on Ubuntu

In Progress

Ubuntu · Linux · Docker · Docker Compose · Tailscale · Immich · Nextcloud · PostgreSQL · Redis · AdGuard Home · Unbound · DNS · UFW · fail2ban · SSH · systemd · Hermes Agent · Discord

I turned an old 2014 Mac mini into a headless home server. The goal is to replace my Google One subscription, so it's meant to hold the photos and files from my 3 Google accounts. It also blocks ads on my own devices with my own DNS, and I can reach and manage it securely from anywhere, including my phone.

The hardware is small: a dual-core Intel i5, 8GB of RAM that's soldered on, so that's the ceiling, and a 256GB SSD. I wiped macOS, installed Ubuntu 26.04 LTS and turned off the desktop, so it boots straight to the command line and saves RAM. It runs headless on Ethernet, with mbpfan for proper Mac fan control and sleep and suspend turned off. It has a static IP on my home network, with a matching DHCP reservation on the router, so the address never clashes.

Every app runs in Docker. Immich is there to replace Google Photos, tuned for the weak CPU with lower job concurrency and video transcoding turned off. Nextcloud, with Postgres and Redis, is there to replace Google Drive, and I fixed its trusted-domain settings so it works properly. AdGuard Home blocks ads over DNS, but only on my own devices, so the rest of the household isn't affected. It forwards to Unbound, a caching DNS resolver on the same machine, with Cloudflare and Quad9 as fallback if Unbound fails. When I tested it with dig, cached lookups dropped to about 1ms, DNSSEC validation worked, and ad domains were still blocked.

I reach everything through Tailscale, with HTTPS on every app through tailscale serve, and I SSH in from my laptop or my iPhone (Termius, with its own key). SSH only takes keys, with password and root login turned off, and the server also runs UFW, fail2ban and automatic security upgrades. Load-shedding power cuts are common here, so a small systemd service turns the Mac back on after power loss.

Hermes Agent from Nous Research lets me control the server from my phone through a private Discord bot. I locked it down on purpose. Only my Discord user can talk to it, every action needs my approval, and a deny-list blocks sudo, destructive Docker volume commands, disabling or resetting the firewall, and taking Tailscale down or logging it out. Tools I don't use, like browser, vision, image generation and computer use, are turned off.

I ran every setup step myself on purpose, to learn real Linux and DevOps work instead of letting automation tools do it.

Architecture

Home server architecture MY DEVICES Laptop Phone iPhone, Android Tailscale private access, HTTPS Mac mini, Ubuntu 26.04 LTS headless, on Ethernet DOCKER Immichphotos Nextcloudfiles, Postgres, Redis AdGuard HomeDNS ad blocking UUnboundcaching DNS resolver HHermes Agentcontrol from my phone Internet

Home server architecture: my laptop and phone connect through Tailscale to the Mac mini running Ubuntu. Inside Docker it runs Immich, Nextcloud, AdGuard Home, Unbound and Hermes Agent. AdGuard Home forwards DNS to Unbound, which goes out to the internet.

Challenges

  • The Mac's Broadcom Wi-Fi had no driver after the install, so I got internet through Bluetooth tethering to my iPhone to install it.
  • I had no spare monitor, so I used a capture card to see the Mac mini on my laptop during the install.
  • The dual-core CPU is old, so I tuned Immich so it doesn't choke during photo processing.
  • Power cuts kept taking the server and router down, so I added auto power-on after power loss. A UPS is next.
  • Telegram signup in Pakistan wanted a paid SMS fee, so I used Discord for the agent instead.
  • A YAML indentation bug in the Hermes config stopped the bot from connecting until I fixed it.

Next steps

  • External backup drive, with restic backups on a timer
  • Import Google Takeout from all 3 accounts: photos into Immich with immich-go, files into Nextcloud
  • Test a full restore, then cancel Google One
  • A UPS for the router and Mac mini
  • WhatsApp backup, and a music library for Navidrome (set up, but paused for now)