Fix GeoDjango "Could Not Find the GDAL Library" in Production and Docker

Fix the GeoDjango ImproperlyConfigured error could not find the GDAL library (tried gdal, GDAL...) on servers and in Docker: install GDAL, GEOS, and PROJ system packages, set GDAL_LIBRARY_PATH when needed, and match library versions to Django.

Problem statement

A GeoDjango project runs on your Mac but fails on the server or in Docker:

django.core.exceptions.ImproperlyConfigured: Could not find the GDAL library
(tried "gdal", "GDAL", "gdal3.9.0", "gdal3.8.0", ...). Is GDAL installed?
If it is, try setting GDAL_LIBRARY_PATH in your settings.

GeoDjango (django.contrib.gis) loads the GDAL, GEOS, and PROJ C libraries with ctypes at startup. They are system libraries, not Python packages, so pip install -r requirements.txt does not provide them. A slim Docker image or a fresh server has none of them until you install the OS packages.

Why GeoDjango failscontrib.gis loadsvia ctypesLooks for libgdalknown namesNot installedslim imageConfig errorstartup stopsGDAL, GEOS, and PROJ are system libraries that pip does not install.
  1. Debian or Ubuntu: apt-get install -y gdal-bin libgdal-dev libgeos-dev libproj-dev (runtime only needs gdal-bin or libgdal3x, libgeos-c1v5, proj-bin).
  2. Docker on python:3.x-slim: add those packages in the Dockerfile before installing Python requirements.
  3. If GDAL is installed in a non-standard place, set GDAL_LIBRARY_PATH and GEOS_LIBRARY_PATH in settings.
  4. Make sure the GDAL version is one your Django release supports; very new GDAL may need a newer Django.
  5. Check the same for GEOS: the related error is "Could not find the GEOS library".

Step-by-step solution

1. Check what is installed

gdal-config --version 2>/dev/null || echo "no gdal-config"
ldconfig -p | grep -E "libgdal|libgeos_c|libproj"

No libgdal.so lines means GDAL is missing. Django searches for library names it knows; a version it does not list will not be found automatically.

2. Install the system packages

On Debian 12 or Ubuntu 24.04:

sudo apt-get update
sudo apt-get install -y gdal-bin libgdal-dev libgeos-dev libproj-dev

The -dev packages are only needed to build Python packages that compile against GDAL (such as the GDAL Python bindings or rasterio from source). GeoDjango itself only needs the shared libraries.

python:3.12-slim baseFROM python:3.12-slimRUN apt-get update && apt-get install -y --no-install-recommends \gdal-binlibgeos-c1v5libproj25 \&&rm -rf/var/lib/apt/lists/*1231Pulls in the libgdal shared library.2GEOS C API used by geometry fields.3Keeps the layer small.Runtime packages only; add -dev packages in a builder stage if you compiledjango-deployment.com
Exact package names change with the Debian release; check them for your base image.

3. Install in Docker

FROM python:3.12-slim
RUN apt-get update \
    && apt-get install -y --no-install-recommends gdal-bin libgeos-c1v5 libproj25 \
    && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .

Package names include version numbers (libproj25, libgeos-c1v5) that differ between Debian releases. gdal-bin depends on the right ones, so installing it alone often suffices. Images such as ghcr.io/osgeo/gdal include everything but are larger.

4. Point Django at the library if needed

When GDAL comes from a custom build, Conda, or a version Django does not know by name:

GDAL_LIBRARY_PATH = os.environ.get("GDAL_LIBRARY_PATH")   # e.g. /usr/lib/x86_64-linux-gnu/libgdal.so.34
GEOS_LIBRARY_PATH = os.environ.get("GEOS_LIBRARY_PATH")   # e.g. /usr/lib/x86_64-linux-gnu/libgeos_c.so.1

Use the full path to the shared object file. Find it with ldconfig -p | grep libgdal. Setting these to None lets Django search as usual.

5. Match versions

Each Django release supports a range of GDAL versions and lists the library names it tries. A brand-new GDAL may not be in the list of an older Django; either set GDAL_LIBRARY_PATH explicitly or upgrade Django. Also keep the PostGIS server version supported by your Django version; that is separate from the client libraries.

error and fixError saysCause and fixcould not find GDALnot installed; apt-getinstall gdal-bincould not find GEOSlibgeos_c missing;install libgeos-c1v5installed, still not foundunknown version name;set GDAL_LIBRARY_PATHworks locally onlyHomebrew on Mac; image lacks the libspip cannot fix these; they are operating system packagesdjango-deployment.com
Build the image in CI to catch missing libraries before deploying.

Explanation

django.contrib.gis.gdal and django.contrib.gis.geos wrap the C libraries through ctypes.util.find_library and a list of known file names. On macOS, Homebrew or Postgres.app usually installed them long ago, so development works. Production servers and slim containers start minimal, and the first import of a GIS model or field fails. Because the check runs when Django loads django.contrib.gis, the whole app fails at startup, not only geographic views.

Edge cases / notes

  • Alpine. Alpine provides gdal and geos packages, but musl and version differences cause more trouble; Debian-based slim images are easier.
  • Managed platforms. Heroku needs the heroku-geo-buildpack or a Docker deploy; Render and Railway work with a Dockerfile.
  • Raster support. GDALRaster and raster fields need GDAL built with the right drivers; the Debian package includes common ones.

Next steps

FAQ

Can I pip install GDAL?

The GDAL Python package provides bindings but needs the system library to build and run. GeoDjango needs the system library itself.

Why does it work on my Mac?

Homebrew or Postgres.app installed GDAL and GEOS. The server or image does not have them.

Do I need libgdal-dev in production?

No, only to compile packages against GDAL. Runtime needs the shared libraries.

What is GDAL_LIBRARY_PATH for?

It tells Django exactly which shared library file to load when automatic search fails.

Keep going