Backends and environment setup

What a backend actually is, how to choose between interactive and headless, and how to make charts work in notebooks, servers and CI.

What a backend is

Matplotlib separates the plot description from the thing that draws it. The backend is the renderer: an interactive one opens a window (Qt, Tk, macOS), a non-interactive one writes a file (Agg). Every "my plot does not appear" problem is really a backend question.

import matplotlib
matplotlib.use("Agg")          # must run BEFORE pyplot is imported
import matplotlib.pyplot as plt

matplotlib.get_backend()       # what is active right now

# inside a Jupyter notebook, instead of a backend call
# %matplotlib inline           # static PNG in the output cell
# %matplotlib widget           # interactive canvas, needs ipympl installed
BackendProducesUse for
AggPNG/PDF/SVG filesScripts, servers, CI, batch jobs
QtAgg / TkAggA windowLocal exploration and pan/zoom
inline (notebook)Static image in the cellReports and tutorials
widget / ipymplLive canvas in the cellInteractive analysis in Jupyter
pdf, svg, psVector files onlyPrint pipelines

Choosing a backend per environment

# headless machine: force a file backend before Python starts
export MPLBACKEND=Agg

# one-off script
MPLBACKEND=Agg python make_charts.py

# a GUI toolkit must be installed for interactive backends
pip install matplotlib PyQt6      # or use tkinter from the standard library
pip install ipympl                # for the widget backend in Jupyter
  • In a script, call matplotlib.use("Agg") before importing pyplot and forget about it.
  • In a notebook, use the %matplotlib magic rather than a backend call.
  • Set MPLBACKEND in CI so a missing display cannot fail the build.
  • If a window flashes and vanishes, the script ended before the GUI loop ran: call plt.show() last.
  • Interactive backends are for you, not for the report you are producing.

Configuration and matplotlibrc

matplotlib.matplotlib_fname()   # the config file actually in use
matplotlib.get_configdir()      # where your user config lives

# a matplotlibrc file, applied to every script on this machine
# backend: Agg
# figure.figsize: 7, 4.5
# figure.dpi: 120
# savefig.dpi: 200
# font.size: 10
⚠️
Importing pyplot selects a backend immediately. Calling matplotlib.use() after that may appear to work and then fail. Put the call at the very top of the file, before any pyplot import.

FAQ

Why does my script work locally but crash in CI?
CI has no display, so an interactive backend fails on start. Set MPLBACKEND=Agg or call matplotlib.use("Agg") before importing pyplot.
Should I edit matplotlibrc or set rcParams?
Use rcParams in code so the settings travel with the script and are reviewable. Reserve matplotlibrc for machine-wide preferences such as the backend.

Publication quality: DPI, vector formats and layout Debugging plots: empty axes, missing data and overlap

Last refreshed 2026-09-18.