githubEdit

Stable Port Assignments for Any Dev Server (AI Agent-Friendly)

TL;DR — Give your AI coding agent (GitHub Copilot, Claude Code, OpenAI Codex, Gemini CLI) a justfile that launches any local development server — Phoenix, Node.js, Python, Go, Ruby, or anything else that listens on a port — with stable, collision-free port assignments using phx-portarrow-up-right. The agent can start, stop, and open your app without guessing ports or stepping on other running projects.

Despite the name, phx-port is not Phoenix-specific. It works with any project that needs a local port.

This article is self-contained: point your coding agent at it and say "Adopt the pattern in https://cookbook.geuer-pollmann.de/elixir-beam/phx-port-and-justfile-for-ai-agents.md for my project."


Table of Contents


The Problem

When you work on multiple web projects, they tend to default to the same port — 4000 for Phoenix, 3000 for Node/Rails, 8000 for Django, 8080 for Go. You end up either:

  • Killing the old server before starting the new one

  • Manually remembering which port you assigned to which project

  • Passing PORT=4007 and hoping you haven't already used 4007 somewhere else

AI coding agents make this worse. When an agent needs to start your dev server to validate a change, it doesn't know which port to use. If another project is already running on that port, the server fails to bind and the agent wastes time debugging a port conflict instead of doing real work.

What we want: every project gets a stable, unique port — automatically — and the agent has a single command to start the server, open the browser, or stop the process.

The Pattern

Two pieces work together:

  1. phx-portarrow-up-right — a small Rust CLI that maintains a TOML registry (~/.config/phx-ports.toml) mapping project directories to port numbers. Each project gets a unique port, allocated once and reused forever. Despite its name, it is framework-agnostic — it works with Phoenix, Express, Django, Rails, Go, or any server that reads a PORT environment variable. Port 4000 is kept free for ad-hoc use.

  2. A justfile — a justarrow-up-right command runner file in the project root that wires together phx-port and your project's start command. For Elixir/BEAM projects, it can additionally integrate distributed Erlang (--sname / --cookie) and the BEAM introspection script (scripts/dev_node.sh).

The agent (or you) runs just start and gets a server on a known, stable port — every time, on every machine.

How phx-port works

phx-port auto-detects behavior based on context:

Context
Behavior

Piped (e.g. PORT=$(phx-port))

Prints just the port number. Auto-registers the current directory if not yet known.

Interactive (run in a terminal with no arguments)

Shows help text. Never auto-registers accidentally.

phx-port list

Shows all registered projects as a directory tree with clickable URLs.

phx-port open

Opens the default browser at http://localhost:<port> for the current project.

phx-port register

Explicitly registers the current directory for a new port.

phx-port register debug

Registers a named port role (e.g., for a debug port, metrics endpoint, etc.).

Projects can have multiple named port roles:

The registry looks like this:


Step 1: Install phx-port

Or build from source:

Verify it works:


Step 2: Add the justfile

Create a justfile in your project root. This is the single entry point for starting, stopping, and managing the server. Below are examples for different stacks.

Generic justfile (Node.js, Python, Go, etc.)

This works for any server that reads the PORT environment variable:

Replace your-start-command with what your project needs — npm start, python manage.py runserver 0.0.0.0:$PORT, go run ., bundle exec rails server -p $PORT, etc.

Elixir / Phoenix justfile

For Phoenix or other BEAM projects, the justfile can additionally wire in distributed Erlang for live introspection:

What each recipe does

Recipe
Description

just start

Starts the server in the foreground. Output goes to the terminal and run.log.

just start-bg

Same, but runs silently in the background. All output goes to run.log.

just open

Opens the app in your browser. For the Elixir version, starts the server in the background first if needed.

just stop

(Elixir) Gracefully shuts down the running BEAM node via System.halt().

just status

(Elixir) Checks whether the BEAM node is registered with epmd.

just rpc '<expr>'

(Elixir) Evaluates an Elixir expression on the running node (requires scripts/dev_node.sh from the BEAM introspection pattern).

Key design decisions

  • PORT respects overrides${PORT:-$(phx-port)} means you can still do PORT=9999 just start if needed, but the default is always the stable phx-port assignment.

  • exec replaces the shell — the server process takes over the shell's PID, so signals (Ctrl+C) go directly to it.

  • (Elixir-specific) --sname is derived from the directory name — no configuration needed. The project directory my_app becomes node my_app@hostname.

  • (Elixir-specific) --cookie devcookie — a shared development cookie so dev_node.sh can connect for introspection.


Step 3: Update .gitignore

Add runtime artifacts that shouldn't be committed:


Step 4: Add Project Instructions for Your Agent

Tell your AI coding agent about the justfile so it knows how to start and manage the server. Add this to your project's agent instructions file.

GitHub Copilot — AGENTS.md or .github/copilot-instructions.md

For Elixir/BEAM projects, add just stop, just status, and just rpc '<expression>' to the list above — see the Elixir justfile variant.

Claude Code — CLAUDE.md

OpenAI Codex / Gemini CLI — AGENTS.md

Same content as the Copilot section above. Both tools read AGENTS.md at the project root.


How It All Fits Together

Here's a typical workflow, whether it's you or an AI agent:

An AI agent working on my_app simply runs just start-bg, waits for the server, and can then curl http://localhost:$(phx-port) to validate its changes — without worrying about port conflicts with the api project running in the background.


Elixir / BEAM Bonus: Combining with BEAM Introspection

This pattern is designed to work together with the BEAM Live Introspection pattern. The justfile recipes (stop, status, rpc) delegate to scripts/dev_node.sh, which provides full runtime introspection capabilities.

To set up both patterns together:

  1. Follow this article to add phx-port and the justfile

  2. Follow the BEAM introspection article to add scripts/dev_node.sh and the introspection skill

The justfile becomes the high-level interface ("start my server"), while dev_node.sh provides the low-level distributed Erlang plumbing ("connect to the running node and evaluate this expression").

Last updated