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.
/proc/net/tcp · tcp6 · udp → ss fallback
Reads the /proc/net tables directly, with ss as a fallback; process names come from /host/proc when that mount is present.
Docker API
Container names, status, images, published mappings.
docker.sock (ro) · /proc/<pid>/fd
Reads docker.sock (read-only mount): name, status, image and published ports per container. Host-network containers are matched via /proc socket inodes.
Compose files
Ports that are declared — even when the stack is stopped.
COMPOSE_SCAN_DIR · depth 4 · ≤400 files
Scans COMPOSE_SCAN_DIR for compose.y*ml / docker-compose.y*ml, up to 4 levels deep. Sibling .env files are read locally, never uploaded.
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.