Skip to content
Latchkey

uvicorn/gunicorn "[Errno 98] Address already in use" in CI

The server couldn’t bind its port because something is already listening on it. In CI this is usually a previous server process that didn’t shut down, or two steps trying to use the same port.

What this error means

Starting uvicorn/gunicorn (or any socket server) for an integration test fails with OSError: [Errno 98] Address already in use. It often appears on a re-run within the same job, or when a background server from a prior step wasn’t stopped.

uvicorn output
ERROR:    [Errno 98] error while attempting to bind on address ('0.0.0.0', 8000):
address already in use
# or
OSError: [Errno 98] Address already in use

Common causes

A previous server still holds the port

A background server started earlier in the job didn’t terminate, so its socket still owns the port when the next bind is attempted.

Port collision between steps or in TIME_WAIT

Two services pick the same fixed port, or a just-closed socket is in TIME_WAIT, so the new bind is refused until it clears.

How to fix it

Use an ephemeral port

Bind port 0 (or a random free port) so collisions can’t happen, and read the assigned port back.

Terminal / Python
# let the OS pick a free port
uvicorn app.main:app --port 0
# or in a fixture: bind to 0 and read sock.getsockname()[1]

Stop the previous server before rebinding

  1. Track the background server PID and kill it in teardown (or trap EXIT in the shell).
  2. Set SO_REUSEADDR so a socket in TIME_WAIT doesn’t block a rebind.
  3. Avoid hardcoding the same fixed port across parallel jobs/steps.

How to prevent it

  • Bind ephemeral ports in tests and read the assigned port back.
  • Always stop background servers in teardown.
  • Enable SO_REUSEADDR and avoid fixed shared ports across steps.

Related guides

Tired of flaky CI? Latchkey auto-heals failed jobs and retries them for you. Start free →