Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Home Server — Self-Hosted Infrastructure

A single Intel NUC running Ubuntu Server 24.04 LTS and eleven Docker containers, serving a household: photo backup, Time Machine backups for Macs, network-wide DNS filtering, home automation, document archiving, and a dashboard to find it all.

This repo is the documentation and the operational fixes — the write-up of an actual running system, including the bugs I found in upstream software and the systemd units I wrote to stop them coming back after a reboot.

All addresses, hostnames and credentials in this repo are placeholders.

The stack

Service What it does Notes
Immich Self-hosted photo & video backup (Google Photos replacement) Postgres + Redis + server; 3.1 TB library on a dedicated disk
TimeNest Self-hosted Time Machine target (Samba + vfs_fruit + Avahi + web admin) Three containers; 293 GB dedicated partition
AdGuard Home Network-wide DNS filtering and ad-blocking for every device Authoritative DNS for the LAN
Home Assistant Home automation Host networking for device discovery
Papra Document / receipt archive
Homer Static dashboard linking every service
Bambu Studio Containerised 3D-print slicer with a web UI
CasaOS Host-level app dashboard and native Samba shares Runs as native systemd services, not a container

Host OS: Ubuntu 24.04.4 LTS · Runtime: Docker CE from the official .deb repo (deliberately not the snap, which sandboxes bind mounts and breaks external drives).

Documentation

Doc Contents
docs/architecture.md Hardware, storage layout, networking, container inventory
docs/storage.md Disk partitioning, filesystem choices, fstab and why nofail matters
docs/timenest-samba.md The Samba/Time Machine service, and the upstream bug I found in it
docs/port-conflict.md Two Samba daemons fighting over ports 139/445 — and why the textbook fix failed
docs/reboot-resilience.md The three systemd changes that make the whole thing survive power loss
docs/runbook.md Diagnostic commands, health checks, recovery procedures

Three problems worth reading about

These are the parts I'd actually want to talk through in an interview.

1. An upstream provisioning bug that silently broke all authentication

Samba requires two things to authenticate a user: an entry in its password database (passdb) and a resolvable Unix account (getpwnam()). The TimeNest web UI created the first but not reliably the second, so every login failed with:

Failed to find a Unix account for <user>
User <user> in passdb, but getpwnam() fails!
check_sam_security: make_server_info_sam() failed with 'NT_STATUS_NO_SUCH_USER'

Fixing it once was easy. The interesting part was that the fix lived in the container's non-persistent writable layer, so any image update or --force-recreate silently undid it. I also learned to pin explicit UIDs — a bare useradd picks the next free UID, which orphans the ownership of every existing backup folder on disk. → docs/timenest-samba.md

2. Two Samba servers, one pair of ports, and a fix that didn't work

CasaOS runs its own native smbd/nmbd on 139/445. TimeNest's compose file uses network_mode: host — deliberately, because Bonjour/mDNS discovery wants real LAN access. Two daemons cannot both reliably bind 0.0.0.0:445; the OS hands each incoming connection to whichever one won, so both sets of shares broke intermittently and non-reproducibly.

The textbook fix is an ipvlan network giving each container its own LAN IP. I built it. It failed — because this server is on Wi-Fi, and ipvlan/macvlan are unreliable on most Wi-Fi adapters, which reject frames using a MAC other than the card's associated one. Containers were unreachable even from the host. I rolled it back and moved Samba to bridge networking with explicit port mapping instead, accepting a documented trade-off in Time Machine auto-discovery. → docs/port-conflict.md

3. A boot race that would have silently corrupted backups

Docker could start before /mnt/timenest finished mounting. The bind mount would then create an empty directory on the root filesystem instead of pointing at the real 293 GB drive — the service would come up looking healthy while writing backups nowhere useful, and filling the boot disk. Fixed with a systemd drop-in ordering Docker after the mount unit, plus a oneshot unit that re-applies the account fix on every boot. Verified with a real reboot test, not just systemctl status. → docs/reboot-resilience.md

Repo contents

docs/                       full write-ups of each area
scripts/
  timenest-fix-accounts.sh  idempotent Unix-account repair, run on every boot
  health-check.sh           one-shot container / disk / port / DNS health report
systemd/
  timenest-fix.service      oneshot unit that runs the repair script
  wait-for-timenest-drive.conf   docker.service drop-in: wait for the mount

What I took away from building it

  • Containers are not a persistence model. Anything you fix with docker exec is gone the next time the container is recreated. Fixes belong in a mounted volume, an image, or a unit that re-applies them.
  • The correct solution and the solution that works are not always the same. ipvlan was right on paper; Wi-Fi made it wrong in practice. Knowing why it failed is worth more than the fix.
  • Boot ordering is a correctness problem, not a convenience one. The failure mode wasn't a crash, it was silent data going to the wrong disk.
  • Write it down while you still remember. Everything here was documented as I fixed it, which is the only reason a second pass took minutes instead of a re-diagnosis.

License

MIT

About

Self-hosted infrastructure on Ubuntu + Docker: photo backup, DNS filtering, Time Machine, home automation. Includes an upstream bug root-caused from Samba auth logs and systemd hardening verified by reboot test.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages