Resist the urge to spin up ten services in one afternoon. Get two or three running well, understand how you'd back each one up, and add from there. These three are chosen because they're genuinely useful day one, not just tech demos.

DNS-level ad blocking: Pi-hole or AdGuard Home

Both block ads and trackers network-wide by acting as your network's DNS server, filtering known ad/tracker domains before they resolve. Pick one, not both:

  • Pi-hole - the longer-established option, huge community, web UI is slightly more info-dense.
  • AdGuard Home - newer, arguably cleaner UI, built-in DNS-over-HTTPS support out of the box.
services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8081:80"
    environment:
      - TZ=Europe/London
      - FTLCONF_webserver_api_password=set-a-real-password
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    restart: unless-stopped
  • FTLCONF_webserver_api_password sets the web UI password. Pi-hole v6 renamed this from the older WEBPASSWORD variable still floating around in outdated tutorials - use the current name, or Pi-hole silently generates a random password instead of the one you set.

After it's running, point your router's DHCP DNS setting at this machine's IP instead of your ISP's DNS - that's what makes the blocking apply network-wide rather than per-device. Covered further in Networking Basics.

Resource expectations: trivial - well under 512MB RAM, negligible CPU, at typical home network query volumes.

A media server: Jellyfin

Free, open-source, no account or cloud dependency required (unlike Plex, which routes some functionality through its own servers even for local playback).

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    ports:
      - "8096:8096"
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /path/to/your/media:/media
    restart: unless-stopped

Resource expectations: idle is light, but transcoding (converting video on the fly for a device that can't play the source format natively) is CPU-intensive - a modest mini PC can transcode one 1080p stream in software; multiple simultaneous transcodes or 4K will want either a beefier CPU or hardware transcoding via an iGPU, which needs extra device passthrough in the compose file (check Jellyfin's docs for your specific hardware).

A dashboard: Homepage or Homarr

Once you have three or four services, remembering IP:port combinations gets old. A dashboard gives you one bookmark with tiles linking to everything else:

services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    ports:
      - "3001:3000"
    volumes:
      - ./config:/app/config
    restart: unless-stopped

Configuration is YAML files describing your services, bookmarks, and optionally live widgets (e.g. showing Pi-hole's block count) - worth the half hour of setup once you have more than a couple of services to track.

A note on exposing any of this to the internet

⚠️ Risk: none of the above should be reachable from the public internet by default. Keep them on your local network only for now. Port-forwarding a router directly to a homelab service is covered, along with why it's usually the wrong first move, in Networking Basics - the safer pattern (a reverse proxy plus a VPN) is an Intermediate topic for a reason.

Next: Networking Basics.