Short answer: PyPy can speed up CPU-bound Django code, but most web apps are I/O-bound, so switching interpreters often buys little — and CPython 3.11+ has already closed much of the gap. PyPy is a fast, JIT-compiled, drop-in replacement for the standard CPython interpreter, and on the right workload it can run pure-Python code several times faster. The catch is that a typical Django request spends most of its time waiting on the database, cache, and network — not executing Python — so the JIT has little to accelerate.
This is a 2026 rewrite of a 2018 post, and the landscape has shifted. The Faster CPython project made CPython 3.11, 3.12, and 3.13 substantially quicker, while PyPy's value is now concentrated in a narrower set of CPU-heavy jobs. Below is an honest, benchmark-first guide to when PyPy is worth it for Django — and when your time is better spent on query optimization, caching, and async.
Key takeaways
- PyPy is a JIT-compiled, CPython-compatible interpreter — it runs the same Django code, just with a tracing just-in-time compiler instead of a plain bytecode loop.
- It shines on CPU-bound, long-running, pure-Python work (data crunching, parsing, simulation, heavy serialization) after a short warm-up period.
- Most Django web requests are I/O-bound (database, cache, external APIs), so PyPy frequently delivers little or no real-world speedup for typical request/response traffic.
- CPython 3.11+ is much faster than the 3.x of 2018, narrowing PyPy's lead for many workloads.
- C extensions are the main compatibility risk. Use
psycopg(psycopg3), notpsycopg2, and check every C-backed dependency before you commit. - Always benchmark with YOUR workload. Generic benchmarks rarely predict your app's result — measure before and after on a realistic load.
What is PyPy and how does it work?
PyPy is an alternative implementation of the Python language that focuses on speed. Where CPython interprets your bytecode instruction by instruction, PyPy ships a tracing just-in-time (JIT) compiler: it watches your program run, finds hot loops, and compiles them down to optimized machine code on the fly. PyPy itself is written in RPython, a restricted subset of Python that its toolchain compiles into the actual interpreter.
The headline points for a Django team evaluating it in 2026:
- It is a drop-in replacement. PyPy targets CPython compatibility, so the vast majority of pure-Python code runs unchanged. As of the 7.3.x series, PyPy supports Python 3.11 syntax and the matching standard library.
- It needs warm-up. The JIT only pays off after it has observed code running enough to compile the hot paths. Short-lived processes — a one-shot management command, a serverless function that cold-starts per request — may finish before the JIT helps, or even run slower because of compilation overhead.
- It optimizes loops, not waiting. PyPy makes Python execute faster. It cannot speed up time your process spends blocked on a database query or an HTTP call.
Where does PyPy actually help a Django app?
This is the question that matters, and the honest answer is it depends on where your request spends its time.
Profile almost any conventional Django view and you will find the wall-clock time dominated by I/O: running SQL and waiting for the database, reading and writing the cache, calling third-party APIs, and serializing responses. The actual Python the interpreter executes is often a small slice of the total. Speeding up that slice with a JIT — even by 3-5x — barely moves a request that is 80% database wait. This is why so many teams try PyPy, see a single-digit-percent change (sometimes a regression), and switch back.
PyPy earns its keep when the bottleneck is CPU and pure Python, for example:
- Long-running data processing, ETL, or report generation in management commands or Celery workers.
- Heavy in-Python computation: parsing, text processing, numeric loops, custom serialization, rule engines, simulations.
- Batch jobs that run long enough for the JIT to warm up and amortize its cost.
If that describes your background workers rather than your web tier, the pragmatic pattern is to run those specific jobs on PyPy while keeping the web app on CPython.
PyPy vs CPython for Django: the picture
Here is how the two interpreters compare for real Django work today. "CPython" here means a current 3.11+ release (3.13 at the time of writing).
| PyPy (7.3.x) | CPython (3.11+) | |
|---|---|---|
| Execution model | Tracing JIT to machine code for hot loops | Bytecode interpreter (experimental JIT in 3.13+) |
| CPU-bound pure Python | Often much faster after warm-up | Baseline; improved a lot since 3.11 |
| I/O-bound web requests | Little to no gain | Same — the bottleneck is the DB/network |
| Startup / short processes | Slower (JIT warm-up cost) | Fast startup |
| Memory | Higher baseline; can be lower under load | Predictable, generally lower at idle |
| C-extension support | Works via cpyext, but slower; some packages unsupported |
Native, first-class |
| Python version coverage | Trails CPython (3.11 as of 7.3.x) | Latest language features first |
| async / ASGI | Supported, but no JIT magic for I/O waits | Supported; the mainstream path |
| Ecosystem default | Niche | What almost every package targets |
Did the Faster CPython project change the math?
Yes — this is the biggest reason a 2018-era "just switch to PyPy" recommendation no longer holds. Starting with Python 3.11, the Faster CPython effort delivered roughly a 10-60% speedup over 3.10 on the standard benchmark suite (around 25% on average), driven by the specializing adaptive interpreter (PEP 659). 3.12 and 3.13 continued the work, and 3.13 shipped an experimental JIT of its own.
The practical consequence: for CPU-bound code where PyPy used to be the obvious answer, modern CPython is now meaningfully closer, while keeping flawless C-extension support and the latest language features. PyPy can still win big on the right workload, but the gap you are buying — and the compatibility cost you are paying for it — is smaller than it was. Measure the current CPython you would otherwise ship, not the one from when the old benchmarks were written.
The catch: C extensions, and psycopg specifically
The number-one practical blocker for PyPy is C-extension packages. Libraries written against CPython's C API run on PyPy through a compatibility layer called cpyext, but that layer adds overhead — C extensions are often slower on PyPy than on CPython, and a few do not work at all. Before committing, audit every dependency that ships compiled code.
The classic Django example is the PostgreSQL driver:
psycopg2is problematic on PyPy. Usepsycopg(psycopg3), which lists PyPy as a supported implementation and is the modern adapter Django supports natively since 4.2 (with connection pooling added in 5.1). Alternatively, the olderpsycopg2cffipackage reimplements the psycopg2 API using CFFI, which PyPy handles far better thancpyext.- Prefer pure-Python or CFFI-based packages where you have a choice — CFFI was co-designed with PyPy and performs well on it.
- Watch the scientific stack. NumPy and friends work but may not be faster on PyPy; if heavy numeric work is your bottleneck, a native library on CPython is often the better tool.
The rule of thumb: the more your dependency tree leans on C extensions, the less attractive — and more fragile — PyPy becomes.
How do I run a Django app on PyPy?
The setup mirrors a normal virtual-environment workflow; you just point the environment at the PyPy binary instead of python. First install PyPy (download a build from pypy.org, or use your package manager or a tool like pyenv), then create a dedicated virtual environment with it.
# 1. Check your PyPy install (a 7.3.x build supporting Python 3.11)
pypy3 --version
# 2. Create a PyPy-backed virtual environment (PyPy ships the venv module)
pypy3 -m venv pypy-env
# 3. Activate it — "python" inside this env is now PyPy
source pypy-env/bin/activate
python --version # -> Python 3.11.x [PyPy 7.3.x ...]# 4. Install Django and the PyPy-friendly PostgreSQL driver.
# Use psycopg (v3), NOT psycopg2, on PyPy.
pip install "Django>=5.0" "psycopg[binary]" gunicorn
# settings.py — psycopg3 uses the standard PostgreSQL backend:
# DATABASES = {
# "default": {
# "ENGINE": "django.db.backends.postgresql",
# "NAME": "mydb", "USER": "me", "PASSWORD": "...",
# "HOST": "localhost", "PORT": "5432",
# }
# }
# 5. Run migrations and serve the app under PyPy.
# gunicorn launched from this venv runs on the PyPy interpreter.
python manage.py migrate
gunicorn myproject.wsgi:application --workers 3 --bind 0.0.0.0:8000
# For long-lived PyPy worker processes, give the JIT time to warm up
# before you trust any latency numbers — early requests pay the JIT cost.How do I benchmark PyPy vs CPython honestly?
Never decide on generic benchmarks — they rarely reflect your routes, your data, or your dependency mix. Run the same realistic workload against the same app on both interpreters and compare. Two rules make the result trustworthy: warm up first (discard the initial requests so PyPy's JIT and your caches are hot), and measure the metric you care about (p95/p99 request latency and throughput for a web tier; total wall-clock for a batch job).
# A) Load-test a running server with the same workload on each interpreter.
# Run it once against a CPython-served instance, once against a PyPy one.
pip install locust # or use: ab, wrk, k6, hey
# Quick throughput/latency check with hey (read p95/p99, not just the mean):
hey -z 60s -c 50 http://localhost:8000/api/heavy-endpoint/
# B) For a CPU-bound batch job, time the whole run on each interpreter.
# The JIT only pays off if the job runs long enough to warm up.
time python manage.py generate_monthly_report
# Compare the numbers. If PyPy is not clearly and repeatably faster on
# YOUR workload, the simpler, more compatible choice is to stay on CPython.Should you use PyPy? A decision guide
| Use PyPy when... | Stick with CPython when... |
|---|---|
| The bottleneck is CPU / pure Python, confirmed by profiling | Requests are I/O-bound (DB, cache, external APIs) — most web apps |
| You run long-lived workers or batch jobs (the JIT warms up) | You rely on short-lived processes or fast cold starts |
| Your hot path is pure Python or CFFI, few C extensions | You depend heavily on C-extension packages |
| You can benchmark a clear, repeatable win on your workload | A quick test shows little or no gain |
| You need raw interpreter speed and can pin to Python 3.11 | You need the latest Python version or language features |
| It is a dedicated service you control end to end | You want the maximum-compatibility, lowest-surprise default |
A faster path for most Django apps
Because the typical Django bottleneck is I/O, the highest-ROI optimizations usually have nothing to do with the interpreter. Before reaching for PyPy, work through these — they help on any interpreter and almost always deliver more than an interpreter swap:
- Fix your queries first. Eliminate N+1 queries, fetch fewer columns, and push work into SQL. See our guide to Django ORM query optimization.
- Cache the expensive, repeated work. A well-placed cache skips the database entirely for hot reads — see implementing Memcached caching in Django.
- Use async for I/O concurrency. Django's ASGI and async support let a request overlap independent I/O waits, which attacks the real bottleneck the JIT cannot touch.
- Tighten the front end too. Faster pages are not only a backend concern — see improving PageSpeed and Core Web Vitals.
Done well, these usually beat switching interpreters — and they keep you on the most compatible, best-supported runtime.
Frequently Asked Questions
Is PyPy faster than CPython for Django?
Only for the right workload. PyPy's JIT can make CPU-bound, pure-Python code several times faster after warm-up, but a typical Django web request is I/O-bound — most of its time is spent waiting on the database, cache, and network, which the JIT cannot speed up. For conventional request/response traffic, many teams see little or no improvement (occasionally a regression). PyPy is most likely to win on long-running data-processing jobs, not the web tier. Always benchmark your own workload before deciding.
Does Django officially support PyPy?
Django is pure Python and runs on PyPy; the project has historically exercised its test suite against PyPy. The practical constraints are your dependencies and the Python version: PyPy's 7.3.x series targets Python 3.11, so you must pin to a Django release that supports 3.11, and every C-extension package in your stack has to work on PyPy. Django itself is rarely the blocker — the database driver and other compiled dependencies usually are.
What database driver should I use with Django on PyPy?
Use psycopg (psycopg3) for PostgreSQL. The older psycopg2 is problematic on PyPy because of how it binds to CPython's C API, whereas psycopg3 lists PyPy as a supported implementation and is the adapter Django supports natively from 4.2 onward. If you must keep the psycopg2 API, psycopg2cffi is a CFFI-based reimplementation that PyPy handles much better than the C extension. In general, prefer pure-Python or CFFI-based drivers on PyPy.
Why might PyPy be slower than CPython?
Three common reasons. First, JIT warm-up: short-lived processes can finish before the JIT compiles the hot paths, so you pay the compilation cost without the payoff. Second, C extensions: libraries using CPython's C API run through PyPy's cpyext compatibility layer, which is often slower than running them on CPython. Third, I/O-bound work: if the request is mostly waiting on the database, there is almost nothing for the JIT to accelerate, so overhead can show through. Profile to see which applies to you.
Did CPython 3.11+ make PyPy unnecessary?
Not unnecessary, but less compelling for many cases. The Faster CPython project made CPython 3.11 roughly 25% faster on average than 3.10 (via PEP 659's specializing adaptive interpreter), and 3.12/3.13 continued the work, with 3.13 adding an experimental JIT. That narrows PyPy's lead on CPU-bound code while CPython keeps perfect C-extension support and the newest features. PyPy can still win on the right pure-Python workload — just measure against the current CPython you would actually deploy.
Can I run PyPy only for background jobs and keep CPython for the web app?
Yes, and it is often the smartest split. Because PyPy's strengths are CPU-bound, long-running tasks, a common pattern is to run Celery workers, ETL, or report-generation jobs on PyPy while serving the I/O-bound web tier on CPython. You get the JIT where it helps and the broadest compatibility where you need it. Keep separate virtual environments and benchmark each job to confirm the worker actually runs faster on PyPy.
Get an honest performance assessment
The right answer to "should we run Django on PyPy?" is almost always "let's measure where the time actually goes first." Across 12+ years and 50+ delivered projects, our team has found that profiling, query tuning, caching, and async deliver bigger, safer wins than an interpreter swap for most apps — and we will tell you honestly when PyPy is the right tool. If you want a Django app that is genuinely fast in production, explore our Django development services and Python development services, and let's find your real bottleneck together.