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.

Installing the Postgres driverpip installpsycopgBinary wheelbundled libpqSource buildneeds headersRuntimelibpq availableBinary wheels avoid build tools; source builds link against the system libpq.

Quick answer

  1. Decide between psycopg 3 (psycopg) and psycopg2; new projects should use psycopg 3, which Django supports since 4.2.
  2. For the quickest fix, install the binary package: psycopg[binary] or psycopg2-binary. It bundles libpq in a wheel.
  3. If you need a source build (for example for a specific libpq or platform), install libpq-dev (Debian/Ubuntu) or libpq-devel (RHEL), plus build-essential and Python headers.
  4. For psycopg 3 without the binary extra, install the system libpq5 runtime library.
  5. In Docker, install build packages in a builder stage and only libpq5 in the final image, or use the binary wheel.
  6. 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

PackageWhat it isNeeds at installNeeds at runtime
psycopg[binary]psycopg 3 with precompiled C extension and bundled libpqnothingnothing
psycopg[c]psycopg 3 C extension built locallycompiler, Python headers, libpq-devlibpq5
psycopg (no extra)psycopg 3 pure Python implementationnothingsystem libpq5
psycopg2-binarypsycopg2 with bundled libpqnothingnothing
psycopg2psycopg2 built from sourcecompiler, Python headers, libpq-devlibpq5
which psycopg package is whichNeeds a compilerNeeds libpq on the systempsycopg[binary]✓no✓no, bundledpsycopg[c]!yes!yes, plus headerspsycopg (pure)✓no!yes, at runtimepsycopg2-binary✓no✓no, bundledpsycopg2!yes!yes, plus headersBinary packages avoid build tools; source builds use the system libpqdjango-deployment.com
Django 4.2 and later support psycopg 3 directly.

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]"
build errors, decodedErrorMissingpg_config executable not foundlibpq-dev (Debian) /libpq-devel (RHEL)Python.h: No such filepython3-dev / python3-develgcc: command not foundbuild-essential / gccno pq wrapper availablelibpq at runtime, or the binary extraOr skip all of it with the binary packagedjango-deployment.com
Match the package names to your distribution family.
  • libpq-dev provides the headers and the pg_config program the build uses to locate them.
  • python3-dev provides Python headers for the version installed by the OS. If your virtualenv uses a different Python (for example 3.13 from deadsnakes), install the matching python3.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 . .
keep compilers out of the runtime imageSingle stage with build tools✕gcc and headers stay in the image✕hundreds of MB larger✕more packages to patchMulti-stage or binary wheel✓build wheels in a builder stage✓runtime gets libpq5 only✓or psycopg[binary], no build at allSlim images lack build tools on purposedjango-deployment.com
Alpine needs musl wheels; slim Debian images are simpler.

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 libpq version number
  • check --database default passes
  • showmigrations connects 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-bookworm rather than python: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 install on 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

SituationRecommendation
Local development, CIpsycopg[binary] for speed and simplicity
Standard Linux servers and containerspsycopg[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 releasespsycopg[c] with the OS or PostgreSQL project libpq
Unusual platforms without wheelsbuild from source with development headers
binary or source build?Do you need the system libpq or its features?no: most appspsycopg[binary]fast, no build toolsyes: shared OpenSSL, GSSAPIpsycopg[c]build against system libpqThe psycopg docs prefer local builds for production; binary works well for mostdjango-deployment.com
Pin the choice in requirements so every environment matches.

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-dev for the system Python while the virtualenv uses a different Python version.
  • Keeping psycopg2-binary pinned in one requirements file and psycopg in another.

Next steps

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.

Keep going