This is a small Python project that builds a static, icon-based homelab diagram page from Docker discovery plus a YAML configuration file. It renders HTML and can export the same page to PNG and PDF.
This is for people who run Docker services and want a self-updating dashboard-style diagram without manually drawing every service. It is designed for Docker Compose stacks behind Traefik, although it could be modified to work with other setups.
- Discovers running Docker containers automatically.
- Allows for the automatic creation of public/private diagrams for hidden information (IPs, Domain Info, etc.)
- Lets you hide containers with
homelab.enabled=false. - Supports parent groups, child groups, subgroups, group descriptions, and manual infrastructure items.
- Reads Traefik
Host(...)andHostRegexp(...)labels to discover public routes. - Downloads only needed icons from the Homarr Dashboard Icons collection into
static/icons/. - Supports light/dark themes.
- Shows storage totals from mounted filesystems.
Public route discovery currently works from Traefik Docker labels. The script looks for labels like:
traefik.http.routers.example.rule: "Host(`example.yourdomain.com`)"This project can be modified to support other routing options, such as Caddy, Nginx Proxy Manager, Cloudflare Tunnel config files, Kubernetes ingress objects, or Docker labels from another proxy. The routing-specific logic is isolated mostly in parse_traefik_hosts() and infer_exposure() inside build_topology.py.
git clone https://github.com/WriteBlocked/docker-topology.git
cd docker-topologypython3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python -m playwright install chromium
sudo .venv/bin/playwright install-deps chromium
cp config.sample.yaml homelab.yaml
nano homelab.yamlPrivate light mode is the default:
python build_topology.pyPrivate dark mode:
python build_topology.py --private --theme darkPublic output:
python build_topology.py --public --theme lightTest HTML without exporting PNG/PDF:
python build_topology.py --skip-exportoutput/index.html
output/homelab-topology.png
output/homelab-topology.pdf
python3 -m http.server 8008Navigate to: http://SERVER-IP:8008/output/
Most containers do not need labels. Running containers are shown automatically.
Use labels in a docker compose file when you want to organize or override something:
labels:
homelab.group: "Media"
homelab.subgroup: "Indexers"
homelab.purpose: "Indexer manager"Hide a container completely:
labels:
homelab.enabled: "false"Hide a container only from public output:
labels:
homelab.hidden: "true"Override display name or icon:
labels:
homelab.name: "FreshRSS"
homelab.icon: "freshrss"Icons are sourced from Homarr's dashboard icons. You can find the name of a service and it's icon on this site. To ensure you have the right name, make sure you filter by the Homarr collection.
In the event Homarr doesn't have an icon for something you want on the dashboard, you can manually download it and put it into the /static/icons folder.
The easiest way to find icons is to search in that same site and right-click then copy the link and use the following command:
curl -o static/icons/<ICON FILENAME> <DOWNLOAD LINK>
An example is:
curl -o static/icons/localsend.svg https://cdn.jsdelivr.net/gh/selfhst/icons/svg/localsend.svg
I will eventually add functionality to download from other sources automatically.
Storage is automatically discovered by reading Linux mount information. The script skips common pseudo filesystems and Docker overlay mounts by default, so most users only need:
stats:
storage:
auto: true
include_mounts: []Set include_mounts only if you want exact control:
stats:
storage:
include_mounts:
- /
- /mnt/media
- /mnt/backupsPrivate output shows total storage, used storage, and mount breakdown. Public output hides the mount breakdown.
Private output shows:
- domains
- IP addresses
- storage mount breakdown
- content flows
Public output hides:
- domains
- IP addresses
- mount breakdown
- content flows
- items marked
hidden: true - groups marked
hidden: true
Flows are configured in YAML and shown only in private mode:
flows:
- name: HTTPS traffic
port: 443
steps:
- Internet
- Cloudflare
- Traefik
- name: Docker Services
icon: dockerRun once per day:
15 3 * * * cd /docker/topology && /docker/topology/.venv/bin/python build_topology.py --private --theme dark >> /var/log/homelab-topology.log 2>&1Public output can be generated separately:
20 3 * * * cd /docker/topology && /docker/topology/.venv/bin/python build_topology.py --public --theme light >> /var/log/homelab-topology-public.log 2>&1This repository only generates files. Publishing them is intentionally left to your own environment.
A personal wrapper script can run the build, detect changes, and push the output to a website repo. For example:
#!/usr/bin/env bash
set -euo pipefail
cd /docker/topology
. .venv/bin/activate
python build_topology.py --private --theme dark
# Example only: copy output somewhere else, then commit if changed.
# rsync -a output/ /path/to/site/static/topology/
# cd /path/to/site
# git diff --quiet || { git add . && git commit -m "Update homelab topology" && git push; }