Skip to content

feat: Use a free port when 3939 is in use and --port isn't given - #73

Merged
nealrichardson merged 2 commits into
mainfrom
auto-port
Oct 23, 2026
Merged

nealrichardson merged 2 commits into
mainfrom
auto-port

Conversation

@nealrichardson

@nealrichardson nealrichardson commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #30.

Stacked on #70 (clean-errors). After #70 merges, retarget this to main and rebase.

Behavior

  • --port N given: used as is. If N is taken, Docker's error is reported.
  • No --port, 3939 free: uses 3939.
  • No --port, 3939 in use: Docker assigns a free port. with-connect reads it back from the container and prints Port 3939 is in use. Using port N. on stderr.

CONNECT_SERVER always has the port in use. This lets several Connect instances run at once without picking ports by hand, which is what posit-sdk-py does with a shell one-liner.

The check binds ("", 3939), the same as Docker publishing on all interfaces. It's needed because Docker Desktop doesn't report a port held by a host process: without the check, the container starts and with-connect waits on, and talks to, the other process. If something takes 3939 between the check and the container start, Docker's "port is already allocated" error is reported and the failed container is removed. There's no retry for that race.

Action

The port input has no default, and --port is only passed when the input is set. A workflow that sets port: 3939 explicitly gets the strict behavior. The README documents both the CLI option and the action input.

Verification

Real runs against ghcr.io/posit-dev/connect:latest on Docker Desktop, with README.md as a stand-in license, so Connect starts and then fails the license check:

  • 3939 held by a host process: Port 3939 is in use. Using port 54060.
  • 3939 held by another container: Port 3939 is in use. Using port 54069.
  • --port 3939 while held by another container: Failed to start container ... port is already allocated, with no container left behind.
  • 3939 free: Connect started on 3939.

Unit tests cover parsing, is_port_free, the default port, the Docker-assigned fallback, an explicit port, and a missing port binding.

🤖 Generated with Claude Code

Comment thread main.py Outdated
# Another process took the default port after we checked it
create_kwargs["ports"] = {"3939/tcp": None}
host_port = None
container = start_container(client, create_kwargs, image_name)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic feels overly complicated to me. There is a default of 3939, but it will find an available port, unless you specify a port, in which case it will fail if that one is not available. That's already complex. But the way this code resolves that seems convoluted. Can we simplify at all?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplified in ccbd2d4. choose_port, PortInUseError, the retry after a failed start, and the host_port/args.port juggling are gone. What's left: port = args.port, or 3939 if it's free, or None to have Docker assign one, which is read back after the container starts.

I tried dropping the free-port check too and relying on Docker's start error, but Docker Desktop doesn't report a port held by a host process. The container started, and with-connect talked to the other process on 3939. So the check stays. The cost of dropping the retry is that a port taken between the check and the start now fails with Docker's "port is already allocated" error.

@nealrichardson

Copy link
Copy Markdown
Collaborator Author

Looks good to me now. Just needs rebase.

nealrichardson and others added 2 commits October 23, 2026 15:08
An explicit --port (or the action's port input) is used as is. Otherwise
with-connect uses 3939, or lets Docker assign a free port if 3939 is
taken, so several Connect instances can run at once. CONNECT_SERVER has
the port.

Fixes #30.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without --port, use 3939 if it's free, and otherwise have Docker assign
a free port. This drops choose_port, PortInUseError and the retry after
a failed start. Losing the race between the check and the start gives
Docker's "port is already allocated" error, which is rare and clear.

The check before starting stays: Docker Desktop doesn't report a port
held by a host process, so Connect would start and with-connect would
talk to the other process.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nealrichardson
nealrichardson merged commit a887440 into main Oct 23, 2026
13 checks passed
@nealrichardson
nealrichardson deleted the auto-port branch October 23, 2026 19:13
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.

feat: generate random port instead of just defaulting to 3939

1 participant