You Have 25 Bookmarks and a Spouse Who Just Wants Jellyfin
It starts innocently. One bookmark for Nextcloud, one for Sonarr, one for the router. Then you add Radarr. Then Prowlarr. Then a second instance of Sonarr for anime because apparently that’s a thing you do now.
Somewhere around bookmark 18, your spouse — who only ever wanted to watch a movie — sends you a message that says “where is the movie thing again.” You have failed.
The fix is a dashboard. A single URL that maps your entire homelab into something a human being can navigate without a PhD in bookmark archaeology. The three main contenders in self-hosted dashboard land are Heimdall, Homepage, and Homer. They all solve the same problem. They solve it very differently.
Here’s the breakdown so you can stop reading GitHub READMEs at midnight and just pick one.
The Three Contenders at a Glance
| Heimdall | Homepage | Homer | |
|---|---|---|---|
| Backend | PHP (LinuxServer.io) | Next.js | Pure static HTML |
| Config method | Web UI + DB | YAML files | YAML file |
| Active dev pace | Slower (as of 2026) | Very active | Maintenance mode |
| Docker auto-discovery | No | Yes (labels) | No |
| Live API widgets | Yes (built-in) | Yes (extensive) | No |
| Page load weight | Medium | Medium | Lightest |
| Best for | GUI config, familiar feel | Power users, YAML fans | Minimal, fast, no fuss |
Heimdall: The Comfortable Old Friend
Heimdall is the dashboard your homelab grew up on. It’s a PHP app maintained by LinuxServer.io, and it gives you a web UI where you add apps by clicking around — no config files, no YAML, no restarting containers every time you want to move a tile.
It supports “enhanced apps” — think of them as live widgets that talk to services like Sonarr, Nextcloud, and Plex to pull in real data. Queue counts, storage used, currently playing. That stuff.
The downside in 2026: development has slowed noticeably. It still works. It still gets security patches. But if you’re hoping for a shiny new widget for the service you added last month, you might wait a while.
Running Heimdall
services: heimdall: image: lscr.io/linuxserver/heimdall:latest container_name: heimdall environment: - PUID=1000 - PGID=1000 - TZ=America/New_York volumes: - ./heimdall/config:/config ports: - 7080:80 - 7443:443 restart: unless-stoppeddocker compose up -d# Open http://localhost:7080# Click the + button. Add your services. Done.That’s genuinely it. No config files. Your 60-year-old neighbor could set this up. The app data lives in a SQLite database inside /config, so back that up or you’ll be re-adding everything after the next disk failure.
Enhanced Apps (The Widget Layer)
In the Heimdall UI, when you add a supported app like Sonarr, you’ll see a toggle for “Enhanced” mode. Flip it, drop in your API key, and the tile shows live data — upcoming episodes, queue size, whatever that app exposes.
Supported enhanced apps include Sonarr, Radarr, Lidarr, Readarr, Plex, Nextcloud, Pi-hole, and a handful of others. The list hasn’t grown much lately, which is the honest limitation.
Homer: The Dashboard That Gets Out of Your Way
Homer is a static HTML single-page app. There’s no backend. There’s no database. There are no widgets that call your services. You give it a YAML file, it renders tiles, and that’s the whole product.
This sounds like a limitation. For a lot of homelab setups, it’s a feature.
Homer loads instantly on any device. It works fine behind the slowest reverse proxy. It runs on hardware you’d otherwise throw away. If your dashboard ever has a “wait, why is this slow?” moment — Homer won’t have that moment.
It’s also in maintenance mode upstream, which means it’s stable and won’t change much. For a static bookmark page, that’s perfectly acceptable.
Running Homer
services: homer: image: b4bz/homer:latest container_name: homer volumes: - ./homer/assets:/www/assets ports: - 8080:8080 restart: unless-stopped user: "1000:1000"Your entire config is one file: assets/config.yml.
---title: "Home Lab"subtitle: "The Art of Wasting Time"logo: "logo.png"header: truefooter: false
theme: defaultcolors: light: highlight-primary: "#3367d6" highlight-secondary: "#4f52ff" highlight-hover: "#5a5fff" background: "#f5f5f5" card-background: "#ffffff" text: "#363636" dark: highlight-primary: "#3367d6" highlight-secondary: "#4f52ff" highlight-hover: "#5a5fff" background: "#131313" card-background: "#2b2b2b" text: "#eaeaea"
services: - name: "Media" icon: "fas fa-film" items: - name: "Jellyfin" logo: "assets/tools/jellyfin.png" subtitle: "The movie thing" url: "https://jellyfin.yourdomain.com" target: "_blank" - name: "Plex" logo: "assets/tools/plex.png" subtitle: "For the other half of the library" url: "https://plex.yourdomain.com" target: "_blank"
- name: "Downloads" icon: "fas fa-download" items: - name: "Sonarr" logo: "assets/tools/sonarr.png" subtitle: "TV automation" url: "https://sonarr.yourdomain.com" target: "_blank" - name: "Radarr" logo: "assets/tools/radarr.png" subtitle: "Movie automation" url: "https://radarr.yourdomain.com" target: "_blank" - name: "Prowlarr" logo: "assets/tools/prowlarr.png" subtitle: "Indexers" url: "https://prowlarr.yourdomain.com" target: "_blank"Edit the file, refresh the browser. No container restart required. Homer watches for config changes and hot-reloads.
The lack of live widgets isn’t a problem if you just want bookmarks with icons. It absolutely is a problem if you want to see your Sonarr queue size without clicking through to Sonarr.
Homepage: The Current Champion
Homepage is what happens when someone looks at Homer and Heimdall and says “what if it did more, and the config was still just YAML.” It’s a Next.js app, actively developed, and it has widgets for basically everything you run in a homelab.
The killer feature is Docker label auto-discovery. Homepage can watch your Docker socket and automatically add services to the dashboard based on labels on your containers. You deploy a new service, stick some labels on it, and it appears in Homepage. No editing config files. No clicking around a UI. It just shows up.
Running Homepage
services: homepage: image: ghcr.io/gethomepage/homepage:latest container_name: homepage ports: - 3000:3000 volumes: - ./homepage/config:/app/config - /var/run/docker.sock:/var/run/docker.sock:ro environment: PUID: 1000 PGID: 1000 restart: unless-stoppedThe Docker socket mount is what enables auto-discovery. Read-only is fine — Homepage doesn’t need to control containers, just see them.
Config lives in ./homepage/config/ as a set of YAML files: services.yaml, widgets.yaml, settings.yaml, bookmarks.yaml, docker.yaml.
services.yaml — Your Service Grid
- Media: - Jellyfin: icon: jellyfin.png href: https://jellyfin.yourdomain.com description: Media server widget: type: jellyfin url: http://jellyfin:8096 key: YOUR_JELLYFIN_API_KEY enableBlocks: true enableNowPlaying: true
- Plex: icon: plex.png href: https://plex.yourdomain.com description: The other media server widget: type: plex url: http://plex:32400 key: YOUR_PLEX_TOKEN
- Downloads: - Sonarr: icon: sonarr.png href: https://sonarr.yourdomain.com description: TV automation widget: type: sonarr url: http://sonarr:8989 key: YOUR_SONARR_API_KEY enableQueue: true
- Radarr: icon: radarr.png href: https://radarr.yourdomain.com description: Movie automation widget: type: radarr url: http://radarr:7878 key: YOUR_RADARR_API_KEY enableQueue: true
- Prowlarr: icon: prowlarr.png href: https://prowlarr.yourdomain.com description: Indexer aggregator widget: type: prowlarr url: http://prowlarr:9696 key: YOUR_PROWLARR_API_KEYThose widgets pull real data. Sonarr shows missing episodes and queue size. Radarr shows monitored movies and what’s downloading. Jellyfin shows active streams. This is the part that makes Homepage feel like a proper ops dashboard rather than a fancy bookmark page.
Docker Auto-Discovery with Labels
This is where Homepage earns its reputation. Drop these labels on any container:
services: sonarr: image: lscr.io/linuxserver/sonarr:latest labels: - homepage.group=Downloads - homepage.name=Sonarr - homepage.icon=sonarr.png - homepage.href=https://sonarr.yourdomain.com - homepage.description=TV automation - homepage.widget.type=sonarr - homepage.widget.url=http://sonarr:8989 - homepage.widget.key=YOUR_SONARR_API_KEYHomepage picks this up automatically — no services.yaml entry needed. Deploy a new service, add the labels, it’s on the dashboard. Delete the container, it disappears. This is the feature that makes maintaining a large homelab actually tolerable.
widgets.yaml — System Info and More
- resources: cpu: true memory: true disk: /
- search: provider: duckduckgo target: _blank
- datetime: text_size: xl format: dateStyle: long timeStyle: short hourCycle: h23docker.yaml — Point at Your Docker Socket
my-docker: socket: /var/run/docker.sockIf you run multiple Docker hosts, you can point Homepage at remote Docker sockets or Portainer instances too.
The X-Frame-Options Gotcha
All three of these dashboards link out to your services. None of them embed your services as iframes by default — and if you try, you’ll hit a wall.
Most self-hosted apps set X-Frame-Options: SAMEORIGIN or X-Frame-Options: DENY, which tells browsers “do not render me inside an iframe from a different origin.” Your dashboard is a different origin. Your iframe will be a blank box.
The fix, if you actually want iframe embedding, is to override the header at your reverse proxy layer. With Caddy:
respond /embed/* { header X-Frame-Options "" header Content-Security-Policy "frame-ancestors 'self' https://dashboard.yourdomain.com"}With Nginx:
location / { proxy_pass http://sonarr:8989; proxy_hide_header X-Frame-Options; add_header X-Frame-Options "ALLOWALL";}Honestly though — don’t do this. Iframes in dashboards are a 2015 idea. The widgets in Homepage and Heimdall show you the data you actually care about. You don’t need to see the full Sonarr UI embedded in a tiny box to know your queue has 3 items.
Theme and Branding Tips
Homer: Drop a custom CSS file at assets/custom.css. Homer loads it automatically if it exists. Full control, no config option needed.
Heimdall: Settings > Custom CSS in the web UI. Or mount a custom CSS file into /config/www/. The tile colors are per-app in the UI.
Homepage: settings.yaml handles theme, color scheme, and layout. You can set color: slate or any Tailwind color name, choose light or dark base, and control column counts per breakpoint:
title: Home Labtheme: darkcolor: slateheaderStyle: cleanlayout: Media: style: row columns: 4 Downloads: style: row columns: 3Homepage also supports custom CSS via a custom.css file in the config directory. The default dark theme is legitimately good out of the box, which is more than you can say for most self-hosted tools.
The Bottom Line
Pick Homer if: You want bookmarks with icons and you never want to think about the dashboard again. It loads fast, it’s dead simple, and it does exactly what it says. If your household just needs links and you don’t care about seeing live queue counts, Homer is fine. It’s been fine for years.
Pick Heimdall if: You hate editing YAML and want to manage everything through a UI. It’s the most accessible option for anyone who isn’t comfortable with config files, and the enhanced app widgets cover the popular services well. Understand that development has slowed and the widget library won’t grow much — but what’s there works.
Pick Homepage if: You have more than 10 services, you want live data from those services, and you’re willing to write some YAML to get it. The Docker label auto-discovery alone is worth the setup cost once your homelab grows past a certain size. When you can deploy a new container and have it appear on the dashboard automatically, that’s when you realize manual bookmark management was genuinely a waste of your time.
The honest answer for most homelabs in 2026: Homepage. The widget ecosystem is mature, auto-discovery is genuinely useful, and the development pace means you’re not going to hit a wall because your preferred service isn’t supported. Your spouse will be able to find Jellyfin. That’s the whole point.