← Back to blog

Guide

Django: That port is already in use. What to do about port 8000

September 14, 20263 min read

Django is blunt about it:

Error: That port is already in use.

No fallback, no silently moving to 8001. That is the right call, because a dev server that quietly changes its address causes stranger bugs than one that refuses to start. You know immediately what is wrong.

The first thing to check

Something is listening on 8000. Ask what:

lsof -i :8000

If the answer is python or python3, it is almost certainly a previous runserver that did not exit.

lsof -ti :8000 | xargs kill

-t prints only process IDs, xargs passes all of them to one kill, and a plain kill sends SIGTERM so the process can shut down properly. Reach for -9 only if it refuses.

The autoreloader runs two processes

This surprises people reading the output. Django’s development server normally runs two Python processes: a parent that watches your files, and a child that actually serves requests. The reloader restarts the child when code changes.

So lsof -i :8000 showing two python entries is not two servers, it is one runserver behaving normally. Kill only the child and the parent may spawn another. The xargs form handles this correctly because it terminates everything holding the port at once.

If you want to see the relationship:

ps -ef | grep runserver

The parent and child will be there with their process IDs, and you can see one is the other’s parent.

Running on another port

Django makes this easy, and it is the right move when you have several projects:

python manage.py runserver 8001

You can also bind a specific address:

python manage.py runserver 0.0.0.0:8000

That last one exposes the server to your whole network, which is what you want for testing on a phone and worth thinking about on a shared network. 127.0.0.1:8000 keeps it on your machine only.

When it is not Django

Port 8000 is popular beyond Django. Python’s own http.server defaults to it, so does a lot of tooling, and it is a common choice for containers.

If lsof shows docker-proxy or com.docke, you have a container:

docker ps --filter "publish=8000"
docker stop <container>

Killing docker-proxy does nothing useful here. Docker republishes the port, and if the container has a restart policy it comes back regardless.

The stale process that has no terminal

A common path into this state: you ran runserver inside a terminal, closed the window without Ctrl+C, and the process was orphaned. It has no visible terminal, keeps serving, and holds 8000.

Ctrl+C is the clean exit. It sends SIGTERM, Django shuts down the child and the parent, and the port is released. Closing the window does not guarantee that.

If nothing is listening but the port is still busy

lsof -i :8000 prints nothing, and Django still refuses. That is TIME_WAIT, a TCP state that reserves a recently closed port for around thirty seconds. There is no process, so there is nothing to kill. Waiting is the fix.

More on that in what EADDRINUSE actually means, and on how the port ranges work in port number ranges explained.

Bosun shows what is holding port 8000 from the menu bar, collapses the reloader’s two processes into one row, and names Docker containers instead of showing docker-proxy. macOS 14 or later, 14 day trial.

See this instead of typing it

Bosun lives in your menu bar and shows every open port on your Mac, live, mapped to the process behind it. One-click kill, SIGTERM first. Useful the first time this happens. Genuinely useful the fifth time it happens in one afternoon.