ConnectionRefusedError: [Errno 111] Connection refused
1 · The lesson
readWhat this error means
Your code reached the machine, and the machine said no. Something answered at that address: the network works, DNS resolved the name and the packets arrived. But nothing is listening on that port, so the operating system on the other end refused the connection immediately.
This is one of the easiest network errors to diagnose. A refusal comes immediately and has only one meaning. A timeout is different: it means nobody answered at all.
When you see it
Traceback (most recent call last): File "app.py", line 12, in <module> sock = socket.create_connection(("localhost", 5432)) ConnectionRefusedError: [Errno 111] Connection refused
Through requests, it arrives wrapped:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8000): Max retries exceeded with url: /api/health (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object>: Failed to establish a new connection: [Errno 111] Connection refused'))
On Windows the number is different ([WinError 10061]), but the meaning is the same.
Why it happens
There are four common causes:
1. The service isn't running. Postgres, Redis, or your own dev server is stopped or crashed.
2. It's running on a different port. You connected to 5432, but it is on 5433 because another Postgres already used 5432.
3. It's listening on the wrong interface. Another container or machine can't reach a server bound to 127.0.0.1. To accept outside connections, the server must bind to 0.0.0.0. This is the typical "works on my laptop, refused in Docker" problem.
4. You're in a container and used localhost. Inside a container, localhost is that container, not the host and not a sibling. You need the service name on the compose network, or host.docker.internal.
How to fix it
Check that something is listening:
ss -ltnp | grep 5432 # Linux lsof -iTCP:5432 -sTCP:LISTEN # macOS netstat -ano | findstr 5432 # Windows
If the command prints nothing, nothing is listening. Start the service.
Check which address it is bound to. 127.0.0.1:5432 in that output means it accepts only local connections. To accept connections from elsewhere, bind 0.0.0.0:
# a dev server that other containers can reach app.run(host="0.0.0.0", port=8000)
setup added so this can run · defines app
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) app = _AutoMock('app')
In Docker Compose, use the service name, not localhost:
services:
api:
environment:
DATABASE_URL: postgresql://db:5432/shop # "db", not "localhost"
db:
image: postgres:16Fail with a message that says what to do, rather than letting the traceback escape:
import socket try: conn = connect_to_db() except (ConnectionRefusedError, OSError) as exc: # database drivers wrap it (psycopg: OperationalError) raise SystemExit( "Nothing is listening on the database port.\n" " Is the container up? docker compose ps\n" " Right port? echo $DATABASE_URL" )
setup added so this can run · defines connect_to_db
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) def connect_to_db(*_a, **_kw): print('-> connect_to_db() called') return _AutoMock('connect_to_db()')
Do not retry a refusal the way you would retry a timeout. A refusal is a clear answer. Retrying it twenty times only delays the same error. Retry with backoff only while a service is known to be starting. A container that waits for its database to start is the valid case.
When you'd see this in real code
- The app container boots faster than the database container and connects before Postgres is accepting connections. Add a healthcheck and
depends_on: condition: service_healthy. - A dev server bound to
127.0.0.1inside Docker: the port is published, but nothing outside the container can reach it. - Redis or RabbitMQ restarted during a deploy, and the connection pool still held connections to a process that no longer exists.
- Someone ran a second Postgres locally, so 5432 was taken and yours came up on 5433.
- A firewall or security group that drops packets (instead of rejecting them) gives you a timeout instead. If the program hangs instead of failing immediately, check the firewall.
Related errors
- TimeoutError: connection timed out: nobody answered at all, which has different causes.
- socket.gaierror: Name or service not known: the name never resolved, so no connection was attempted.
- [OSError: [Errno 98] Address already in use](../error-address-in-use/): the matching error on the listening (server) side.
See Also
- All Python errors: the full index, by type and by when it happens.
- Environment & Configuration: keeping host and port out of your code.
- APIs & HTTP
Found this useful? Share it with a friend who’s learning Python.