Install QGIS with Python Bindings on Linux
On Linux, QGIS's Python bindings are part of the distribution's package system rather than a self-contained installer, which is both an advantage — python3 can import qgis.core directly, no wrapper scripts needed — and a source of confusion: distribution repositories often carry old versions, packages are split in non-obvious ways, and installing extra Python packages with pip can collide with the system. A few deliberate choices avoid nearly all of it.
This recipe belongs to Virtual Environments for GIS. It compares the installation routes, installs QGIS LTR from the official repositories on Debian and Ubuntu, covers Fedora, conda-forge and Flatpak, verifies the bindings, adds Python packages safely, and runs a first headless script.
Prerequisites
- A 64-bit Linux distribution: Debian 12 or 13, Ubuntu 22.04 or 24.04, Fedora, or any distribution for the conda route.
- Administrator rights for system packages, or none for conda.
Choose LTR or latest
QGIS has two release lines. The long-term release (LTR) gets bug fixes for a year and is what organisations standardise on; the latest release has new features every four months. For scripts and plugins that others will run, target the LTR — currently the 3.40 series — and test on the latest.
Install from the official repositories (Debian and Ubuntu)
The QGIS project publishes packages for current Debian and Ubuntu releases. Adding its repository gives up-to-date QGIS with Python bindings for the system's python3.
sudo apt install gnupg software-properties-common wget
sudo mkdir -m755 -p /etc/apt/keyrings
sudo wget -O /etc/apt/keyrings/qgis-archive-keyring.gpg https://download.qgis.org/downloads/qgis-archive-keyring.gpg
# LTR line; use https://qgis.org/debian for the latest release instead
cat <<EOF | sudo tee /etc/apt/sources.list.d/qgis.sources
Types: deb deb-src
URIs: https://qgis.org/debian-ltr
Suites: $(lsb_release -cs)
Architectures: amd64
Components: main
Signed-By: /etc/apt/keyrings/qgis-archive-keyring.gpg
EOF
sudo apt update
sudo apt install qgis qgis-plugin-grass python3-qgis
Breakdown: The signing key verifies packages; the sources file points apt at the LTR repository for your release's codename. On Ubuntu, use https://qgis.org/ubuntu-ltr instead of the Debian URL. python3-qgis is the package with the Python bindings — it is pulled in by qgis, but naming it makes the intent explicit, and on servers you can install it without the desktop application. qgis-plugin-grass adds the GRASS Processing provider. Check the current instructions on qgis.org before automating this, as repository paths occasionally change between releases.
Verify that Python sees QGIS
The test that matters is whether the system Python can import QGIS and initialise it.
python3 - <<'EOF'
import os
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from qgis.core import Qgis, QgsApplication
app = QgsApplication([], False)
app.initQgis()
print("QGIS", Qgis.version())
import processing
from processing.core.Processing import Processing
Processing.initialize()
print(len(QgsApplication.processingRegistry().algorithms()), "Processing algorithms")
app.exitQgis()
EOF
Breakdown: On a distribution install, qgis is a normal package in the system's dist-packages, so no PYTHONPATH or prefix setup is needed. Setting QT_QPA_PLATFORM=offscreen lets the check run over SSH without a display. Initialising Processing and counting algorithms confirms that providers load; a count in the hundreds is normal. If the import fails, the bindings are missing (python3-qgis not installed) or a different python3 — a virtual environment, a pyenv shim — is being used.
Add Python packages without breaking the system
Recent Debian and Ubuntu releases mark the system Python as externally managed, so pip install into it is refused. Three approaches work.
# 1. distribution packages, where available
sudo apt install python3-pandas python3-geopandas python3-matplotlib
# 2. a virtual environment that can see system packages, including qgis
python3 -m venv --system-site-packages ~/venvs/pyqgis
source ~/venvs/pyqgis/bin/activate
pip install scikit-learn rasterio
python -c "import qgis.core, sklearn; print('ok')"
# 3. per-user installs for QGIS Desktop plugins (last resort)
pip install --user --break-system-packages some-package
Breakdown: Distribution packages are the safest, built against the same libraries as QGIS. A virtual environment created with --system-site-packages sees QGIS from the system while installing extra packages into its own folder — the best general solution for scripts, as described in installing Python packages into QGIS. Packages with compiled GDAL dependencies, such as rasterio and fiona, must be compatible with the system GDAL; prefer distribution packages for those, or the conda route. Breaking the system-packages guard is a last resort for desktop use.
Other routes: Fedora, conda-forge and Flatpak
Other distributions and setups have their own sensible routes.
# Fedora
sudo dnf install qgis python3-qgis qgis-grass
# conda-forge: isolated environment with a pinned version
conda create -n qgis340 -c conda-forge qgis=3.40 python=3.12
conda activate qgis340
python -c "from qgis.core import Qgis; print(Qgis.version())"
# Flatpak: desktop application, sandboxed
flatpak install flathub org.qgis.qgis
Breakdown: Fedora packages QGIS in its own repositories, usually current. Conda-forge provides any QGIS version in an isolated environment with its own Python, GDAL and PROJ — ideal for reproducible analysis, CI and mixing with scientific packages, covered in using PyQGIS with conda and mamba. Flatpak runs QGIS Desktop in a sandbox with its own Python inside; scripts run from the system cannot import that QGIS, so Flatpak suits desktop users more than developers.
Keep the installation current and pinned
An LTR receives point releases with bug fixes roughly monthly. Desktop users want them; servers running scheduled jobs want them only after testing. Apt can do both.
# server: hold QGIS at a tested version
apt-cache policy python3-qgis | head -5
sudo tee /etc/apt/preferences.d/qgis-pin > /dev/null << 'PIN'
Package: qgis* python3-qgis* libqgis*
Pin: version 1:3.40.11*
Pin-Priority: 1001
PIN
sudo apt update && apt-cache policy python3-qgis | head -3
Breakdown: apt-cache policy shows the installed and candidate versions. An apt preference with a priority above 1000 holds every QGIS package at the pinned version — including the matching libraries, so they never get out of step — while the rest of the system keeps updating. The version string here is illustrative; copy the exact one from the policy output. When a new point release has passed your tests, edit the pin and upgrade deliberately. This is the Linux equivalent of the pinned container images in running PyQGIS in a Docker container.
Run a first headless script
With the bindings verified, a standalone script follows the usual pattern: start the application, do the work, exit cleanly.
#!/usr/bin/env python3
import os, sys
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
from qgis.core import QgsApplication, QgsVectorLayer
app = QgsApplication([], False)
app.initQgis()
try:
layer = QgsVectorLayer(sys.argv[1], "input", "ogr")
if not layer.isValid():
raise SystemExit(f"cannot open {sys.argv[1]}")
print(layer.featureCount(), "features in", layer.crs().authid())
finally:
app.exitQgis()
Breakdown: The try/finally ensures exitQgis runs even when the script fails, which avoids crashes at interpreter shutdown. Running it as python3 count.py /data/roads.gpkg needs no environment setup beyond the offscreen platform. For scheduled runs, see scheduling PyQGIS scripts with cron; for the general structure of standalone scripts, running Python scripts outside QGIS Desktop.
QGIS version compatibility
The official repositories provide QGIS 3.40 LTR and the current latest release for supported Debian and Ubuntu versions; QGIS 4 packages follow the same repository structure as they become available. Python bindings require the Python version the distribution ships; conda-forge allows choosing it. Check qgis.org's installation page for current repository URLs and supported releases.
Troubleshooting
ModuleNotFoundError: No module named 'qgis'.python3-qgisis missing, or a different Python (venv without system packages, pyenv) is active.- "externally-managed-environment" from pip. Use distribution packages or a venv with
--system-site-packages. - "could not connect to display". Set
QT_QPA_PLATFORM=offscreenfor headless runs. - Old QGIS version after install. The distribution repository was used instead of qgis.org; check
apt policy qgis.
Conclusion
Install QGIS LTR from the official qgis.org repositories on Debian and Ubuntu (or Fedora's own packages), verify that python3 imports qgis.core, reports the right version and starts headless with Processing, add Python packages through distribution packages or a venv with system site packages, use conda-forge for isolated, pinned environments, and avoid Flatpak for scripting.
Frequently Asked Questions
Can I have LTR and latest installed together? Not from the system repositories; use conda environments for side-by-side versions.
Which Python version does QGIS use on Linux?
The distribution's default python3 — 3.11 on Debian 12, 3.12 on Ubuntu 24.04. Scripts must run with that interpreter, or with a venv built from it.
Do I need the desktop application on a server?
No — python3-qgis and the server or provider packages suffice for headless scripts.
Why does my IDE not find qgis?
It uses a different interpreter; point it at /usr/bin/python3 or the venv created with system site packages, as in configuring VS Code for PyQGIS.
How do I remove a broken installation cleanly?
Purge the packages with sudo apt purge 'qgis*' 'python3-qgis*' 'libqgis*', remove the repository file, then reinstall. User profiles in ~/.local/share/QGIS are untouched, so settings and plugins survive.
Are nightly builds available? Yes, from qgis.org's nightly repositories, for testing upcoming changes.