May 20, 2026

How I Self-Host 17 Services on a Mac Mini

The complete guide to running a full homelab on Apple Silicon — from Docker stacks to reverse proxy, with compose files you can copy.

homelab docker mac-mini self-hosting infrastructure

How I Self-Host 17 Services on a Mac Mini

I run my entire digital life on a Mac mini M4. Media server, Git, backups, monitoring, a custom freight dashboard, even an IRC bot — all running in Docker, all reachable from anywhere.

Here’s the full stack, how it’s wired together, and the exact compose files you can copy to get started.

The Hardware

DeviceRole
Mac mini M4Primary compute, Docker host
UGREEN NASStorage, compose files, media library
Raspberry Pi 5AdGuard Home, WireGuard (planned)
Raspberry Pi 4Media center (planned)
Raspberry Pi 3BHome Assistant (planned)

The Mac mini has 16GB RAM and a 512GB SSD. That’s it. No rack, no server motherboard, no noise.

The Software Stack

Everything runs in Docker via OrbStack on macOS. I group services into logical stacks:

Core Infrastructure

ServicePurposePort
PortainerContainer management UI9000
Nginx Proxy ManagerReverse proxy + SSL80/443/81
WatchtowerAuto-update containers
DuplicatiBackups to NAS + B28200
GiteaSelf-hosted Git3000

Media Acquisition (“arr” Stack)

ServicePurposePort
ProwlarrIndexer aggregator9696
RadarrMovie management7878
SonarrTV management8989
LidarrMusic management8686
qBittorrentTorrent client8080
JellyseerrRequest UI5055
slskdSoulseek client5030
Mylar3Comics management8090
MeTubeYouTube downloads8081

Media Server

ServicePurposePort
JellyfinMedia server8096
PortfolioStatic site (nginx)3001

Monitoring

ServicePurposePort
BeszelSystem monitoring8089
DozzleDocker log viewer8888
HomepageService dashboard3005
DashboardCustom status page3004

Tools

ServicePurposePort
AnythingLLMLocal LLM chat3002
PlankaKanban board3333
Daily BriefNews aggregator3003

Total: 17 services across 6 compose stacks.

Storage Architecture

The key insight: compose files live on the NAS, container data lives on the Mac mini’s SSD.

/Volumes/homelab/compose/     # NAS — all docker-compose.yml files
~/homelab-data/               # Mini SSD — container config volumes
/Volumes/homelab/media/       # NAS — Jellyfin library + downloads

This gives me:

Networking

Tailscale (Admin Access)

Install Tailscale on any device, log in, and you have direct access to all services:

100.102.8.89:9000   # Portainer
100.102.8.89:7878   # Radarr
100.102.8.89:8989   # Sonarr
100.102.8.89:8096   # Jellyfin

Cloudflare Tunnel (Public Access)

Seven services are public-facing via Cloudflare Tunnel — no port forwarding, no DDNS:

URLService
https://iamfaulty.comPortfolio
https://jellyfin.iamfaulty.comJellyfin
https://request.iamfaulty.comJellyseerr
https://gitea.iamfaulty.comGitea
https://home.iamfaulty.comHomepage
https://dailybrief.iamfaulty.comDaily Brief
https://irc.iamfaulty.comIRC

The Compose Files

Core Stack

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    ports: ["9000:9000"]
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ~/homelab-data/portainer:/data

  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: npm
    restart: unless-stopped
    ports: ["80:80", "443:443", "81:81"]
    volumes:
      - ~/homelab-data/npm:/data
      - ~/homelab-data/npm/letsencrypt:/etc/letsencrypt

  watchtower:
    image: containrrr/watchtower:latest
    container_name: watchtower
    restart: unless-stopped
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    environment:
      - WATCHTOWER_CLEANUP=true
      - WATCHTOWER_POLL_INTERVAL=3600

  duplicati:
    image: lscr.io/linuxserver/duplicati:latest
    container_name: duplicati
    restart: unless-stopped
    ports: ["8200:8200"]
    volumes:
      - ~/homelab-data/duplicati:/config
      - /Volumes/homelab:/source:ro
      - /Volumes/faultycloud:/backup

  gitea:
    image: gitea/gitea:latest
    container_name: gitea
    restart: unless-stopped
    ports: ["3000:3000", "222:22"]
    volumes:
      - ~/homelab-data/gitea:/data

Jellyfin

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    restart: unless-stopped
    ports: ["8096:8096"]
    volumes:
      - ~/homelab-data/jellyfin/config:/config
      - ~/homelab-data/jellyfin/cache:/cache
      - /Volumes/homelab/media:/media:ro

See the private wiki for complete compose files for all 17 services.

What It Costs

ItemCost
Mac mini M4 (16GB)~$800 (one-time)
UGREEN NAS (4-bay)~$300 (one-time)
Cloudflare TunnelFree
TailscaleFree (personal)
Backblaze B2 backups~$2/month
Domain (iamfaulty.com)$12/year

Total monthly: ~$3 for a full self-hosted platform.

Lessons Learned

  1. Put compose files on the NAS. When I rebuild the mini, I just re-mount the NAS and docker compose up.
  2. Use Nginx Proxy Manager. SSL certificates, reverse proxy rules, and access lists — all in a web UI.
  3. Tailscale for admin, Cloudflare for public. Two paths, zero port forwarding.
  4. Watchtower with CLEANUP=true. Auto-updates without filling disk with old images.
  5. SMB → NFS for arr hardlinks. Still migrating this. NFS enables instant imports without double-copies.

What’s Next


Want the full starter pack? Get the Docker Compose files →

Questions? Find me at iamfaulty.com or on IRC.