How to Use Homepage: The Beginner's Guide (First Compose, the Config-Files Rule, Labels vs YAML, and the Allowed-Hosts Fix)

CodexFuse Team · 2026-10-31T10:49:45.474Z · 15 min read

Learn how to use Homepage: Docker Compose install, the five config files, services.yaml vs Docker labels, widgets, and the HOMEPAGE_ALLOWED_HOSTS fix.

How to Use Homepage: The Beginner's Guide (First Compose, the Config-Files Rule, Labels vs YAML, and the Allowed-Hosts Fix)

Learning how to use Homepage is the fastest way to stop juggling browser bookmarks, IP addresses, and port numbers for every service you self-host. Homepage is a free, open-source application dashboard (published by the gethomepage project) that puts all of your self-hosted services on one fast start page: clickable tiles for each app, live Docker container status, and widgets that show system stats, weather, and service health at a glance. This Homepage dashboard tutorial runs the whole beginner arc in one sitting: a Docker Compose install, the config-files rule that stops the dreaded blank first boot, your first service cards written by hand in services.yaml, the same services auto-discovered through Docker labels, info and service widgets, and the HOMEPAGE_ALLOWED_HOSTS fix that trips up nearly every beginner. Homepage runs on top of Docker, so start with our Docker beginner guide if containers are new to you — and if you want an entire home-server OS around your dashboard, the Cosmos beginner guide shows the platform Homepage often runs on.

What is the fastest way to start using Homepage?

The fastest beginner path is: create a config folder, write a small Docker Compose file with a pinned Homepage image tag and HOMEPAGE_ALLOWED_HOSTS set to your hostname, create the five config files Homepage will not create for you, bring the stack up, then open the dashboard at http://your-server-ip:3000. After that you add service cards in services.yaml (or let Docker labels add them for you), drop a weather or system-stats widget into widgets.yaml, and set up the v2 password login before you expose the page anywhere beyond your home network.

What is Homepage (the self-hosted dashboard)?

Homepage is an open-source (GPL-3.0), self-hosted start page for your server: one web page that links all your self-hosted apps, shows which Docker containers are running right now, and displays live information from those apps through widgets. It has no database — everything lives in flat YAML files in a config folder, which makes the whole dashboard version-controllable with Git and trivially backed up. The project (v2.7.0 at the time of writing, with over 33,000 GitHub stars) ships more than 100 service integrations — media apps like Plex and Jellyfin, downloaders like Sonarr and Radarr, monitoring tools like Uptime Kuma — plus info widgets for CPU, RAM, disk, weather, and search. All API requests to those services are routed through Homepage's own server-side proxy, so your API keys stay hidden from the browser. Its documentation lives at gethomepage.dev, and its canonical image is ghcr.io/gethomepage/homepage.

How do you install Homepage with Docker Compose?

The install is a short compose file — but the directory layout comes first, because Homepage refuses to create your config folder for you. Decide on a home for the project (this guide uses /opt/homepage), create the config directory inside it, and then write the compose file next to it.

mkdir -p /opt/homepage/config
cd /opt/homepage

The compose file pins the image tag (never use :latest for a dashboard — an unattended pull can change behavior under you), publishes port 3000, mounts the config folder, and sets the one environment variable Homepage demands for non-localhost access. If you do not have a server yet, a what is VPS hosting primer covers the cheap machines most beginners run Homepage on.

services:
  homepage:
    image: ghcr.io/gethomepage/homepage:v2.7.0
    container_name: homepage
    restart: unless-stopped
    ports:
      - 3000:3000
    volumes:
      - /opt/homepage/config:/app/config
    environment:
      HOMEPAGE_ALLOWED_HOSTS: 192.168.1.50
      PUID: 1000
      PGID: 1000

Set HOMEPAGE_ALLOWED_HOSTS to the hostname or IP you will actually type in the browser (more on the exact rules and the 400 error below). PUID and PGID let the container run as a non-root user — match them to your own user id and group id, and make sure the config folder's ownership matches, or Homepage cannot read the files you are about to create. Bring it up:

docker compose up -d

One habit to lock in now: environment variables are read when the container is created, not when it restarts. If you change an environment variable later, recreate the container (docker compose up -d recreates it when the compose file changed; a bare docker restart keeps the old values). Open http://192.168.1.50:3000 (your address, of course) and you will see Homepage — but it will be blank, because you have not given it anything to show yet. That is the next rule.

Why is Homepage blank on first boot?

A blank Homepage page is not a bug — it is the config-files rule: Homepage ships without creating any of its YAML config files, and it silently renders an empty dashboard until you create them. The five files that make up a Homepage setup are settings.yaml (app-level options: title, theme, layout), services.yaml (your service cards), bookmarks.yaml (plain links), widgets.yaml (header widgets like weather and system stats), and docker.yaml (Docker connections, needed for container status and label discovery). The container only reads them from /app/config, so they must live in the folder you mounted — create each as an empty file now so you never chase a blank page again:

cd /opt/homepage/config
touch settings.yaml services.yaml bookmarks.yaml widgets.yaml docker.yaml

Then click the refresh icon in the bottom-right corner of the dashboard — that clears the cached dashboard data and reloads the page. This one detail (Homepage caches the dashboard data client-side) is worth remembering for every YAML edit you make from here on: if a change does not show up, hit the refresh icon before assuming the YAML is wrong. Local images and icons also require a container restart to be discovered, and never bind-mount over the whole /app/public/ directory to add them — mount an images folder separately.

How do you add your first service cards with services.yaml?

The hand-written route is services.yaml: a list of groups, each holding named services with an icon, an href, a description, and — once Docker is wired up — the Docker server and container to pull live status from. Start with two plain groups so you see the shape of the file before anything fancy:

- Media:
    - Plex:
        icon: plex.png
        href: http://192.168.1.50:32400
        description: Movies and TV
    - Jellyfin:
        icon: jellyfin.png
        href: http://192.168.1.50:8096
        description: Second media server
- Infrastructure:
    - Uptime Kuma:
        icon: uptime-kuma.png
        href: http://192.168.1.50:3001
        description: Service monitoring
        server: my-docker
        container: uptime-kuma
        widget:
          type: uptimekuma
          url: http://192.168.1.50:3001
          slug: paste-your-uptime-kuma-slug

The server and container keys attach a service to a Docker instance (configured in docker.yaml, next section) so the card shows live running status and container stats. Icons are built-in: Homepage ships a large icon pack, so plex.png resolves without you downloading anything. One naming rule that burns beginners: a service group and a bookmark group with the same name will misbehave (a bookmark group can be hidden) — keep service group names and bookmark group names distinct. Service cards like these are the core of the whole "homepage docker" beginner arc: write the YAML, hit the refresh icon, and the tile appears.

How do Docker labels auto-add services to Homepage?

Docker labels let your containers announce themselves to Homepage, so new services appear on the dashboard without you editing services.yaml at all. This needs a Docker connection in docker.yaml first. You have two wiring choices: point Homepage at a socket proxy (recommended) or mount the Docker socket directly (requires Homepage to run as root, and a raw socket inside a web-facing container is a privilege-escalation risk you should avoid).

The recommended setup uses the tecnativa/docker-socket-proxy container, which exposes the Docker API read-only on a local port:

services:
  dockerproxy:
    image: ghcr.io/tecnativa/docker-socket-proxy:latest
    container_name: dockerproxy
    restart: unless-stopped
    environment:
      - CONTAINERS=1   # allow reading container info
      - POST=0         # disallow any POST — effectively read-only
    ports:
      - 127.0.0.1:2375:2375
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  homepage:
    image: ghcr.io/gethomepage/homepage:v2.7.0
    container_name: homepage
    restart: unless-stopped
    ports:
      - 3000:3000
    volumes:
      - /opt/homepage/config:/app/config
    environment:
      HOMEPAGE_ALLOWED_HOSTS: 192.168.1.50
      PUID: 1000
      PGID: 1000

Then tell Homepage about the proxy in config/docker.yaml:

my-docker:
  host: dockerproxy
  port: 2375

With the connection in place, any container carrying the right labels appears on the dashboard automatically — and the server and container values are inferred, so you do not repeat them:

services:
  emby:
    image: lscr.io/linuxserver/emby:latest
    container_name: emby
    restart: unless-stopped
    labels:
      - homepage.group=Media
      - homepage.name=Emby
      - homepage.icon=emby.png
      - homepage.href=http://192.168.1.50:8096
      - homepage.description=Media server
      - homepage.widget.type=emby
      - homepage.widget.url=http://192.168.1.50:8096
      - homepage.widget.key=yourembyapikeyhere

Every label starts with homepage. and uses dot notation: homepage.group, homepage.name, homepage.icon, homepage.href, homepage.description, plus homepage.widget.type/url/key for service widgets. Two gotchas: if you write labels in mapping syntax instead of list syntax, array values like homepage.widget.fields must be wrapped in single quotes so Docker passes them as one string; and on Docker Swarm, labels must go under deploy/labels (not container labels), with Homepage running on a manager node and swarm: true in docker.yaml. You can even hide widget keys by putting secrets in environment variables starting with HOMEPAGE_VAR_ and referencing them as {{HOMEPAGE_VAR_EMBY_KEY}} inside your config files.

Should you use services.yaml or Docker labels?

Use services.yaml when a service is not in Docker (a bare-metal app, a router, a hosted bookmark) or when you want the card to look exactly the way you wrote it; use Docker labels when the service runs in Docker and you want it discovered, started, and widget-wired without editing a second file. Most real dashboards end up mixed: labels for the containers, services.yaml for everything else. Here is the decision at a glance:

Factorservices.yamlDocker labels
Non-Docker servicesWorks for anything with a URLOnly works for containers
New service appears on dashboardAfter you edit the YAMLAutomatically on container start
Live container status + statsNeeds server + container keysInferred automatically
Ordering and groupingFull control via layout + weightDefault weight 0, or add weight
Version-controllableYes — one fileYes — lives in your compose files
Secrets (API keys){{HOMEPAGE_VAR_*}} referencesSame, via dot-notation labels

How do you add widgets to Homepage?

Widgets come in two kinds: info widgets live in the header (system stats, weather, search) and are configured in widgets.yaml; service widgets attach to a service card and show live data from that service, configured inside services.yaml or as labels. Homepage supports over 100 service integrations — media servers, downloaders, monitoring tools — and every widget request is proxied server-side, so keys never reach the browser.

A classic first info widget is the weather, using Open-Meteo (no API key needed):

- openmeteo:
    label: Current
    latitude: 33.68
    longitude: 73.04
    cache: 5

Add a resources widget next to it for CPU, RAM, and disk, and a search widget as your dashboard's quick-launcher — those two plus weather cover what most beginners want in the header. A service widget, like the Uptime Kuma block in the services.yaml example above, drops live monitor data onto the service's card itself. If your widgets keep showing errors, the usual cause is the widget URL: Homepage calls it from inside the container, so localhost means "the Homepage container," not your laptop — use the container's service name or a LAN IP. Our Uptime Kuma beginner guide pairs naturally here, since its status page is the most common first service widget on a fresh Homepage.

What is HOMEPAGE_ALLOWED_HOSTS and how do you fix the 400 error?

HOMEPAGE_ALLOWED_HOSTS is the mandatory allow-list of hostnames Homepage will answer for. Since v1.0, Homepage refuses any request that arrives under an unknown Host header — a guard against DNS-rebinding attacks on its API proxy. Without the variable set correctly, opening the dashboard gives you HTTP 400 and {"error":"Host validation failed. See logs for more details."} instead of your start page. This is the single most common Homepage beginner failure, and the fix has three parts.

First, the value must match exactly what the browser sends, as reported in the Homepage container logs — including the port if one is in the URL. Second, it is a comma-separated list with no spaces. Third, localhost:3000 and 127.0.0.1:3000 are always allowed, but everything else must be listed:

HOMEPAGE_ALLOWED_HOSTS=192.168.1.50,start.mydomain.com

If you are behind a reverse proxy with HTTPS (the right way to expose Homepage — see the security section), the host is your domain, and the container still answers on port 3000 internally. When the error persists after an edit, check the logs for the exact host string, update the variable, and recreate the container (docker compose up -d) — remember, a restart keeps the old environment. Setting the variable to * disables the check, but the official docs explicitly recommend against it; the host check is a best-effort guard for local setups and is no substitute for a reverse proxy with authentication and TLS on public deployments.

How do you secure Homepage before exposing it?

A dashboard that can read your Docker socket and holds your API keys must never sit open on the internet. Since v2, Homepage ships a built-in login gate — enable it before your first public exposure. The required variables are a master switch, a long random secret, a password, and the external URL you access the dashboard from:

HOMEPAGE_AUTH_ENABLED=true
HOMEPAGE_AUTH_SECRET=$(openssl rand -base64 32)
HOMEPAGE_AUTH_PASSWORD=a-long-unique-password-you-use-nowhere-else
HOMEPAGE_EXTERNAL_URL=https://start.mydomain.com

Keep these in a .env file next to your compose file (chmod 600) and reference them from compose — never commit the secret or the password to Git. If you run an identity provider, Homepage also supports OIDC login (HOMEPAGE_OIDC_ISSUER, HOMEPAGE_OIDC_CLIENT_ID, HOMEPAGE_OIDC_CLIENT_SECRET), which overrides the password login. One honesty note the official docs carry: Homepage does not rate-limit password attempts itself, so public deployments should rate-limit POST requests to /api/auth/callback/credentials at the reverse proxy, and the logged <nextauth> Failed password sign-in attempt lines work as a fail2ban or CrowdSec filter.

Even with the login gate on, the sane production shape is: Homepage behind a reverse proxy with HTTPS, or behind a VPN, never a raw port on a public IP. Our Caddy beginner guide shows the reverse-proxy side with automatic HTTPS in minutes. And remember the socket lesson from earlier: if Homepage reads Docker through a read-only socket proxy (POST=0), a compromised dashboard can only look; a raw /var/run/docker.sock mount is effectively root on your host.

How do you customize the layout in settings.yaml?

settings.yaml is where Homepage stops being a plain list and starts being your start page. The layout block controls how groups arrange: column style is the default, row style lets you set columns per group, and groups sort in the order you list them in the layout block.

title: My Start Page
theme: dark
color: slate
layout:
  Media:
    style: row
    columns: 4
  Infrastructure:
    style: row
    columns: 3
  Tools:
    tab: Everyday
    iconsOnly: false

Add tab: Name to at least one group and the dashboard gains tabs (every tab is directly linkable as #tabname in the URL); set header: false on a group to hide its header; fullWidth: true stretches the page edge to edge on wide monitors. Other quick wins: cardStyle: hairline swaps the default drop shadows for thin hover-brightened borders, blockStyle: refined makes widget values heavier and easier to read, and font: plexData lines up all the numbers in columns while keeping names in the default Manrope font. Icons can also ride on the group headers themselves with an icon: key under the layout group.

Homepage vs Homarr: which dashboard should you choose?

Homepage and Homarr (plus Dashy, Glance, Heimdall, and Homer) are the names that come up in every dashboard thread, but they are different tools for different temperaments. Homepage is YAML-first: you write config files or Docker labels, and the whole dashboard is diffable, version-controlled, and fast, with 100+ service integrations and no database. Homarr takes the opposite approach — a drag-and-drop web UI where you arrange boards visually. Here is the honest comparison:

FactorHomepageHomarr
Configuration styleYAML files + Docker labelsDrag-and-drop web UI
Best forBeginners who like Git and text config; big Docker stacksUsers who want a visual editor and zero YAML
Auto-discoveryDocker labels, server + container inferredManual board editing
Service integrations100+ service widgetsRich integrations, app-focused
DatabaseNone — flat filesSQLite-backed
BackupCopy the config folder (or git push)Export / volume backup

Pick Homepage if you self-host a growing Docker stack and want your dashboard to update itself when containers appear. Pick Homarr if you want to drag cards around in a browser and never touch YAML. Dashy and Glance sit nearby in the lane (Dashy's strength is status checks and themes; Glance's is dynamic feed widgets), but they are alternatives to evaluate later, not parts of this guide.

What mistakes do Homepage beginners always make?

Every support thread about Homepage repeats the same short list. Skim it before you debug anything:

MistakeWhy it hurtsThe fix
Using :latest as the image tagAn unattended pull changes your dashboard's behavior overnightPin a version tag like v2.7.0 and upgrade on your schedule
Forgetting the config filesHomepage renders a blank page with no errorCreate settings.yaml, services.yaml, bookmarks.yaml, widgets.yaml, docker.yaml before first boot
Restarting instead of recreating after an env changeOld values stay live; the 400 error "won't fix"docker compose up -d recreates; a bare restart does not
Setting HOMEPAGE_ALLOWED_HOSTS=*Disables the host-validation guard on purposeList your real hosts, comma-separated, no spaces
Exposing port 3000 on a public IPYour dashboard, Docker status, and API keys sit on the open internetEnable the v2 auth gate and put Caddy or a VPN in front
Mounting the raw Docker socketA compromised dashboard becomes root on your hostUse the read-only socket-proxy pattern (CONTAINERS=1, POST=0)
Same name for a service group and a bookmark groupThe bookmark group can be hidden without warningKeep group names unique across both files
Editing YAML and seeing nothing changeHomepage caches dashboard data client-sideClick the refresh icon in the bottom-right corner

Sources

Every fact in this guide was verified against these sources on 2026-10-31. The gethomepage.dev official documentation is the authority for installation, the Docker wiring, labels, widgets, settings, and authentication; the GitHub releases API is the authority for the current version and star count.

  • gethomepage.dev — Installation (official docs, crawled 2026-10-31 10:44 UTC): HOMEPAGE_ALLOWED_HOSTS required since v1.0 for non-localhost access; comma-separated no spaces; localhost:3000 and 127.0.0.1:3000 always included; exact host from logs; * disables the check (not recommended); NextAuth routes exempt; v2 auth gate env vars HOMEPAGE_AUTH_ENABLED / HOMEPAGE_AUTH_SECRET (min 32 chars, openssl rand -base64 32) / HOMEPAGE_EXTERNAL_URL / HOMEPAGE_AUTH_PASSWORD; OIDC vars HOMEPAGE_OIDC_ISSUER / HOMEPAGE_OIDC_CLIENT_ID / HOMEPAGE_OIDC_CLIENT_SECRET / HOMEPAGE_OIDC_NAME / HOMEPAGE_OIDC_SCOPE (default "openid email profile") / HOMEPAGE_OIDC_AUTO_LOGIN; no app-level rate limiting — rate-limit POST to /api/auth/callback/credentials; failed attempts logged as <nextauth> Failed password sign-in attempt for fail2ban/CrowdSec filters.
  • gethomepage.dev — Docker Installation (official docs, crawled 2026-10-31 10:45 UTC): image ghcr.io/gethomepage/homepage:latest, config mount /app/config (directory must exist), optional docker.sock mount; PUID/PGID non-root (volumes need matching ownership); direct socket use not recommended, requires root or docker group; HOMEPAGE_VAR_/HOMEPAGE_FILE_ secret substitution ({{HOMEPAGE_VAR_XXX}}).
  • gethomepage.dev — Docker / docker.yaml (official docs, crawled 2026-10-31 10:45 UTC): IP:PORT and socket connections; tecnativa/docker-socket-proxy pattern with CONTAINERS=1 and POST=0 (read-only); proxy on 127.0.0.1:2375; socket-direct needs homepage running as root; automatic service discovery via homepage.* labels (group/name/icon/href/description; server and container auto-inferred); widget labels homepage.widget.type/url/key; multiple widgets via homepage.widgets[0..]; single quotes needed for array values in mapping-syntax labels; Docker Swarm needs deploy/labels, manager node, swarm: true; discovery weight rules (discovered default 0, configured (index+1)*100).
  • gethomepage.dev — Widgets (official docs, crawled 2026-10-31 10:45 UTC): info widgets (widgets.yaml, e.g. openmeteo with latitude/longitude/cache) vs service widgets (services.yaml, e.g. tautulli, uptimekuma); 100+ service integrations; API requests proxied server-side, keys hidden from the frontend.
  • gethomepage.dev — Settings (official docs, crawled 2026-10-31 10:45 UTC): layout styles (column default, row + columns), tabs via tab: and #group URLs, iconsOnly bookmarks, header:false, fullWidth, cardStyle hairline, blockStyle refined, font plex/plexData, title/theme/color, background images (restart needed for new local images; never bind-mount whole /app/public/), refresh icon clears cached dashboard data, same-name service+bookmark group warning.
  • GitHub API — gethomepage/homepage (queried 2026-10-31): latest release v2.7.0 published 2026-10-23; ~33,461 stars; GPL-3.0; description "A highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations."
  • dev.to — "Homepage: The Dashboard for All Your Self-Hosted Services" by serverkueche (29 days old, crawled 2026-10-31): v2.2.0 Traefik arc; YAML config folder layout; HOMEPAGE_ALLOWED_HOSTS mandatory with the exact 400 host-validation-failed error; v2 password login via .env + HOMEPAGE_AUTH_SECRET/HOMEPAGE_AUTH_PASSWORD; Docker socket read for live container status.
  • dev.to — "Homepage Dashboard: Docker Setup, Config, Widgets, and Labels" by deploylark (9 days old, crawled 2026-10-31): v2.6.0 arc; docker-socket-proxy over raw socket mount; ALLOWED_HOSTS comma-separated; recreate-not-restart on env change; ~33,200 stars as of Oct 20, 2026.
  • dev.to — "Building a Local Dashboard with Homepage" by ericwoooo_kr (115 days old, crawled 2026-10-31): no-database flat-YAML design; the five config files Homepage will not create; pin the image tag.
  • selfhosting.sh — "Self-Host Homepage Dashboard with Docker Compose" (227 days old, crawled 2026-10-31): YAML version-controllable config; HOMEPAGE_VAR_ secret pattern; auth-proxy note (Authelia/Authentik) for pre-v2 deployments.

Frequently asked questions

Is Homepage free to use?

Yes. Homepage is free and open-source software under the GPL-3.0 license — no trial, no feature paywall, no account needed to run it on your own server. You pay only for whatever machine you run it on, which for most beginners is a cheap VPS or an old PC at home.

Do I need a domain name to use Homepage?

No. On your home network, an IP address works fine — just put that IP in HOMEPAGE_ALLOWED_HOSTS. A domain becomes worth it when you want HTTPS and outside access: then you point the domain at your server, add it to the allowed-hosts list (comma-separated, no spaces), and put a reverse proxy with TLS in front.

Can Homepage run without Docker?

Officially, Docker is the supported path — the project publishes Docker images and a Helm chart, plus from-source installation for non-Docker setups. The Docker-label auto-discovery and live container status obviously only work with Docker, but the hand-written services.yaml cards work for anything reachable over HTTP, Docker or not.

Why do my Docker labels not add services to the dashboard?

Check three things in order: docker.yaml has a working Docker connection (a socket proxy or socket), the labels use list syntax with the homepage. prefix, and the container was recreated after you added them (labels are read at container creation). On Docker Swarm, labels must sit under deploy/labels with Homepage on a manager node — container-level labels are invisible to discovery.

Homepage vs Homarr — which one is better for a beginner?

They optimize for different beginners. Homepage is YAML-first: fast, version-controllable, and self-updating through Docker labels, but you edit text files. Homarr is drag-and-drop: you arrange boards in a web UI and never touch YAML, but adding each service is manual. If your stack lives in Docker Compose and grows monthly, Homepage saves you more time; if you want a visual editor and a handful of static tiles, Homarr is the gentler start.

Featured on Twelve Tools Launch Llama Tools Launch your MCP