The Django development server runs as a foreground process; pressing Ctrl+C in the same terminal stops it. For background runs (CI, parallel terminals, automation), the team that runs the server in the background needs to kill it by PID or by port. The team that uses runserver --noreload knows what they’re doing; the team that uses nohup or backgrounding without tracking the PID has orphaned servers eating ports.
Table of contents
- The simple case: foreground server
- Killing a background server by PID
- Killing a server by port
- Common scenarios
- Automation patterns
- FAQ
The simple case: foreground server
When the team runs python manage.py runserver in the foreground, Ctrl+C stops the server. This works in every terminal - bash, zsh, fish, PowerShell. The team that runs the dev server in a dedicated terminal window has the simplest workflow: Ctrl+C when done.
The reload watcher is a separate process. Django’s auto-reload spawns a watcher that monitors file changes; Ctrl+C stops both. With --noreload, only the server runs; Ctrl+C stops only the server. The team that runs without reload has fewer moving parts and faster startup.
Django 5.2 added a startup warning that the dev server is not for production. The warning reminds the team that runserver is single-threaded, slow, and insecure. The team that takes the warning seriously runs gunicorn or uvicorn in production; the team that ignores it ships a single-threaded server and gets paged when traffic spikes.
Killing a background server by PID
Capture the PID at start time and kill it at stop time. The team that runs python manage.py runserver & captures $! as the PID, saves it to a file, and kills it later. Pattern: python manage.py runserver & echo $! > django.pid; kill $(cat django.pid).
Use kill with the right signal. kill <pid> sends SIGTERM, which Django’s runserver catches and shuts down cleanly. kill -9 <pid> sends SIGKILL, which Django cannot catch and may leave resources half-closed. The team that uses SIGTERM has clean shutdowns; the team that uses SIGKILL sometimes has port-in-use errors on restart.
On Windows, kill does not exist by default. The team on Windows uses PowerShell’s Stop-Process -Id <pid> or taskkill /PID <pid>. The same PID-tracking pattern works; the kill command differs by OS.
Killing a server by port
Find the PID listening on port 8000 (Django’s default) and kill it. On Linux: lsof -ti:8000 | xargs kill. On macOS: lsof -ti:8000 | xargs kill (lsof ships with Xcode Command Line Tools). On Windows: netstat -ano | findstr :8000 then taskkill /PID <pid>.
The team that uses fuser -k 8000/tcp on Linux has an alternative. fuser finds and kills the process listening on a port. The team that does not have lsof installed can install psmisc (which provides fuser) or use netstat.
The pattern: lsof -ti:8000 | xargs -r kill. The -r flag tells xargs to not run kill if lsof returns no PIDs (empty stdin). The team that uses this pattern has a one-liner that gracefully handles ‘no server running’.
Common scenarios
Multiple servers on different ports. The team that runs runserver 8000, runserver 8001, etc. needs to kill by the specific port. lsof -ti:8000 returns the PID for that port only.
Server in a tmux/screen session. The team that runs Django in a tmux window has to either reattach to the tmux session and Ctrl+C, or kill the process from outside tmux using PID or port. The team that uses tmux for long-running dev processes benefits from named sessions (tmux new -s django) for easy attach.
Server in a Docker container. The team that runs Django in Docker has the container as the kill target: docker stop <container_id>. The team that uses Docker Compose: docker compose stop web. The team that uses Kubernetes: kubectl delete pod <pod> (the deployment will recreate it - use this only when terminating the deployment).
Automation patterns
Use a Makefile target for dev server start/stop. make dev starts the server in the background with PID tracking; make stop-dev kills it. The team that uses Make targets has a discoverable interface for the team.
Use django-extensions for runserver_plus. runserver_plus from django-extensions uses the Werkzeug debugger and adds features like SSL support. The team that needs Werkzeug debugger or SSL on the dev server installs django-extensions and uses runserver_plus.
Use a process manager for production-like local dev. honcho, foreman, or supervisord run multiple processes and track PIDs. The team that runs Django + Celery worker + Redis locally uses a Procfile and honcho start to manage all three with PID tracking built in.
FAQ
Why does Ctrl+C not stop Django in some terminals?
On Windows cmd or PowerShell, Ctrl+C works the same as Linux. In tmux/screen, Ctrl+C sends SIGINT to the foreground process - works the same. The team that has Ctrl+C not working is usually running Django through a wrapper that swallows signals (nohup, &, or a tmux pane in detached mode). The fix: kill the wrapper or attach to it.
How do I run Django on a different port?
python manage.py runserver 8080 runs on port 8080. The team that runs multiple projects locally assigns different ports to each: 8000 for project A, 8001 for project B. The kill-by-port pattern works for any port.
Can I run multiple Django servers on the same port?
No. The second server fails to start with ‘That port is already in use.’ The team that wants to run multiple servers uses different ports. The team that hits this error has an orphaned server - kill it by PID or port first.
Why does Django print ‘Quit the server with CONTROL-C.’?
This is the standard message Django prints at startup. It reminds the team that the server is stopped with Ctrl+C. The team that runs in a CI environment where Ctrl+C is not available kills the process by PID instead.
How do I run Django on 0.0.0.0 instead of 127.0.0.1?
python manage.py runserver 0.0.0.0:8000 binds to all interfaces, accessible from other machines on the network. The team that develops in Docker or WSL uses 0.0.0.0 so the host machine can reach the server. WARNING: this exposes the dev server to the network; do not use in production.
How do I run Django with HTTPS locally?
runserver_plus --cert /tmp/cert.crt --key /tmp/cert.key from django-extensions generates a self-signed cert and runs HTTPS. The team that needs to test HTTPS-specific behavior (secure cookies, HSTS) uses this. Without django-extensions, manually generate certs and configure SSL.
How this fits the rest of the stack
For a sense of what the full project costs before it commits, the RunxBuild hosting calculator shows the line items together. The API, the database, the storage, the worker, the bandwidth - each one is a separate number, and the team’s mental model for the platform is the sum of those numbers.
Useful related references: