Fix psycopg Installation Errors on Django Servers
Fix psycopg and psycopg2 install failures on servers and Docker images: pg_config not found, missing libpq, binary versus source builds, and missing wheels.
Problem statement
Installing dependencies for a Django project fails on a server, in CI, or during a Docker build with errors like:
Error: pg_config executable not found.
pg_config is required to build psycopg2 from source. Please add the directory
containing pg_config to the $PATH or specify the full executable path with the
option:
python setup.py build_ext --pg-config /path/to/pg_config build ...
or:
ImportError: no pq wrapper available.
Attempts made:
- couldn't import psycopg 'c' implementation: No module named 'psycopg_c'
- couldn't import psycopg 'binary' implementation: No module named 'psycopg_binary'
- couldn't import psycopg 'python' implementation: libpq library not found
Django's PostgreSQL backend needs a driver: psycopg (version 3, recommended for current Django) or psycopg2. Both ultimately use libpq, PostgreSQL's C client library. Installation fails when pip must compile the driver from source without build tools, or when the pure-Python variant cannot find libpq at runtime.
Quick answer
- Decide between psycopg 3 (
psycopg) andpsycopg2; new projects should use psycopg 3, which Django supports since 4.2. - For the quickest fix, install the binary package:
psycopg[binary]orpsycopg2-binary. It bundleslibpqin a wheel. - If you need a source build (for example for a specific
libpqor platform), installlibpq-dev(Debian/Ubuntu) orlibpq-devel(RHEL), plusbuild-essentialand Python headers. - For psycopg 3 without the binary extra, install the system
libpq5runtime library. - In Docker, install build packages in a builder stage and only
libpq5in the final image, or use the binary wheel. - Check that a wheel exists for your Python version and architecture; very new Python releases sometimes lack wheels for a short time.
Step-by-step solution
1. Understand the package variants
| Package | What it is | Needs at install | Needs at runtime |
|---|---|---|---|
psycopg[binary] | psycopg 3 with precompiled C extension and bundled libpq | nothing | nothing |
psycopg[c] | psycopg 3 C extension built locally | compiler, Python headers, libpq-dev | libpq5 |
psycopg (no extra) | psycopg 3 pure Python implementation | nothing | system libpq5 |
psycopg2-binary | psycopg2 with bundled libpq | nothing | nothing |
psycopg2 | psycopg2 built from source | compiler, Python headers, libpq-dev | libpq5 |
The psycopg documentation recommends the binary packages for development and for deployments where the bundled libpq is acceptable, and building locally when you need control over libpq (for example to pick up a newer version with specific fixes). Many production deployments run psycopg[binary] without issues.
2. Quick fix: use the binary package
In requirements.txt:
psycopg[binary]==3.2.10
Then reinstall:
pip install -r requirements.txt
python -c "import psycopg; print(psycopg.__version__, psycopg.pq.__impl__)"
psycopg.pq.__impl__ prints binary, c, or python, confirming which implementation loaded.
Django's django.db.backends.postgresql engine works with psycopg 3 automatically; no settings change is needed when switching from psycopg2.
3. Build from source on Debian or Ubuntu
sudo apt update
sudo apt install -y build-essential python3-dev libpq-dev
pip install "psycopg[c]"
libpq-devprovides the headers and thepg_configprogram the build uses to locate them.python3-devprovides Python headers for the version installed by the OS. If your virtualenv uses a different Python (for example 3.13 from deadsnakes), install the matchingpython3.13-dev.
Confirm pg_config is found:
which pg_config && pg_config --version
4. Build from source on RHEL, Rocky, or Alma
sudo dnf install -y gcc python3-devel libpq-devel
pip install "psycopg[c]"
On some systems pg_config is provided by a PostgreSQL development package from the PostgreSQL project repositories and lives in a versioned directory such as /usr/pgsql-16/bin. Add it to PATH for the install:
PATH=/usr/pgsql-16/bin:$PATH pip install "psycopg[c]"
5. Pure Python psycopg needs libpq at runtime
If you install plain psycopg without extras, it imports fine only when the system has libpq:
sudo apt install -y libpq5
python -c "import psycopg; print(psycopg.pq.__impl__)"
The ImportError: no pq wrapper available message lists each implementation it tried; libpq library not found for the Python implementation means libpq5 is missing.
6. Docker images
Slim Python images do not include compilers or libpq. Two good patterns:
Binary wheel (simplest):
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt # includes psycopg[binary]
COPY . .
Source build with a small runtime image:
FROM python:3.13-slim AS builder
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential libpq-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.13-slim
RUN apt-get update && apt-get install -y --no-install-recommends libpq5 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/* && rm -rf /wheels
COPY . .
The builder compiles wheels with the headers; the final image contains only the runtime library.
Alpine-based images use musl instead of glibc, and binary wheels built for glibc do not apply. Building on Alpine needs postgresql-dev and build-base, and results in slower builds. Debian-based slim images are usually the easier choice for Django.
7. Python version and architecture wheels
Binary packages are published as wheels per Python version, operating system, and CPU architecture. If pip starts compiling from source unexpectedly, it did not find a matching wheel:
pip download --no-deps --only-binary=:all: "psycopg-binary==3.2.10" -d /tmp/wheel-check
python -c "import sys, platform; print(sys.version, platform.machine())"
The [binary] extra installs a separate psycopg-binary distribution, which is the package that carries the compiled wheels, so that is the one to check. If no wheel exists (for example on a brand-new Python release or an uncommon architecture), use a supported Python version, or build from source with the steps above.
8. Migrating from psycopg2 to psycopg 3
For most Django projects the switch is a dependency change:
pip uninstall -y psycopg2 psycopg2-binary
pip install "psycopg[binary]"
python manage.py check --database default
Code that imports psycopg2 directly (raw connections, psycopg2.extras, exception classes) needs updating to psycopg 3 APIs. Django-level code using the ORM or connection.cursor() usually needs no changes.
9. Verify
python -c "import psycopg; print(psycopg.__version__, psycopg.pq.__impl__, psycopg.pq.version())"
python manage.py check --database default
python manage.py showmigrations | head -5
Verification check:
- the import prints the psycopg version, implementation, and
libpqversion number check --database defaultpassesshowmigrationsconnects to the database and lists migrations
Explanation
PostgreSQL drivers are thin Python layers over libpq, so installing them involves both Python packaging and system libraries. Binary wheels package everything, including libpq, which removes build dependencies at the cost of shipping a bundled library version. Source builds link against the system libpq, which requires development headers at build time and the runtime library at run time. Nearly every installation error is one of those requirements missing in the environment where pip runs.
Edge cases / notes
Both psycopg2 and psycopg installed
Django prefers psycopg 3 when both are installed. Remove the unused one to avoid confusion about which driver is active.
Managed platforms
Heroku, Render, and similar platforms install binary wheels without extra system packages. If a source build fails there, switch to the binary package.
SSL and libpq versions
TLS features for database connections (such as sslmode=verify-full with newer certificate types) depend on the libpq version. Check psycopg.pq.version() when debugging TLS behavior differences between environments.
Keeping driver installs reproducible
Driver installation problems often reappear when an environment is rebuilt months later. A few habits prevent that:
- Pin the driver version in your dependency file or lockfile, and upgrade deliberately.
- Pin the base image by version (
python:3.13-slim-bookwormrather thanpython:3-slim), so the OS libraries and available wheels do not change underneath you. - Build once, deploy the artifact. Build a wheelhouse or image in CI and deploy it, instead of running
pip installon every production server. - Check the driver in CI with the same import command used in step 9, so a dependency change that drops the binary extra fails before deploy.
Checking which libpq a process actually loaded
When behavior differs between environments (for example TLS options or connection parameters), confirm the library version and implementation from inside the running application's environment:
python - <<'PY'
import psycopg
from psycopg import pq
print("psycopg", psycopg.__version__)
print("implementation", pq.__impl__)
print("libpq version", pq.version())
print("build libpq version", getattr(pq, "__build_version__", "n/a"))
PY
pq.version() returns the libpq version as an integer, for example 170002 for 17.2. With the binary package it reflects the bundled library; with the C or Python implementation it reflects the system library.
Choosing between binary and source builds
| Situation | Recommendation |
|---|---|
| Local development, CI | psycopg[binary] for speed and simplicity |
| Standard Linux servers and containers | psycopg[binary] is widely used; switch to psycopg[c] if you need the system libpq |
Need a specific libpq feature or security patch faster than binary releases | psycopg[c] with the OS or PostgreSQL project libpq |
| Unusual platforms without wheels | build from source with development headers |
Whichever you choose, use the same variant in staging and production so connection behavior matches.
Common mistakes
- Installing
psycopg2(source) in a slim Docker image without build tools. - Adding compilers to the final production image instead of using a builder stage.
- Installing
python3-devfor the system Python while the virtualenv uses a different Python version. - Keeping
psycopg2-binarypinned in one requirements file andpsycopgin another.
Next steps
- Configure the connection itself with How to Connect Django to PostgreSQL in Production.
- Keep images lean with Build Smaller, Faster Django Docker Images.
- If the package installs but imports fail at runtime, see Fix ModuleNotFoundError When Starting Django with Gunicorn.
FAQ
Should Django use psycopg or psycopg2?
New projects should use psycopg 3 (psycopg), supported by Django since 4.2. psycopg2 still works but is in maintenance, and psycopg 3 is where new features land.
How do I fix "pg_config executable not found"?
Either install the binary package (psycopg[binary] or psycopg2-binary), or install PostgreSQL development headers such as libpq-dev plus a compiler and Python headers before building from source.
Is psycopg binary safe to use in production?
Many deployments use it successfully. The psycopg documentation suggests building locally when you need control over the bundled libpq version; otherwise the binary package is a practical choice.
Why does psycopg say "no pq wrapper available"?
None of its implementations could load: the binary and C extensions are not installed, and the pure Python implementation could not find the system libpq library. Install psycopg[binary] or the libpq5 package.