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.
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
.envfiles, 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.
Related documentation
Homepage sits on top of the same core stack as the rest of the homelab:
- Deploy Traefik as a Docker reverse proxy with Cloudflare DNS
- Deploy Authentik SSO with Docker and Traefik
- Deploy Cloudflare Companion for automatic Traefik DNS records
- Deploy ComfyUI AI workflows with Docker, Traefik, and Authentik
- Deploy OpenClaw Gateway with Docker, Traefik, and Authentik
- Deploy Pi-hole private DNS with Docker and Headscale
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:
- Traefik labels on the service.
- Authentik proxy provider/application attached to the outpost.
- Authentik callback route for
/outpost.goauthentik.io/.