How to set the Matplotlib backend for headless scripts

Matplotlib can draw figures through desktop windows or through file-only renderers. Headless scripts need a file-rendering backend so scheduled jobs, SSH sessions, CI workers, and containers can save plots without trying to open a GUI window.

The Agg backend renders PNG output without a display server. Select it before importing matplotlib.pyplot, because pyplot initializes the backend when it creates figures or connects to a plotting session.

Use a script-level backend selection when the script always runs without a desktop. MPLBACKEND can override the backend for one launch, but avoid putting it in shell startup files because it overrides local matplotlibrc choices and can surprise interactive sessions.

Steps to set Matplotlib backend for headless scripts:

  1. Add the Agg backend selection before importing matplotlib.pyplot.
    headless_plot.py
    from pathlib import Path
     
    import matplotlib
    matplotlib.use("Agg")
     
    import matplotlib.pyplot as plt
     
    output = Path("headless-plot.png")
    fig, ax = plt.subplots(layout="constrained")
    ax.plot([1, 2, 3, 4], [2, 4, 3, 5], marker="o")
    ax.set_title("Headless Matplotlib")
    ax.set_xlabel("Run")
    ax.set_ylabel("Value")
    fig.savefig(output, dpi=150)
    plt.close(fig)
     
    print(f"backend: {matplotlib.get_backend()}")
    print(f"saved: {output.name}")
    print(f"bytes: {output.stat().st_size}")

    Call matplotlib.use(“Agg”) before any pyplot import or figure creation. If the backend has already been initialized, Matplotlib may fail to switch or raise an import error.

  2. Run the script from the same Python environment used by the scheduled job or CI worker.
    $ python headless_plot.py
    backend: Agg
    saved: headless-plot.png
    bytes: 44494

    For an uneditable script, run one launch with MPLBACKEND=Agg python existing_plot.py. The environment variable overrides matplotlibrc backend settings for that process, so avoid exporting it globally unless every Matplotlib process in that account should use Agg.

  3. Verify that the saved plot file exists.
    $ python - <<'PY'
    from pathlib import Path
    path = Path("headless-plot.png")
    print(f"verified: {path.name} exists={path.is_file()} size={path.stat().st_size}")
    PY
    verified: headless-plot.png exists=True size=44494
  4. Remove the smoke-test files if they are not part of your project.
    $ rm headless_plot.py headless-plot.png