Self-Host Homelab Atlas with Docker Compose and Draw Your Homelab Diagram

Homelab Atlas is a free, MIT-licensed, self-hosted app that lays out homelab diagrams for you and exports them as PNG or SVG. This guide runs the official v0.12.0 release with one Compose file, then walks through the editor, the export privacy filter and the discovery wizard.

Self-Host Homelab Atlas with Docker Compose and Draw Your Homelab Diagram

Homelab Atlas is "a self-hosted app for making homelab diagrams." You add your servers, services and network gear, and it lays out the diagram for you. It has an offline, searchable logo catalog and exports PNG or SVG. It runs locally with no account, no database and no LLM. Your project autosaves in your browser, and you can save a JSON copy as a backup.

This guide follows the path we ran and checked against v0.12.0 (MIT): the project's own compose.release.yaml and the prebuilt image ghcr.io/aiden0rchad/homelab-atlas:0.12.0. You don't build anything, and you don't need a .env file, volumes or secrets.

The Homelab Atlas example

Prerequisites

  • Docker with the Compose v2 plugin (docker compose version works). We tested on Linux x86_64 with Docker Compose 5.6.1. The image is published for Linux AMD64 and ARM64.
  • curl
  • Port 8080 free on the Docker host (the upstream default). Step 5 shows how to use a different port if 8080 is taken.
  • A browser on the same machine. The release file binds to 127.0.0.1 only.

Install

1. Download the release Compose file

curl -fsSLO https://github.com/aiden0rchad/homelab-atlas/releases/download/v0.12.0/compose.release.yaml

This file is identical to deploy/compose.release.yaml in the repo (we diffed them). It defines two services:

  • atlas: the web app. It is published on 127.0.0.1:8080, runs with a read-only root filesystem, has all capabilities dropped and sets no-new-privileges.
  • collector: an optional Docker discovery helper behind the discovery profile. It is off unless you enable that profile, and this guide leaves it off.

2. Pull the image

docker compose -f compose.release.yaml pull

In our run the image pulled in about 5 seconds:

✔ Image ghcr.io/aiden0rchad/homelab-atlas:0.12.0 Pulled   5.0s

3. Start it

docker compose -f compose.release.yaml up -d

Compose creates two networks (_default and _discovery) and starts one container.

4. Check that it is healthy

docker compose -f compose.release.yaml ps
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/healthz

Right after startup, ps shows Up … (health: starting), and a few seconds later (healthy). The health check prints 200.

5. (Optional) Use a different host port

The release file hard-codes ${ATLAS_BIND:-127.0.0.1}:8080:8080 and has no port variable. To publish another port without editing the upstream file, add an override next to it. We used port 3000 for the recording:

cat > compose.override.yaml <<'EOF'
services:
  atlas:
    ports: !override
      - "127.0.0.1:3000:8080"
EOF

docker compose -f compose.release.yaml -f compose.override.yaml up -d
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:3000/healthz   # 200

The !override tag needs Docker Compose 2.24.4 or newer. The app keeps listening on 8080 inside the container. In the rest of this guide, replace 8080 with your port if you changed it. Our screenshots show localhost:3000.

Browser setup

Open http://localhost:8080/ (or your custom port). Use localhost or 127.0.0.1, not the machine's LAN IP. The API checks the host name, and a LAN IP gets 403 from /api/*.

Load the example project

On first load you get an empty dark editor with Saved locally in the header.

Empty Homelab Atlas editor on first load

Click File → Load example and accept the "Replace this project?" prompt. The bundled "Home Lab" example loads with 5 devices, 3 networks and 58 apps. Click Fit page to see the whole diagram. (To draw your own lab, choose File → Start empty instead.)

The example project loaded: 5 devices, 3 networks, 58 apps

Arrange and auto layout

Atlas places the cards for you. To adjust the layout, click Arrange in the canvas header, then drag or resize device cards. Auto layout snaps everything back to the automatic layout. Click Arrange again to leave arrange mode.

Arrange mode with the

Auto layout has moved the card back into place

Add a device and its services

  1. In the Inventory drawer, click Devices Add and pick a template. We picked Server. A new card appears, the device count goes from 5 to 6, and the Inspector opens.
  2. In the Inspector, find the device's Services group and click Apps.
  3. Search the logo catalog (we searched Jellyfin). Tick the checkbox next to each app you want; the picker supports multi-select. Then click Add selected.

The new Server device with the Inspector open

Jellyfin searched, ticked and added; the card now shows Apps (1)

Pressing Escape in the app picker clears the search box and leaves the picker open. Close it with its own cancel/✕ button.

Export PNG or SVG

Click Export. The Export diagram dialog shows a read-only preview with Hide network details turned on by default. With the filter on, IP addresses, MAC addresses, URLs and email addresses in the exported image are replaced with [hidden]. You can also list your own terms to hide.

Export dialog with Hide network details on by default

Click the SVG or PNG card. The download starts immediately; there's no separate Download button. Close the dialog with ✕.

The filter only changes the exported image. It doesn't change your saved project JSON or text that is part of an uploaded logo image.

Discover devices (optional, view only)

Click Discover devices to open a Choose → Find → Review wizard with three sources: Docker apps, network devices (Nmap), and Import saved scan (Docker JSON or Nmap XML). On a default install the Docker and Nmap cards say Setup needed, because the collector profile is off and Nmap isn't in the published image. Import saved scan says Available. Atlas never scans on its own, and every result starts unchecked until you review it.

Discover devices wizard on the Choose step

We cancelled at this step and didn't run a scan.

The finished diagram, including the new Server card

Verification

# Container is up and healthy
docker compose -f compose.release.yaml ps
# → … Up N seconds (healthy)   127.0.0.1:8080->8080/tcp

# Health endpoint
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8080/healthz
# → 200

# The UI is served
curl -s http://localhost:8080/ | grep -o '<title>[^<]*</title>'
# → <title>Homelab Atlas</title>

# The API answers (the bundled frontend sends this header; a raw curl without it gets 403)
curl -s -H 'X-Atlas-Request: 1' http://localhost:8080/api/discovery/sources
# → []

In the browser: the example diagram renders with logos, Arrange and Auto layout move cards, and Export downloads a file. In our run, the exported home-lab.svg was a real 1448×1727 SVG.

Back up your work

Autosave is stored in your browser's localStorage, not in the container. Clearing site data, using a private window, or opening Atlas at a different host name or port can make your project look missing. Use File → Save project to download a JSON copy, and File → Import project to load it back. You can't import a PNG or SVG back as an editable project.

Troubleshooting

SymptomCauseFix
/api/* returns 403You opened the app at a LAN IP or hostname that isn't allowed, or a raw request left out the X-Atlas-Request: 1 headerOpen http://localhost:8080 or http://127.0.0.1:8080. For curl, add -H 'X-Atlas-Request: 1'
port is already allocated on up -dSomething else is using 8080 on the hostUse the override in step 5 with a free port
!override tag rejectedDocker Compose is older than 2.24.4Upgrade Compose, or edit the port line in compose.release.yaml to "127.0.0.1:<port>:8080" and run without the override
Container keeps restarting with ATLAS_API_TOKEN is required…ATLAS_ALLOWED_HOSTS includes a non-loopback host but no ATLAS_API_TOKEN is setUnset ATLAS_ALLOWED_HOSTS to stay on localhost, or follow the upstream setup guide for remote access
Project is gone after reopeningAutosave is per browser and per origin (host + port)Open the same URL you used before, or load your saved JSON with File → Import project
App picker won't close with EscapeEscape only clears the search fieldUse the picker's cancel/✕ button
No Download button in ExportIt doesn't existClicking the SVG or PNG card downloads the file
Discovery shows "Setup needed" for Docker and NmapThe discovery collector profile is off, and Nmap isn't in the imageExpected on a default install. Use Import saved scan, or see the upstream discovery guide
Pull is denied or rate-limited from GHCRRegistry issueClone the repo and run docker compose -f compose.yaml up -d --build (multi-stage Go build)

Security note

Homelab Atlas has no login system. Keep it on localhost or a trusted LAN, and don't expose it directly to the internet. The release file's 127.0.0.1 bind already enforces this by default.

Teardown

docker compose -f compose.release.yaml down --remove-orphans
docker image rm ghcr.io/aiden0rchad/homelab-atlas:0.12.0   # optional

If you used the step 5 override, add -f compose.override.yaml to the down command. There are no volumes, so this removes everything. Your diagrams survive only in your browser's localStorage and in any JSON you saved.

Assumptions & decisions

  • The main commands follow the README (port 8080, no project name). Our recorded run used -p shortform-homelab-atlas-20261029-215532 and an override that published 127.0.0.1:3000:8080. The reviewer confirmed the two are equivalent apart from the host port, so the override appears as optional step 5 and the screenshots are labelled as showing :3000.
  • The privacy filter is described by what it does, not by a before/after picture. The example project has no IPs, MACs or URLs, so in our capture the preview didn't change when we toggled the filter (a pixel diff showed only the checkbox moving). I left out 09-export-show-network.png and 10-export-hide-network.png because they would suggest a visible change that doesn't happen.
  • No pull-time measurement beyond the one we logged. The 5.0 s pull and the "health: starting → healthy" transition come from the deploy log and recording. They aren't guaranteed on other machines.
  • Discovery is shown view-only. The collector profile needs the Docker socket and a token, so it stays out of scope. The upstream discovery guide is linked instead.
  • The source-build troubleshooting row uses the repo's compose.yaml from the install plan's fallback B. We didn't run it in this job because the GHCR pull worked.
  • Image paths are relative to the pipeline root (runs/<job>/browser/…), matching how the ghost stage resolves local images. Every referenced file exists.
  • The slug is homelab-atlas (the repo name), so the post lives at https://howto.croogserverdude.ca/homelab-atlas. It is drafted, not published.