Skip to content

fix: seed port allocations for every command, not just up and tasks run - #3063

Open
aostanin wants to merge 2 commits into
cachix:mainfrom
aostanin:seed-allocated-ports
Open

fix: seed port allocations for every command, not just up and tasks run#3063
aostanin wants to merge 2 commits into
cachix:mainfrom
aostanin:seed-allocated-ports

Conversation

@aostanin

@aostanin aostanin commented Aug 4, 2026

Copy link
Copy Markdown

Problem

processes.<name>.ports.<port>.value resolves to the declared base port in every
command except devenv up and devenv tasks run, even while a process manager is
running with a different allocation. Env vars derived from it are wrong too.

{ config, pkgs, ... }:

{
  processes.server = {
    ports.http.allocate = 8080;
    exec = "${pkgs.python3}/bin/python -m http.server ${toString config.processes.server.ports.http.value}";
  };

  env.SERVER_PORT = config.processes.server.ports.http.value;
}

Run it in two clones of the project. The first takes the base port:

~/project $ devenv up -d
~/project $ devenv processes list
server    ready restarts: 0 ports: http:8080

The second allocates around it, but its shell reports the first clone's port:

~/project2 $ devenv up -d
~/project2 $ devenv processes list
server    ready restarts: 0 ports: http:8081

~/project2 $ devenv shell -- printenv SERVER_PORT
8080

Root cause

reserve_running_ports() seeds the allocator from the running native manager,
but is only called from up() and tasks_run(). shell (and therefore
devenv hook, which spawns devenv shell) and direnv-export never seed, so
allocatePort is not registered and .value falls back to allocate.

This is a regression of the #2710 fix rather than a gap in it.
88ac631
seeded inside assemble(), which every evaluating command went through;
d0f9c588
("split apart and remove assemble") kept the call only in up() and
tasks_run(). The example above reports the allocated port at commit
6792fc1e
and the base port at commit
d0f9c588,
so that is the commit where it stopped working.

Fix

Call reserve_running_ports() once after Devenv::new in run_backend, before
anything evaluates. Best-effort: it only seeds when a live-PID manager answers,
so the gating added in #3024 still applies. The socket query gets a 2s timeout,
since it now runs before every command and a socket that accepts but never
answers must not hang the CLI.

The existing calls in up() and tasks_run() are left in place — they are
redundant for the CLI but still cover direct use of those public methods.

Seeding still requires a live-PID manager, so a command run during a manager's
startup window — between its processes binding their ports and the PID file
being written after the readiness probes — resolves the base port as before.
tests/process-port-allocation-two-repos waits for the PID file for that
reason. Closing that window is a separate change.

Validation

  • devenv-run-tests run --only process-port-allocation-two-repos tests, extended
    with a devenv shell assertion in the second project: fails on v2.2.1, passes
    with the fix
  • devenv-run-tests run --only 'process-*' --only 'tasks-*' tests: 18 passed
  • Two concurrent projects on x86_64-linux and aarch64-darwin: devenv shell,
    direnv-export and devenv processes list agree on the allocated port

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant