HOWTO · Matplotlib

How to Save Plots as an Image File Without Displaying in Matplotlib

Save a Matplotlib figure without opening a window by using a non-interactive backend, savefig(), and the correct save-then-close order.

On this page

To save a complete Matplotlib plot as an image without opening a GUI window, omit show(), save the specific figure with savefig(), and close it only after writing the file. For truly headless environments, select the non-interactive Agg backend before importing pyplot.

Save a Matplotlib Plot Without Displaying It

To save a complete Matplotlib figure without opening a GUI window, omit show(), call savefig() on the specific figure, and call close() only after the file is written. In a server, CI job, container, or another environment that needs an explicitly headless backend, select the static Agg backend before importing matplotlib.pyplot.

The essential order is therefore select the backend, build the figure, save it, and then close it. Keeping the Figure reference makes the target unambiguous and avoids relying on whichever figure pyplot currently considers active.

An explicit Agg selection is most useful when the script must behave consistently on a machine with no display server. It is not required merely because a script saves a file: Matplotlib may already choose a suitable non-interactive backend automatically. For a command that cannot be edited, the backend can instead be selected through the MPLBACKEND=Agg environment setting. Do not combine several backend-selection methods without a reason, because Matplotlib applies a precedence order and the last applicable configuration wins.

Save a Figure With the Headless Agg Backend

The following tested example saves a sine-wave plot as sine-wave.png without calling show(). Agg is a non-interactive backend that writes raster output, so it does not require an on-screen window.

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

output = Path("sine-wave.png")
x = np.linspace(0, 2 * np.pi, 200)

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, np.sin(x), color="#2166ac", linewidth=2)
ax.set(title="Sine wave", xlabel="x", ylabel="sin(x)")
ax.grid(alpha=0.25)

fig.savefig(output, dpi=150, bbox_inches="tight")
plt.close(fig)

saved = plt.imread(output)
assert output.is_file() and saved.size > 0
print("backend:", matplotlib.get_backend())
print("saved:", output.name)
print("valid PNG:", True)

The verified output is:

backend: Agg
saved: sine-wave.png
valid PNG: True

Saved Matplotlib sine-wave figure with labeled axes and a grid.

The image is written relative to the process’s current working directory. An absolute Path or a path built from a known project directory is safer when a scheduler or service may start the script elsewhere.

The assertions reopen the generated PNG and confirm that it exists and contains image data. That check detects an absent or unreadable output, while the rendered preview above confirms the expected curve, labels, and grid. In production, choose a similarly concrete verification step when an empty or misplaced export would be costly.

Choose an Image Format and savefig() Options

Figure.savefig() normally infers the format from the filename extension. Use PNG for widely supported raster output, SVG for scalable web graphics, and PDF for a vector document. If the filename has no suitable extension, pass the format argument explicitly.

For raster files, dpi controls output resolution; it does not make vector paths in SVG or PDF sharper. bbox_inches="tight" trims excess surrounding space, while transparent=True makes the figure and axes backgrounds transparent unless their colors are overridden. These options affect the exported file, not whether a window appears. The formats actually available can depend on the installed backend and optional libraries.

Choose the format for its consumer. PNG is appropriate for a chart embedded in a document or web page; SVG remains crisp when a browser scales line art and text; PDF is convenient for print-oriented workflows. A high dpi increases raster dimensions and file size, so set it from the intended display or print requirements rather than using the largest value indiscriminately.

Understand ioff(), show(), and Save Order

plt.ioff() disables pyplot’s interactive mode, but it is not a universal headless switch. In particular, notebook frontends can automatically display the final figure in a cell even when interactive mode is off. Use Agg when a non-GUI renderer is required, and suppress the final figure value in a notebook or explicitly close the figure after saving.

Conversely, calling savefig() does not itself require interactive mode or show(). show() is for presenting figures through an interactive backend; omitting it is the normal choice for an export-only script. This distinction is why turning interaction off and selecting a file-rendering backend solve related but different problems.

Save before calling close(fig). Closing first removes pyplot’s reference to that figure, so a later stateful plt.savefig() can target a different or newly created figure. Similarly, the show() documentation warns that saving after a blocking show() can produce an empty figure; save first, or keep the figure object and call its savefig() method.

Release Figures in Loops and Save In Memory

Pyplot retains references to figures created through its interface. A batch job that creates many plots should call close(fig) after each successful save so those figures and their memory can be released. Use a try/finally cleanup pattern when later processing may fail.

When another API needs the image bytes and no disk file is required, pass an io.BytesIO object to fig.savefig() instead of a filesystem path. Rewind the buffer with seek(0) before reading or uploading it, and still close the figure after the save.

Handle a Missing Output Directory

savefig() creates the image file but does not create missing parent directories. This verified boundary example catches that failure and closes the figure in all cases:

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt

output = Path("missing") / "plot.png"
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 3])

try:
    fig.savefig(output)
except FileNotFoundError:
    print("Create the parent directory before savefig().")
finally:
    plt.close(fig)
Create the parent directory before savefig().

For a real export, create the directory first with output.parent.mkdir(parents=True, exist_ok=True). Also verify that the process has permission to write there.

Use imsave() for a Numeric Array

Use matplotlib.pyplot.imsave() when the input is a 2D or RGB(A) numeric array rather than a plotted figure with axes, labels, legends, and other artists. It maps array values to image pixels and does not replace Figure.savefig() for a complete plot.

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

pixels = np.array([[0.0, 0.5, 1.0], [1.0, 0.5, 0.0]])
output = Path("array-image.png")

plt.imsave(output, pixels, cmap="gray", vmin=0, vmax=1)

saved = plt.imread(output)
assert output.is_file() and saved.shape[:2] == pixels.shape
print("saved array:", output.name)
print("pixel grid:", saved.shape[0], "x", saved.shape[1])
saved array: array-image.png
pixel grid: 2 x 3