# Port Registry

> Manage local port allocations to prevent conflicts between Docker containers, dev servers, and services across projects. Use when setting up docker-compose, starting dev servers, assigning ports, or when the user mentions port conflicts. Use when this capability is needed.

- Skill: `tomevault-io/port-registry` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/port-registry`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/port-registry/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/port-registry

---


# Port Registry

You have access to `portctl`, a CLI that allocates and tracks local ports across projects. Use it to prevent port conflicts between Docker containers, dev servers, and other services.

## When to Use

- Setting up or modifying `docker-compose.yml` (always allocate ports instead of hardcoding)
- Starting dev servers that need a port
- Configuring services that bind to localhost ports
- User mentions port conflicts or "address already in use"
- Any time you would otherwise pick an arbitrary port number

## Prerequisites

The port-registry must be running. Check with:

```bash
portctl health
```

If it's not running, start it:

```bash
port-registry &
```

If `portctl` is not on PATH, build it first:

```bash
# From the port-registry repo
make build
# Then either add bin/ to PATH or use the full path
```

## Commands

### Allocate a port (auto-assign)

```bash
portctl allocate --service <service>
```

Output: `allocated port 4521 (id=3) for myapp/main/postgres`

`--app` defaults to the git repo name (or folder name if not in a repo). `--instance` defaults to the git worktree directory name (for linked worktrees) or branch name (for main worktree). Pass `--app <name>` or `--instance <name>` only to override.

### Allocate a specific port

```bash
portctl allocate --service <service> --port <N>
```

Fails if the port is already taken. Prefer auto-assign unless the user explicitly requests a specific port.

### Check if a port is available

```bash
portctl check --port <N>
```

Exit code 0 = available, exit code 1 = taken. Use this before hardcoding any port.

### List allocations

```bash
portctl list                          # current project (auto-detected --app)
portctl list --app <project>          # explicit project filter
portctl list --app <project> --json   # JSON output for parsing
```

### Release ports

```bash
portctl release                                          # release ports for current project + instance (auto-detected)
portctl release --instance <env>                         # release ports for a specific instance
portctl release --app <project>                          # release by explicit project name
portctl release --id <N>                                 # release a single allocation by ID
```

## Naming Conventions

| Field      | Value                        | Examples                          |
|------------|------------------------------|-----------------------------------|
| `--app`    | Project or repository name (auto-detected from git repo name, even in linked worktrees) | `myapp`, `backend`, `analytics` |
| `--instance` | Worktree or branch name (auto-detected: worktree dir name for linked worktrees, branch name for main worktree) | `main`, `feature-auth`, `dev` |
| `--service`  | Container or service name   | `postgres`, `redis`, `web`, `api` |

Do not pass `--app` or `--instance` unless you need to override the auto-detected values.

## What to Register

Only register ports that **actually bind on the host machine**. These are the ports that can conflict with other projects.

### Docker Compose ports

Read the `ports:` section carefully:

- **`"5432:5432"`** (host:container) — fixed host binding, **register the host port** (left side)
- **`"9100:8080"`** (host:container differ) — fixed host binding, **register 9100** (host port)
- **`"3001"`** (container-only) — Docker assigns a random ephemeral host port, **do NOT register**

### Non-Docker ports

Also register ports from other sources that bind on the host:

- **npm scripts** (`npm run dev` on port 3001, `npm run storybook` on port 6006)
- **dev servers** started outside Docker
- **Ports in .env files** used by services running on the host

### Register all port numbers

Any valid port (1-65535) can be registered — including well-known low ports like 80, 443, 1080, etc.

## Workflow

When setting up services that need ports:

1. **List existing allocations** for the project to avoid duplicates:
   ```bash
   portctl list
   ```

2. **Allocate ports** for each service (--app and --instance auto-detected):
   ```bash
   portctl allocate --service postgres
   portctl allocate --service redis
   portctl allocate --service web
   ```

3. **Use the allocated ports** in configuration files (docker-compose.yml, .env, etc.)

4. **When tearing down**, release the ports:
   ```bash
   portctl release
   ```

When registering ports from an existing project:

1. **Scan all port sources**: docker-compose.yml, .env, package.json scripts, Makefile, etc.
2. **Filter to host-bound ports only**: skip container-only Docker port mappings
3. **Register with specific port**: use `--port <N>` for each known host port (--app and --instance are auto-detected)

## Port Range

Ports are allocated from the full range **1-65535**. Any valid port number can be requested.

## Reference

For detailed workflow examples including Docker Compose integration, multi-service patterns, and troubleshooting, see [references/WORKFLOW.md](references/WORKFLOW.md).

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/n3r) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

