First Services
Pi-hole or AdGuard, a media server, and a dashboard - three real, satisfying wins to run before chasing anything more elaborate.
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_passwordsets the web UI password. Pi-hole v6 renamed this from the olderWEBPASSWORDvariable 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.