Export Publication-Quality Static Images from Plotly with Kaleido
Generate publication-ready PNG, SVG, PDF, or JPEG files from Plotly figures using the Kaleido engine—no browser required. Covers installation, configuration, common rendering issues, and CI-friendly verification.
26 Feb 2026, 02:22 UTC

Desired Outcome
Produce high-resolution PNG, SVG, PDF, or JPEG files from Plotly figures without a browser, suitable for reports, publications, or automated pipelines. The Kaleido engine provides a self-contained binary that eliminates external dependencies such as Chrome or the legacy Orca service.
Prerequisites
- Python 3.7 or newer
- Plotly 5.0 or newer
- Kaleido package installed:
pip install -U plotly kaleido
Kaleido bundles its own rendering binary, so no system-level browser installation is required. On minimal Linux containers (e.g., Alpine or slim Debian), you may still need a few shared libraries for the binary to execute; see the recovery section for a typical apt-get list.
Procedure
Single-Figure Export
Import plotly.io, create a figure with Plotly Express or Graph Objects, then call write_image with the Kaleido engine:
import plotly.io as pio
import plotly.express as px
fig = px.scatter(x=[1, 2, 3], y=[1, 3, 2], title="Simple Scatter")
fig.write_image(
"output.png",
engine="kaleido",
scale=2, # 2× the default 72 DPI → 144 DPI
width=1200,
height=800
)
The same call works for "output.svg", "output.pdf", or "output.jpg"; the format is inferred from the file extension.
Batch Configuration
To avoid repeating arguments across a pipeline, set module-level defaults once:
pio.kaleido.scope.default_format = "png"
pio.kaleido.scope.default_scale = 2
pio.kaleido.scope.default_width = 1200
pio.kaleido.scope.default_height = 800
# Later in the script
fig.write_image("batch_output.png") # uses the defaults above
Using pio.write_image Directly
If you prefer a functional style, pio.write_image(fig, "out.png", engine="kaleido", scale=2) is equivalent.
Expected Checks
- File existence and size: confirm the output file exists and has non-zero bytes (
os.path.getsize("output.png") > 0). - Visual verification: open the image and inspect fonts, colors, axis labels, and legend placement.
- Dimension validation: use an image utility (e.g.,
identify output.pngfrom ImageMagick orfile output.png) to verify the pixel dimensions matchwidth × height × scale. - Stress test: export a complex figure (many traces, annotations, subplots) to ensure no clipping or missing layers.
Recovery Options
| Symptom | Likely Cause | Remediation |
|---|---|---|
FileNotFoundError: Kaleido executable not found |
Corrupted or incomplete kaleido wheel |
pip install --force-reinstall kaleido |
| Missing glyphs or fallback fonts | Requested font family not installed on the system | Install system fonts (e.g., apt-get install -y fonts-dejavu-core) or set a safe default: fig.update_layout(font_family="DejaVu Sans") |
| MemoryError or OOM on large figures | High scale with many pixels |
Reduce scale (e.g., 1.5) or export to vector SVG: fig.write_image("out.svg", engine="kaleido") |
WebGL traces (scattergl, heatmapgl) render incorrectly |
Kaleido does not fully support WebGL-based trace types | Switch to SVG-backed traces (scatter, heatmap) for static export, or keep interactive HTML for those figures |
| Permission denied on the Kaleido binary in CI | Binary lacks execute bit in the container layer | Ensure the container user can execute the binary; in Docker, RUN chmod +x /path/to/kaleido or install required libraries (see below). |
Headless CI Library Dependencies
On Debian/Ubuntu-based CI images, the Kaleido binary may require the following libraries (similar to Chrome headless):
apt-get update && apt-get install -y \
libglib2.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \
libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
libxfixes3 libxrandr2 libgbm1 libasound2
Alpine users should prefer the kaleido wheel built for musl or install the equivalent apk packages.
Limitations
- WebGL traces:
scattergl,heatmapgl, and other GL-based traces may render incorrectly or fall back to a blank layer. Test each trace type before committing to a pipeline. - MathJax/LaTeX: Kaleido's built-in MathJax support is limited. For complex LaTeX labels, consider exporting to SVG and post-processing with an external LaTeX renderer, or use Plotly's built-in
mathtextsubset (basic Greek, superscripts, subscripts). - Version coupling: Plotly 5.x pairs with Kaleido 0.1.x+. Verify compatibility with:
import plotly; print(plotly.__version__) import kaleido; print(kaleido.__version__)
Verification Checklist
- Run a minimal script that creates a simple scatter plot and exports to PNG. Confirm the output is a valid PNG:
file output.pngshould reportPNG image data, 1200 x 800, 8-bit/color RGBA. - Check Plotly and Kaleido versions as shown above to ensure compatibility.
- In CI, add a test step that exports a representative figure and compares its checksum (e.g., SHA-256) against a known-good baseline to detect rendering regressions.
Quick Reference
| Task | Code Snippet |
|---|---|
| Install | pip install -U plotly kaleido |
| Export PNG at 2× scale | fig.write_image("out.png", engine="kaleido", scale=2, width=1200, height=800) |
| Set global defaults | pio.kaleido.scope.default_scale = 2 |
| Export SVG (vector) | fig.write_image("out.svg", engine="kaleido") |
| Force font family | fig.update_layout(font_family="DejaVu Sans") |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.