Self-hosted port registry
Give your agents a port to ask for, not a guess to make.
PortKeep hands out stable ports and permanent HTTPS URLs to people, scripts and AI coding agents. It checks what is actually running, keeps Caddy in sync, and flags drift. It never deletes anything on its own.
Same project, same port, every time. The URL is permanent and served over HTTPS.
- Self-hosted
- Linux server
- HTTPS by default
- Flags drift, never deletes
- Source-available, BSL 1.1
From the first drift soak
17 unregistered port squatters. 3 orphaned services.
That is what PortKeep's drift detection found on one dev box during its first soak. The owner did not know about any of them. A squatter is a process listening inside a machine's assigned range that PortKeep never handed out. An orphan is an allocation whose service is gone. PortKeep flagged all twenty and released none of them.

For AI coding agents
Three agents, one box, one port 3000.
Run Claude Code, Codex CLI and a third agent on the same machine and each starts its own dev server. None of them knows about the others, so each picks a port independently. They collide many times a day. The agent that loses spends a turn debugging a bind error that has nothing to do with its task, and you cannot referee it in real time.
PortKeep moves the decision before the bind. The agent asks the registry for a port. The registry gives it one nobody else holds, and gives the same one back next time. Allocation is idempotent, so a retry is safe.
Agents can reach it three ways: the portkeep CLI, plain HTTP, or portkeep-mcp, an MCP server that exposes seven tools over stdio.
MCP tools: list_services, get_service, allocate_port, check_port, release_service, list_machines, restart_service.
A ready-made block for your CLAUDE.md or AGENTS.md ships with the docs.
Check before binding.
check_port tells the agent whether a port on a machine is free and, if not, who owns it.
Scoped by default.
Give agents a machine token. It can only allocate, move and restart on its own machine.
Read-only when you want it.
Set PORTKEEP_MCP_READ_ONLY=1, or hand the agent a read-only API token, which the server enforces.
For homelabs
Your port map is in a wiki page and your head.
Ports live in a wiki page, a half-remembered compose file, and your memory of what you did in March. A new service takes whatever looked free. You find the collision when the container fails to bind. A box you repurposed last year still has allocations pointing at it.
PortKeep gives each machine its own non-overlapping port range and allocates from it. Every service gets a permanent hostname like https://myproject.lab, and a dashboard shows status, owner, machine and port for all of them. Docker containers register themselves from two labels:
Set portkeep.project explicitly on every container. A compose file with several services needs a distinct project name for each one.
The server runs on your hardware and makes no calls to a hosted service.
For small dev teams
Stop answering "is 5173 taken" in chat.
A solo developer or a shop of a few people runs several projects across a laptop, a shared dev box and a cheap VPS. Every npm run dev or uvicorn is a fresh guess at a free port. Nobody has a shared view of what runs where, and a stuck dev server on another machine means an SSH session and some hunting.
PortKeep gives the team one place to ask. A project keeps its port across restarts, and re-creating a deleted project gets the old number back when it is still free. Accounts are admin or member. Each project has an owner, each user can mint API tokens with an optional expiry or read-only scope, and every allocate, move, restart and delete lands in an audit log. Restart a service and tail its logs from the dashboard.
How it works
Install once, enroll each machine, then ask.
Install the server
Run the Docker Compose bundle on a Linux box, or install natively under systemd. Point wildcard DNS for
*.lab(or your own domain) at it. The Compose bundle includes Caddy.*.lab -> your serverEnroll each machine
An admin mints a one-time token. On the machine, run
portkeep enroll <token>. The server assigns it a port range. No config file edit and no server restart.portkeep enroll <token>Ask for a port
A human, script or agent runs
portkeep alloc myproject --machine workstation. It returns the port andhttps://myproject.lab. Asking again returns the same answer. Caddy gets the route over its admin API, live.portkeep alloc myproject --machine workstationWatch it
The dashboard shows live status per service, machine liveness, health checks (HTTP or TCP), logs and the audit trail. Heartbeats and drift detection run in the background and flag anything that does not match the registry.
https://myproject.lab


The dashboard
What you see once it is running.

One page per service.
Each project has a record with its machine, port, URL, owner and health check, and a log tail beside it. Restart a service and tail its logs from the dashboard.

An audit trail.
Every allocate, move, restart and delete lands in an audit log. Each entry shows when it happened, who did it and what they did.

A Trust page for every client.
PortKeep serves the root certificate and a Trust page with install steps for Windows, Linux, macOS, Firefox and the command line, so each person can do it themselves.
Drift detection
It flags. A human decides.
PortKeep compares the registry with what is actually listening. It flags three things:
Orphaned allocations
Allocations whose service has gone.
Unregistered listeners
Processes listening inside a machine's range that were never registered.
Stale machines
Machines that stopped checking in.
It does not kill, release or restart anything on its own. Release is a manual action, taken by you or by an agent acting for you. We built it that way on purpose. A tool that reaps on its own will eventually reap something you needed, and that is the end of your trust in it.
A host that shut down cleanly is reported as "host down (clean shutdown)" and not as a set of errors.
HTTPS by default
Every project hostname is HTTPS.
Every project URL is served over HTTPS by Caddy, using its internal certificate authority. That keeps traffic on your LAN encrypted and keeps browsers from complaining about mixed content on your own services.
It is a LAN certificate authority, not a public one. Each client machine needs to trust the PortKeep root certificate once. It is not Let's Encrypt, and public ACME certificates are not part of this release.
Security basics are in place: login throttling, security headers, logs without secrets, machine tokens scoped to their own machine. The security review before launch left no open critical or high issue.
License and requirements
Source-available, self-hosted, Linux.
| License | Source-available under the Business Source License 1.1. It is not open source. Each version converts to Apache-2.0 four years after its release. |
|---|---|
| Free to run | Production use is free for an individual's personal, non-commercial home lab, and for a single legal entity with up to 5 enrolled machines. Beyond that you need a commercial license. |
| Server | A Linux machine, x86-64 or arm64, with Docker Engine 24 or newer and Compose v2. Debian 12/13 and Ubuntu 22.04/24.04 are the reference distros. Native systemd install is supported on Debian 12/13 and Ubuntu 24.04. |
| DNS | Wildcard DNS for *.lab or your own domain. The admin settings page generates ready-to-paste OPNsense/dnsmasq config. |
| CLI and agents | Python 3.12 or newer on machines that run the portkeep CLI, agent or MCP server. |
| Not included | No hosted option. PortKeep runs on your hardware. |
Terms are being finalized. This summary is not the license text.
Questions
Can I read the source?
Not yet. It is source-available under the Business Source License 1.1, not open source. The repository is private until release. Once published you can read the code, run it free for personal home-lab use or for one organization with up to 5 enrolled machines, and each version becomes Apache-2.0 after four years. Larger use needs a commercial license.
Do you run it for me?
No. PortKeep is self-hosted only. The data stays on your machines and no external service sits in the path.
Will it kill or release things on its own?
Never. Drift detection only flags. A person, or an agent acting for a person, presses Release.
Does it replace Caddy, Traefik or Consul?
No. It sits in front of Caddy and manages its routes. PortKeep decides which port a project gets and records it. Caddy does the routing. It tracks runtime state only and is not a project tracker, orchestrator or CI system.
Which agents work with it?
Anything that can run a shell command or make an HTTP request. Claude Code and other MCP clients can use portkeep-mcp directly. There is a ready-made block for your CLAUDE.md or AGENTS.md.
Does HTTPS work with real certificates?
HTTPS uses Caddy's internal CA, so each client machine has to trust the PortKeep root certificate once. Public domain certificates through ACME are not supported in this release.
What runs where?
The server runs on Linux. The portkeep CLI, the Docker discovery agent and the MCP server run on any systemd Linux with Python 3.12 or newer. There are no Windows or macOS agents. Windows and macOS machines can open the dashboard and the project URLs once they trust the root certificate.
How do I get it?
Join the waitlist. The repository and image are private for now, and there is no public download yet. We will email once, when there is something to run.
Get a port. Keep it.
PortKeep is in development and not publicly available yet. Leave an email and we will write once, when there is something to run.