Try Pro free for 14 days
← All posts
Tutorials

Stop parallel coding agents fighting over ports and dev servers

Separate runtime resources as well as branches so parallel coding tasks test the application they actually changed.

One agent starts a development server on port 3000. A second tries the same port and fails. A third finds an existing server, opens it in a browser, and reports that its change looks correct. Unfortunately, that server belongs to the first agent.

Parallel agents need separate runtime resources in addition to separate files. Git worktrees help keep branches apart, but a worktree doesn't give its processes a private set of network ports. A browser pointed at the wrong app can make an otherwise careful verification pass meaningless.

Find the owner before stopping a server

When a port is occupied, identify the listening process and the task it belongs to. On macOS, this read-only command shows listeners on port 3000:

lsof -nP -iTCP:3000 -sTCP:LISTEN

Use the process details and your terminal sessions to find which checkout launched it. An agent shouldn't kill a process just because it blocks its preferred port. The process could be another agent's verification run or a service you're using yourself.

If the owning task has finished, stop its server in the terminal as you normally would. If it is still active, assign a different port to the new task.

Allocate all the resources a task shares

For two local tasks, reserve frontend port 3000 and API port 8787 for task A, then 3001 and 8788 for task B. Pass those values using your framework's supported flags or environment variables. Check the project's scripts before assuming PORT controls every server.

The API address the frontend calls needs to match, and so do browser test base URLs and OAuth callback settings where applicable. An application that listens on 3001 while its tests still open 3000 hasn't achieved isolation.

Also inspect shared data. Two branches can have separate servers and still reset the same test database. Give each task a separate database, schema or disposable service instance as your application supports. Apply the same reasoning to Redis keys, queues, storage buckets and fixed container names.

A short task record is useful here:

Task: pagination boundary fix
Branch: fix/pagination-boundary
Frontend: localhost:3001
API: localhost:8788
Database: pagination_test
Browser test base URL: http://localhost:3001

These are example values. The point is that the agent can read the assignment before launching anything.

Give the agent a collision policy

Put the runtime rules in repository guidance or the task brief. For example:

Use the assigned ports and test database. If either port is occupied, identify its owner and pause for guidance. Do not kill an unrelated process or silently switch the browser to another running app. Record the URL and commit used for browser verification.

Allowing a server to choose the next available port can be convenient for manual work. In an automated workflow, the agent must capture the actual port and update every dependent test. Otherwise the fallback leads to results that are hard to reproduce.

Use a separate cloud sandbox for each task

Hoplite cloud agents give each thread its own sandbox. That separates the execution environments for concurrent threads, so each can run its own app without competing for your laptop's listener on port 3000.

Configure the app through .hoplite/settings.json. ports.preview identifies the port Hoplite waits on and proxies; ports.additional names extra ports. The documented port range is 3000 through 9999. The run command still needs to launch the application on the configured port.

Sandbox isolation doesn't isolate external services automatically. If two threads receive credentials for the same test database, they can still alter the same records. Give those services their own per-task separation where concurrent writes matter.

Verify the exact instance

For each completed change, record the branch or commit, application URL and test command. Check visible behavior introduced by that change. A homepage loading is weak evidence when every branch has the same homepage.

Start by separating two small tasks. Confirm each opens its own app and writes to its own test data. Once that works, add concurrency gradually. The next shared resource is usually less obvious than port 3000.