cd /news/developer-tools/preventing-port-conflicts-in-git-wor… · home › topics › developer-tools › article
[ARTICLE · art-151445] src=wietsevenema.eu ↗ pub= topic=developer-tools verified=true sentiment=· neutral

Preventing port conflicts in Git worktrees using directory hashing

A developer published a technique for preventing port conflicts across git worktrees by hashing each worktree's absolute directory path with SHA-256 and mapping the digest into 400 five-port buckets spanning ports 8000 to 9999. The approach, implemented in a Makefile, computes PORT once via `:=` and exports it, reserving PORT+1 through PORT+4 for secondary services such as the Firestore emulator, so parallel coding agents on the same repository no longer collide on port 8080.

read2 min views2 publishedOct 16, 2026
Preventing port conflicts in Git worktrees using directory hashing
Image: source
        Coding agents are making `git worktree` popular because worktrees let
        multiple agents work on the same repository at the same time without interfering with
        each other. Each branch is checked out in its own directory: if your
        *main* branch lives in `main/`, a `feature-1` branch can
        sit right next to it in `feature-1/`.
      

        However, directory isolation doesn't isolate network ports. When a server in one
        worktree runs on port `8080`, starting the server in another worktree fails
        because the address is already in use.
      

        At first, I passed ports manually per worktree (like `PORT=8001 make run`),
        which got tedious. Next, I decided to calculate a deterministic port number for each
        worktree, using its absolute directory path. Because each worktree lives in a different
        directory, every environment gets its own port automatically.

Hashing the directory path in your Makefile #

        To turn a directory path into a port number, take the SHA-256 hash of the current
        working directory, convert the hex digest to an integer, and map it into 400 buckets of
        5 ports across the `8000` to `9999` range:
full_path = os.getcwd().encode("utf-8")
path_hash = int(hashlib.sha256(full_path).hexdigest(), 16)
port = 8000 + ((path_hash % 400) * 5)
        Spacing base ports 5 numbers apart (`8000`, `8005`,
        `8010`, and so on) gives you 400 buckets, making collisions across a handful
        of local worktrees unlikely while reserving four adjacent ports (`PORT + 1`
        through `PORT + 4`) for secondary services.
      

        To avoid changing your application code, you can compute `PORT` directly in
        your `Makefile` and export it as an environment variable:
ifeq ($(PORT),)
export PORT := $(shell python3 -c "import hashlib, os; h = hashlib.sha256(os.getcwd().encode('utf-8')).hexdigest(); print(8000 + ((int(h, 16) % 400) * 5))")
else
export PORT
endif

.PHONY: run
run:
	@echo "Starting server on http://localhost:$(PORT)"
	@uvicorn main:app --host 0.0.0.0 --port $(PORT)
        If `PORT` isn't already set, Make runs the Python command once
        (`:=`) to calculate the port and exports it to child processes. If you pass a
        port manually (like `PORT=9000 make run`), the `else` branch keeps
        your value and exports it instead. Using `ifeq` with `:=` rather
        than `PORT ?=` avoids re-running the Python subshell every time
        `$(PORT)` is referenced.

Offsetting ports for secondary services #

        Because each worktree gets a 5-port bucket, you can assign up to four secondary services
        to `PORT + 1` through `PORT + 4` without colliding with another
        worktree's base port. Here's an example with the Firestore emulator:
FIRESTORE_PORT ?= $(shell expr $(PORT) + 1)
export FIRESTORE_EMULATOR_HOST ?= 127.0.0.1:$(FIRESTORE_PORT)

.PHONY: emulator
emulator:
	@gcloud emulators firestore start --host-port=$(FIRESTORE_EMULATOR_HOST)

expr $(PORT) + 1 adds 1 to the base port, and exporting FIRESTORE_EMULATOR_HOST tells the Google Cloud client libraries in your app and test runner which emulator port to connect to automatically. Passing --host-port=$(FIRESTORE_EMULATOR_HOST) binds the Firestore emulator to that same address on startup.

── more in #developer-tools 4 stories · sorted by recency
── more on @git worktree 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/preventing-port-conf…] indexed:0 read:2min 2026-10-16 · —