Introduction
"More and more services, longer and longer bookmarks, and every time you need something you have to think for a moment — it's time for a dashboard."
This is the 182nd article in the "One Open Source Project a Day" series. Today's project is Homepage.
If you run a home server or NAS with Jellyfin, Nextcloud, Sonarr, Portainer, and a dozen other services all humming away — you've definitely felt the pain: every service lives on its own port, you manage them through memory or bookmarks, and the only way to check if something's healthy is to open it and look.
Homepage solves exactly this. It's a unified entry point dashboard for self-hosted services — configure all your services in YAML, auto-discover Docker containers, and get live status plus key metrics on a single page.
32.6k Stars, GPL-3.0, 200+ contributors, and Next.js static generation for blazing-fast loads.
What You Will Learn
- Homepage's YAML configuration system (services, widgets, bookmarks, settings — four files)
- How Docker Label auto-discovery works
- 150+ Service Widget integrations (Sonarr, AdGuard Home, and more as examples)
- Secure secret injection with environment variables
- How Homepage stacks up against similar tools (Dashdot, Heimdall, Homarr)
Prerequisites
- Basic familiarity with Docker and docker-compose
- Comfortable reading and writing YAML
Project Background
What It Is
Homepage positions itself as a configuration-driven self-hosted service dashboard. It isn't a drag-and-drop visual editor — you describe your service layout and integrations precisely through YAML files. This design decision pays off in two concrete ways: your config can live in version control, and it can be reused across multiple machines without any re-clicking.
All API requests are proxied through the backend. The browser never sees your API keys — which addresses a real security problem common in similar tools.
Author / Team
- Primary maintainer: @shamoon
- Contributors: 200+
- License: GPL-3.0
Project Stats
- ⭐ GitHub Stars: 32,600+
- 🍴 Forks: 2,100+
- 📄 License: GPL-3.0
- 💻 Stack: Next.js (React) + YAML config
- 🌐 Website: gethomepage.dev
- 🐳 Image:
ghcr.io/gethomepage/homepage:latest
What It Does
The Problem It Solves
Self-hosting without a dashboard:
Jellyfin → http://192.168.1.100:8096
Nextcloud → http://192.168.1.100:8080
Sonarr → http://192.168.1.100:8989
Portainer → http://192.168.1.100:9000
Grafana → http://192.168.1.100:3000
… (and ten more)
Problems:
- IP + port combos live in your memory or bookmarks
- A service goes down and you don't notice until you need it
- How many things are in the media library? What's in the download queue?
You have to navigate in to find out.
With Homepage:
One page → entry points to all your services
Service cards → live status (online / offline / response time)
Widgets → key metrics right on the card (episode count, currently playing, disk usage…)
Use Cases
-
Home server / NAS management panel
- Everything in one place; a single entry point for all devices on your local network
-
Media center dashboard
- What's playing in Jellyfin/Plex, Sonarr/Radarr download queue status, disk usage — at a glance
-
Network and security monitoring
- AdGuard Home block statistics, Uptime Kuma availability, Traefik routing status — live
-
Internal team tool navigation
- A shared entry page for a small team's internal services, replacing hand-maintained bookmarks
-
VPS multi-service management
- Manage all services on a VPS from one page, safely accessible externally behind a reverse proxy
Quick Start
The minimal docker-compose deployment:
services:
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
ports:
- 3000:3000
volumes:
- ./config:/app/config # YAML config directory
- /var/run/docker.sock:/var/run/docker.sock:ro # Docker auto-discovery (optional)
environment:
HOMEPAGE_ALLOWED_HOSTS: your-domain.dev # or LAN IP
restart: unless-stopped
After launch, visit http://localhost:3000 and edit the YAML files in ./config/ to configure your services.
Core Features
1. Four YAML Configuration Files
| File | Purpose |
|---|---|
services.yaml |
Define service groups and service cards |
widgets.yaml |
Top-of-page info widgets (clock, weather, search bar, etc.) |
bookmarks.yaml |
Quick-access link groups |
settings.yaml |
Global settings (title, theme, layout, language, etc.) |
2. 150+ Service Widget Integrations
Each service card can embed a widget that pulls live data directly from the corresponding service's API:
| Category | Representative Services |
|---|---|
| Media management | Sonarr, Radarr, Lidarr, Readarr, Bazarr |
| Media servers | Jellyfin, Plex, Emby, Navidrome |
| Download clients | qBittorrent, Transmission, Deluge, SABnzbd, NZBGet |
| Network tools | AdGuard Home, Pi-hole, Traefik, Nginx Proxy Manager |
| Monitoring | Uptime Kuma, Grafana, Prometheus, Netdata, Glances |
| Storage / cloud | TrueNAS, Nextcloud, Portainer |
| System info | CPU, memory, disk, network usage (built-in) |
3. Docker Label Auto-Discovery
With the Docker socket mounted, just add homepage.* labels to any container and it appears in the dashboard automatically — no services.yaml edits needed:
# Add these labels to any other container's docker-compose
services:
sonarr:
image: linuxserver/sonarr
labels:
homepage.group: Media
homepage.name: Sonarr
homepage.icon: sonarr.png
homepage.href: http://sonarr:8989/
homepage.description: Series management
homepage.widget.type: sonarr
homepage.widget.url: http://sonarr:8989
homepage.widget.key: your-api-key
4. Secure Secret Management
API keys don't live in YAML — they're injected via environment variables:
# services.yaml uses a placeholder
widget:
type: sonarr
url: http://sonarr:8989
key: {{HOMEPAGE_VAR_SONARR_KEY}} # placeholder
# docker-compose.yml injects the real value
environment:
HOMEPAGE_VAR_SONARR_KEY: your-actual-api-key
The config files are safe to commit to Git.
5. Multi-Platform and Localization
- AMD64 and ARM64 support (covers Raspberry Pi and similar ARM devices)
- 40+ languages
- Light / dark theme switching
A Deeper Look
The Configuration System in Practice
A typical services.yaml looks like this:
- Media:
- Jellyfin:
icon: jellyfin.png
href: http://jellyfin:8096/
description: Media server
siteMonitor: http://jellyfin:8096/
widget:
type: jellyfin
url: http://jellyfin:8096
key: {{HOMEPAGE_VAR_JELLYFIN_KEY}}
fields: ["movies", "series", "episodes"]
- Sonarr:
icon: sonarr.png
href: http://sonarr:8989/
description: Series management
widget:
type: sonarr
url: http://sonarr:8989
key: {{HOMEPAGE_VAR_SONARR_KEY}}
fields: ["wanted", "queued", "series"]
- Network:
- AdGuard Home:
icon: adguard-home.png
href: http://adguard:80/
widget:
type: adguard
url: http://adguard:80
username: admin
password: {{HOMEPAGE_VAR_ADGUARD_PASS}}
fields: ["queries", "blocked", "filtered"]
The siteMonitor field sends periodic HTTP HEAD requests and displays online status with response time on the card — no API integration required, so any HTTP service can be monitored.
Why Next.js Static Generation
Homepage uses Next.js with an unusual rendering strategy:
Most dashboard tools:
Browser requests page → server renders dynamically each time → returns HTML
↑ Every load has latency
Homepage:
Build time: static HTML/CSS/JS generated → served directly as static files
Widget data: separate API routes fetch in real time
↑ Page shell loads instantly; widget data fills in asynchronously
The practical effect: the page is essentially instant. Widgets populate asynchronously afterward, so one slow service doesn't block the whole page from loading.
Docker Socket Security Considerations
Mounting /var/run/docker.sock enables container auto-discovery, but it grants full Docker daemon access — a security surface worth thinking about.
Common mitigations:
# Option 1: Read-only mount (Homepage only needs read access)
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# Option 2: Docker Socket Proxy (recommended for security-sensitive setups)
# Insert a proxy between Homepage and the socket, exposing only read-only APIs
services:
socket-proxy:
image: tecnativa/docker-socket-proxy
environment:
CONTAINERS: 1 # allow reading container info only
POST: 0 # block all write operations
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
homepage:
environment:
# connect to the proxy instead of the socket directly
HOMEPAGE_DOCKER_HOST: socket-proxy
HOMEPAGE_DOCKER_PORT: 2375
Option 2 is the right choice in security-conscious environments.
Comparison with Similar Tools
| Dimension | Homepage | Homarr | Heimdall | Dashdot |
|---|---|---|---|---|
| Configuration | YAML files | GUI drag-and-drop | GUI | YAML |
| Docker auto-discovery | ✅ Labels | ✅ | ❌ | ❌ |
| Service widget count | 150+ | 50+ | Few | Few |
| API proxy (key security) | ✅ Backend proxy | Partial | ❌ | ❌ |
| Static generation | ✅ | ❌ | ❌ | ❌ |
| Version-controllable config | ✅ Natural | ❌ DB-stored | ❌ DB-stored | ✅ |
| Stars | 32.6k | 7k+ | 6k+ | 3k+ |
Homepage's edge: the most widgets + YAML configs that belong in version control + backend proxy for key protection. The trade-off is a slightly higher floor for getting started compared to drag-and-drop tools.
Project Links and Resources
Official Resources
- 🌟 GitHub: https://github.com/gethomepage/homepage
- 📚 Docs: https://gethomepage.dev
- 🐳 Docker image:
ghcr.io/gethomepage/homepage:latest - 💬 Discord: Homepage Discord community
- 🐛 Issues: GitHub Issues
Related Resources
- awesome-selfhosted — The definitive self-hosted software list, for finding more services to put in your dashboard
- Uptime Kuma — A self-hosted monitoring tool that integrates exceptionally well with Homepage
- tecnativa/docker-socket-proxy — Recommended Docker socket security proxy to pair with Homepage
Summary
Key Takeaways
- YAML as the center of gravity: services, widgets, bookmarks, settings — four files, config as documentation, Git version control out of the box
-
Docker Label auto-discovery: new container +
homepage.*labels = it appears in the dashboard, no manual maintenance needed - 150+ Service Widgets: from the *arr stack to AdGuard Home, API data surfaces directly on each card
- Backend proxy protects secrets: API keys never reach the browser; environment variable injection lets config be committed safely
- Static generation architecture: page shell loads instantly, widget data fills asynchronously — a slow service doesn't drag down the rest
Who This Is For
- Home server / NAS enthusiasts: running a pile of self-hosted services and wanting one unified front door
- Config-as-code people: prefer precise YAML control over drag-and-drop, and want configs in version control
- Security-minded self-hosters: not willing to expose API keys to the browser
- **arr media stack users*: Sonarr + Radarr + Jellyfin all-in — Homepage has the most comprehensive widget coverage for this setup
One-Line Verdict
Homepage does one small, sharp thing: it takes your services scattered across a dozen ports and unifies them with one YAML file and one elegant page.
Check out PrimeSkills — a curated marketplace of AI agents and skills that have been validated in real-world, enterprise-grade workflows. No fluff, just what actually works.
Find more useful knowledge and interesting products on my Homepage
Top comments (0)