Generating Matplotlib Graphs Without a Running X Server

Matplotlib is often used on laptops and desktop machines where a graphical display is available. On a server, cron job, Docker container, CI worker, or SSH-only environment, that assumption can break. If Matplotlib tries to use an interactive backend that expects an X server, your script may fail with display-related errors instead of producing a chart.

The fix is to use a non-interactive backend. The most common choice is Agg, which renders images in memory and writes them to files such as PNGs. It does not need a window manager, monitor, or running X server.

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [2, 5, 4, 8, 7]

plt.figure(figsize=(6, 4))
plt.plot(x, y, marker="o")
plt.title("Server-side Matplotlib chart")
plt.xlabel("Step")
plt.ylabel("Value")
plt.tight_layout()
plt.savefig("chart.png", dpi=150)
plt.close()

The important detail is the order. Call matplotlib.use("Agg") before importing matplotlib.pyplot. Once pyplot is imported, Matplotlib may already have selected a backend, and changing it later can be unreliable.

You can also set the backend outside the script by using the MPLBACKEND environment variable. This is useful when you do not want to modify application code, or when the same code runs in both local and server environments.

MPLBACKEND=Agg python generate_chart.py

In Docker or CI, this environment-variable approach keeps the container simpler because you do not need to install desktop packages just to export an image. Your Python dependencies can focus on the libraries required for rendering and data processing.

If you are troubleshooting an existing failure, check the error message for phrases like cannot connect to X server, no display name, or backend names such as TkAgg. Those are strong hints that the script is trying to use an interactive backend in a headless environment.

If your script generates many charts, remember to close figures after saving them. plt.close() releases memory associated with the current figure. Without it, long-running jobs can gradually consume more memory, especially when charts are created inside loops.

for name, data in reports.items():
    plt.figure(figsize=(8, 4))
    plt.plot(data["x"], data["y"])
    plt.title(name)
    plt.tight_layout()
    plt.savefig(f"{name}.png")
    plt.close()

Using Agg is best for saved image files, not interactive exploration. If you need to zoom, pan, or inspect plots visually, run the code on a machine with a graphical backend. For automated reporting, email attachments, dashboards, and scheduled exports, the non-interactive backend is usually the cleanest option.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x
()
x