Post

Deploy Homepage as a Docker dashboard for homelab web UIs

Build a self-hosted Homepage dashboard that advertises only real homelab web UIs, runs from one project folder, and is protected by Traefik and Authentik.

Deploy Homepage as a Docker dashboard for homelab web UIs

Homepage is a clean self-hosted dashboard for collecting service links, status checks, bookmarks, widgets, and Docker-aware cards in one place.

In a homelab, it becomes the front page for every web UI you actually use: reverse proxy dashboard, SSO portal, DNS admin, documentation, AI tools, storage UI, bookmark manager, password vault, and public sites.

This guide deploys gethomepage/homepage with Docker Compose, routes it through Traefik, protects it with Authentik, and keeps everything related to the service under one folder.

1
2
3
4
5
6
7
8
9
Browser
  ↓ HTTPS
homepage.example.com
  ↓
Traefik
  ↓ forward-auth
Authentik Embedded Outpost
  ↓ after login
Homepage container on port 3000

Use placeholder domains and secrets in public documentation. Do not publish real public server IPs, API keys, private .env files, or internal-only credentials.


What Homepage does

Homepage solves a simple but important problem: once a homelab has many services, remembering every URL becomes annoying.

Instead of keeping links in notes, browser bookmarks, or memory, Homepage gives you one protected start page with grouped cards.

Good cards are usually real web UIs, for example:

  • Traefik dashboard;
  • Authentik admin portal;
  • Portainer;
  • Pi-hole admin;
  • Headscale UI;
  • documentation site;
  • forum;
  • ComfyUI;
  • OpenClaw Gateway;
  • Garage S3 Web UI;
  • Vaultwarden;
  • Karakeep;
  • PocketBase admin.

Avoid advertising raw API endpoints that have no useful browser UI. They tend to look broken, have missing icons, or open to confusing responses.


Folder layout

Create a dedicated folder:

1
2
3
4
5
6
7
8
9
10
11
12
/home/ubuntu/homepage/
├── docker-compose.yml
├── .env
├── README.md
├── config/
│   ├── settings.yaml
│   ├── services.yaml
│   ├── bookmarks.yaml
│   ├── widgets.yaml
│   ├── docker.yaml
│   └── kubernetes.yaml
└── images/

Create it:

1
2
mkdir -p /home/ubuntu/homepage/config /home/ubuntu/homepage/images
cd /home/ubuntu/homepage

Keeping all files here makes the service easy to back up, inspect, move, or rebuild.


Environment file

Create /home/ubuntu/homepage/.env:

HOMEPAGE_HOST=homepage.example.com
HOMEPAGE_ALLOWED_HOSTS=homepage.example.com,localhost,127.0.0.1

HOMEPAGE_ALLOWED_HOSTS is important because modern Homepage versions validate the incoming host header. If this is missing or wrong, the app may reject requests through your reverse proxy.


Dashboard settings

Create /home/ubuntu/homepage/config/settings.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
title: NOX Homelab
description: One dashboard for web services, admin panels, AI tools, and public sites.
theme: dark
color: slate
language: en
headerStyle: clean
statusStyle: dot
target: _blank
hideVersion: false
fiveColumns: true
layout:
  Core Infrastructure:
    style: row
    columns: 4
  Web Services:
    style: row
    columns: 4
  AI & Automation:
    style: row
    columns: 4
  Content & Apps:
    style: row
    columns: 4
  Storage & Secrets:
    style: row
    columns: 4
  Public Sites:
    style: row
    columns: 4

This gives the dashboard a dark theme and groups services by purpose.


Service cards

Create /home/ubuntu/homepage/config/services.yaml.

This example intentionally lists only services with useful web UIs or public pages:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
- Core Infrastructure:
    - Traefik:
        icon: traefik.png
        href: https://traefik.example.com
        description: Reverse proxy, TLS, Docker routing
        siteMonitor: https://traefik.example.com
    - Authentik:
        icon: authentik.png
        href: https://auth.example.com
        description: SSO, proxy providers, embedded outpost
        siteMonitor: https://auth.example.com
    - Portainer:
        icon: portainer.png
        href: https://portainer.example.com
        description: Docker management UI
        siteMonitor: https://portainer.example.com
    - Pi-hole:
        icon: pi-hole.png
        href: https://pihole.example.com/admin/
        description: Private DNS, blocking, SafeSearch
        siteMonitor: https://pihole.example.com/admin/
    - Headscale UI:
        icon: headscale.png
        href: https://headscale-ui.example.com
        description: Tailnet control-plane UI
        siteMonitor: https://headscale-ui.example.com

- Web Services:
    - WordPress:
        icon: wordpress.png
        href: https://example.com
        description: Main website
        siteMonitor: https://example.com
    - WordPress Admin:
        icon: wordpress.png
        href: https://example.com/wp-admin/
        description: WordPress dashboard
        siteMonitor: https://example.com/wp-login.php
    - Discourse:
        icon: discourse.png
        href: https://forum.example.com
        description: Community/forum
        siteMonitor: https://forum.example.com
    - Documentation:
        icon: jekyll.png
        href: https://docs.example.com
        description: Homelab documentation website
        siteMonitor: https://docs.example.com

- AI & Automation:
    - OpenClaw Gateway:
        icon: openai.png
        href: https://agent.example.com
        description: AI agent gateway and chat surface
        siteMonitor: https://agent.example.com
    - ComfyUI:
        icon: comfyui.png
        href: https://comfyui.example.com
        description: Node-based AI workflow UI
        siteMonitor: https://comfyui.example.com

- Content & Apps:
    - Karakeep:
        icon: karakeep.png
        href: https://bookmarks.example.com
        description: Bookmark and read-it-later app
        siteMonitor: https://bookmarks.example.com
    - PocketBase:
        icon: pocketbase.png
        href: https://pocket.example.com/_/
        description: Backend admin UI
        siteMonitor: https://pocket.example.com/_/

- Storage & Secrets:
    - Garage S3 Web UI:
        icon: garage.png
        href: https://s3.example.com
        description: S3-compatible object storage UI
        siteMonitor: https://s3.example.com
    - Vaultwarden:
        icon: vaultwarden.png
        href: https://vault.example.com
        description: Password manager vault
        siteMonitor: https://vault.example.com

Why skip API-only services?

  • They usually do not have a friendly UI.
  • Their icons may be missing or misleading.
  • They can create broken-looking cards.
  • They are better documented near the service that consumes them.

For example, an S3 API endpoint should be documented in the object storage post, while the Garage Web UI belongs on the dashboard.


Bookmarks

Create /home/ubuntu/homepage/config/bookmarks.yaml:

1
2
3
4
5
6
7
8
9
10
- Repositories:
    - Homepage GitHub:
        - icon: github.png
          href: https://github.com/gethomepage/homepage
    - ComfyUI GitHub:
        - icon: github.png
          href: https://github.com/comfy-org/ComfyUI
    - OpenClaw Docs:
        - icon: github.png
          href: https://docs.openclaw.ai

Bookmarks are useful for upstream docs and repositories, not just local services.


Widgets

Create /home/ubuntu/homepage/config/widgets.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- search:
    provider: duckduckgo
    target: _blank
- datetime:
    text_size: xl
    format:
      dateStyle: long
      timeStyle: short
      hourCycle: h23
- openmeteo:
    label: Server Weather
    latitude: 36.75
    longitude: 3.06
    timezone: Africa/Algiers
    units: metric

Keep widgets simple at first. Add API-backed widgets later only when you are ready to manage their credentials safely.


Docker integration

Homepage can read Docker metadata if you mount the Docker socket.

Create /home/ubuntu/homepage/config/docker.yaml:

1
2
local:
  socket: /var/run/docker.sock

This is convenient, but it is sensitive: Docker socket access is powerful. Protect Homepage with Authentik and avoid exposing it publicly without SSO.

Disable Kubernetes if you are not using it:

1
mode: disabled

Save that as /home/ubuntu/homepage/config/kubernetes.yaml.


Docker Compose file

Create /home/ubuntu/homepage/docker-compose.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    restart: unless-stopped
    env_file: .env
    environment:
      HOMEPAGE_ALLOWED_HOSTS: ${HOMEPAGE_ALLOWED_HOSTS}
    volumes:
      - ./config:/app/config
      - ./images:/app/public/images
      - /var/run/docker.sock:/var/run/docker.sock:ro
    ports:
      - "127.0.0.1:3001:3000"
    networks:
      - proxy
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.routers.homepage.entrypoints=http"
      - "traefik.http.routers.homepage.rule=Host(`${HOMEPAGE_HOST}`)"
      - "traefik.http.middlewares.homepage-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.homepage.middlewares=homepage-https-redirect"
      - "traefik.http.routers.homepage-secure.entrypoints=https"
      - "traefik.http.routers.homepage-secure.rule=Host(`${HOMEPAGE_HOST}`)"
      - "traefik.http.routers.homepage-secure.tls=true"
      - "traefik.http.routers.homepage-secure.tls.certresolver=cloudflare"
      - "traefik.http.routers.homepage-secure.middlewares=authentik@docker"
      - "traefik.http.routers.homepage-secure.service=homepage"
      - "traefik.http.services.homepage.loadbalancer.server.port=3000"
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://127.0.0.1:3000/ || exit 1"]
      interval: 60s
      timeout: 10s
      retries: 5
      start_period: 60s

networks:
  proxy:
    external: true

The host port is bound to 127.0.0.1 only. Public traffic should enter through Traefik, not directly through the app port.


Start Homepage

Run:

1
2
cd /home/ubuntu/homepage
docker compose up -d

Check status:

1
2
docker compose ps
docker compose logs -f homepage

Expected log shape:

1
2
Next.js ...
Ready

Test the app locally:

1
curl -I http://127.0.0.1:3001/

Expected result:

1
HTTP/1.1 200 OK

Add Authentik protection

Create an Authentik proxy provider and application:

1
2
3
4
5
6
7
Application name: Homepage Dashboard
Slug: homepage-dashboard
Launch URL: https://homepage.example.com/
Provider type: Proxy Provider
External host: https://homepage.example.com
Mode: Forward auth, single application
Cookie domain: example.com

Attach the provider to the authentik Embedded Outpost.

If you forget this step, Traefik may call Authentik correctly, but Authentik will not know which app belongs to the hostname.


Add the Authentik callback route

Add an outpost route to the Authentik server labels:

1
2
3
4
5
6
7
labels:
  - "traefik.http.routers.authentik-homepage-outpost.entrypoints=https"
  - "traefik.http.routers.authentik-homepage-outpost.rule=Host(`homepage.example.com`) && PathPrefix(`/outpost.goauthentik.io/`)"
  - "traefik.http.routers.authentik-homepage-outpost.priority=100"
  - "traefik.http.routers.authentik-homepage-outpost.tls=true"
  - "traefik.http.routers.authentik-homepage-outpost.tls.certresolver=cloudflare"
  - "traefik.http.routers.authentik-homepage-outpost.service=authentik"

Recreate Authentik after changing labels:

1
2
cd /home/ubuntu/authentik
docker compose up -d server worker

DNS automation

If you use a Cloudflare companion container that reads Traefik labels, restart it after adding a new hostname:

1
docker restart traefik-cloudflare-companion

Check its logs for a message like:

1
Created new record: homepage.example.com to point to example.com

Verification checklist

Check the Homepage container:

1
2
cd /home/ubuntu/homepage
docker compose ps

Check local app response:

1
curl -I http://127.0.0.1:3001/

Check Authentik outpost callback routing:

1
2
curl -k -I --resolve homepage.example.com:443:127.0.0.1 \
  https://homepage.example.com/outpost.goauthentik.io/ping

Expected result:

1
HTTP/2 204

Check unauthenticated public route:

1
2
curl -k -I --resolve homepage.example.com:443:127.0.0.1 \
  https://homepage.example.com/

Expected result:

1
2
HTTP/2 302
location: https://auth.example.com/application/o/authorize/...

That proves Homepage is not exposed directly and Authentik is handling login.


Troubleshooting

Root URL returns 404 from Authentik

Wait a few seconds and test again. The embedded outpost may need time to reload the new provider mapping.

If it stays broken, check:

  • the Authentik proxy provider exists;
  • the provider external host exactly matches https://homepage.example.com;
  • the provider is attached to the embedded outpost;
  • the callback route exists for /outpost.goauthentik.io/;
  • the Homepage secure router uses authentik@docker.

Homepage rejects the host

Check .env:

HOMEPAGE_ALLOWED_HOSTS=homepage.example.com,localhost,127.0.0.1

Then restart:

1
docker compose up -d

Icons look broken

Remove API-only services and cards for endpoints that are not real web UIs.

A dashboard should advertise usable interfaces, not every backend endpoint.

Good:

1
Garage S3 Web UI

Usually not useful as a dashboard card:

1
Garage S3 API

Docker socket warning

Mounting /var/run/docker.sock gives the container visibility into Docker. Keep the service behind SSO and avoid giving dashboard access to untrusted users.


Homepage sits on top of the same core stack as the rest of the homelab:

The pattern is consistent: one project folder, one Compose file, Traefik labels for routing, Authentik for sensitive UIs, and public docs without secrets.


Operational commands

Common commands:

1
2
3
4
5
6
cd /home/ubuntu/homepage

docker compose ps
docker compose logs -f homepage
docker compose up -d
docker compose pull && docker compose up -d

Edit services:

1
2
nano /home/ubuntu/homepage/config/services.yaml
docker compose restart homepage

If you add a new protected hostname, remember the three pieces:

  1. Traefik labels on the service.
  2. Authentik proxy provider/application attached to the outpost.
  3. Authentik callback route for /outpost.goauthentik.io/.
This post is licensed under CC BY 4.0 by the author.