Generating Matplotlib Plots on Headless Servers with the Agg Backend
A step‑by‑step guide to rendering Matplotlib figures on headless servers using the Agg backend, covering backend selection, plotting, saving, verification, and common failure recovery.
24 Apr 2026, 10:59 UTC

Desired outcome
Produce static image files (PNG, PDF, SVG, EPS) from Matplotlib scripts that run on servers without a graphical display.
Prerequisites
- Python 3.8+ with
matplotlibinstalled (pip install matplotlib). - Access to a terminal on the target machine (SSH, CI runner, container, etc.).
- No X server or GUI libraries required.
Procedure
1. Select the Agg backend before any pyplot import
The backend must be set at the very top of the script, before import matplotlib.pyplot. Use matplotlib.use('Agg').
import matplotlib
matplotlib.use('Agg') # <-- must be first Matplotlib call
import matplotlib.pyplot as plt
If pyplot is imported first, Matplotlib locks the default backend and a later use() call raises RuntimeError.
2. Create the figure and plot
Standard plotting commands work unchanged.
fig, ax = plt.subplots(figsize=(8, 4), dpi=150)
ax.plot([0, 1, 2, 3], [0, 1, 4, 9], marker='o')
ax.set_title('Headless Agg example')
ax.set_xlabel('x')
ax.set_ylabel('y')
3. Save the figure to a file
Call fig.savefig() with the desired format and resolution.
output_path = 'plot.png'
fig.savefig(output_path, dpi=300, bbox_inches='tight')
print(f'Saved {output_path}')
Supported formats include png, pdf, svg, eps. The file extension determines the renderer.
4. Verify the output
- Existence and size:
ls -lh plot.png– file should be non‑zero. - Valid image:
file plot.png– should report “PNG image data”. - Dimensions match figsize × dpi: using Pillow:
from PIL import Image im = Image.open('plot.png') print(im.size) # expected (width, height) = (figsize[0]*dpi, figsize[1]*dpi)
Expected checks
- Script exits with status 0 and prints the saved filename.
- No GUI window appears (run via SSH without
-Xor in a container). - Output file passes the three verification steps above.
Recovery options
Backend set too late
If you see RuntimeError: Cannot change backend after matplotlib.pyplot has been imported, move the matplotlib.use('Agg') line to the very top of the file, before any Matplotlib import.
Missing font or style warnings
Agg uses the default font stack. If a custom font is required, install the font on the server and set rcParams['font.family'] before creating the figure.
Large memory consumption
For very large figures (high dpi, large figsize) the raster buffer can exceed available RAM. Reduce dpi or figsize, or render in tiles (advanced).
Limitations
- No interactive features: toolbar, zoom/pan, event callbacks.
- Only raster (Agg) or vector (PDF/SVG/EPS) output; no live display.
- Text shaping relies on FreeType; complex scripts may need additional font configuration.
Complete minimal example
#!/usr/bin/env python3
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 3), dpi=200)
ax.plot([0, 1, 2, 3], [0, 1, 4, 9])
ax.set_title('Agg backend demo')
fig.savefig('demo.png', dpi=300, bbox_inches='tight')
print('demo.png written')
Run with python3 demo.py on any headless host; verify as described.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.