Port occupancy map for homelabs
Know which ports are taken, and which are free.
Port-Light merges your host's listen tables, Docker, and Compose files into a single traffic-light grid, so you can tell taken ports from free ones without reaching for ss.
45 in use ยท 4 configured
live demo ยท simulated data
How it works
Three sources, one grid.
Nothing to install elsewhere, nothing leaves the machine. Port-Light reads what is already true on your host and merges it into one view.
Host listen tables ยท /proc, ss
TCP/UDP ports actually bound right now.
Docker API
Container names, status, images, published mappings.
Compose files
Ports that are declared โ even when the stack is stopped.
Features
For hosts running more than a few stacks.
Search that suggests
Type a port number; if it's taken, nearby free ones light up.
Conflict radar
Two Compose projects claiming the same host port get flagged before they collide.
Multi-host, one screen
Pull occupancy maps from other Port-Light instances over LAN or Tailscale.
Live updates
SSE pushes a refresh the moment occupancy changes; local history records each transition.
Hooks & metrics
Optional webhooks on new listeners and conflicts; Prometheus aggregates when you want them.
Agent-friendly API
GET /api/ports/suggest returns a free port and can reserve it with an expiring lease.
Speaks your theme
Fifteen palettes from Gruvbox to Kanagawa; four UI languages.
Stays on your machine
No telemetry, no accounts, no cloud. Your port map never leaves the host.
For coding agents
A port API your agent can rely on
Coding agents pick ports by guessing until one collides with your stacks. Port-Light exposes a small HTTP API that returns ports which are free right now and can hold them with an expiring lease. An agent skill and an MCP server wrap the same endpoints; AGENT_TOKEN requires an auth header.
$ export PORT_LIGHT_URL=http://127.0.0.1:2100 curl -s "$PORT_LIGHT_URL/api/ports/suggest?count=2&reserve=true&ttl=3600&label=preview" { "ports": [8081, 8082], "reserved": [8081, 8082], "expires_at": "2026-08-25T13:04:11Z" }
Reserved ports appear as configured (amber) on every map until the lease expires.
Appearance
Fifteen palettes, straight from the app.
Click one and the whole page re-skins, mirroring the app's theme system. Your pick persists.
Quick start
One compose file.
services:
port-light:
image: stepaniah/port-light:v0.7.2
container_name: port-light
restart: unless-stopped
ports:
- "${PORT_LIGHT_PORT:-2100}:2100"
volumes:
- /path/to/your/compose-stacks:/compose:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- /proc:/host/proc:ro
- ./data:/data
environment:
COMPOSE_SCAN_DIR: /compose
mkdir -p data docker compose up -d
โ http://localhost:${PORT_LIGHT_PORT:-2100}
The public port follows PORT_LIGHT_PORT; it defaults to 2100.
Images for linux/amd64 and arm64, also on GHCR. Prefer version tags over latest.
A port occupancy map โ not a container manager.
Port-Light doesn't start or stop containers, tail logs, or replace Portainer. It serves one read-only page on your LAN โ keep it behind Basic Auth or a reverse proxy and off the public internet. The security checklist collects the hardening notes.