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.
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 useCommon 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.
# 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
- Track the background server PID and kill it in teardown (or trap EXIT in the shell).
- Set
SO_REUSEADDRso a socket in TIME_WAIT doesn’t block a rebind. - 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_REUSEADDRand avoid fixed shared ports across steps.