Grafana PDF Exporter is a tool that enables you to export Grafana v12 dashboards as high-quality PDFs using Puppeteer. It runs a lightweight Node.js server that processes HTTP requests, launches a headless browser, and captures full dashboard views.
You can also inject a custom button into the Grafana interface to allow users to export directly from the dashboard UI.
- Docker
- Docker Compose
Clone this repository and navigate to the project directory:
git clone https://github.com/arthur-mdn/ExportGrafanaDashboardToPDF.git
cd ExportGrafanaDashboardToPDFDuplicate the example environment file and rename it.
cp .env.template .env
nano .envEdit the values according to your configuration.
GRAFANA_USER=gfexp
GRAFANA_PASSWORD=gfexp
GRAFANA_SERVICE_ACCOUNT=falseGRAFANA_USER and GRAFANA_PASSWORD are the credentials the exporter will use to log into Grafana. For a service account
set GRAFANA_SERVICE_ACCOUNT to true and provide token in GRAFANA_PASSWORD.
To start the server, run the following command:
docker compose up -d --buildBy default, the service runs on port 3001.
To generate a PDF, send a POST request to /generate-pdf with the Grafana dashboard URL.
The server will respond with the URL of the generated PDF.
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{ "url": "http://your-grafana-server/d/your-dashboard-id"}' \
http://localhost:3001/generate-pdfdocker exec -it grafana-export-to-pdf /usr/src/app/generate-pdf.sh GF_DASH_URL 'http://your-grafana-server/d/your-dashboard-id'Make sure the
disable_sanitize_htmlsetting is set totruein your Grafana configuration to allow HTML/JS injection.
To add a PDF export button:
- Copy the content of
grafana-button.htmlinto a Grafana text panel.
- Modify the server URL in the script if necessary.
window.gfexpPdfGenerationServerUrl = 'http://localhost:3001';
The button should now be displayed in the native Grafana export dropdown menu.
You can disable injection by commenting out the HTML marker.
<!-- <div id="GFEXP_marker"> -->
<!-- This is a marker to enable HTML injection in this panel -->
<!-- </div> -->In the examples below, the time range is
now-1y/y, which corresponds to last year.
See more details on supported time ranges in the Grafana documentation.
To generate a PDF with a time range, you can simply add the native Grafana time range parameters to the URL.
http://your-grafana-server/d/your-dashboard-id?from=now-1y%2Fy&to=now-1y%2FyOr provide them directly in the request body:
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{ "url": "http://your-grafana-server/d/your-dashboard-id", "from": "now-1y/y", "to": "now-1y/y"}' \
http://localhost:3001/generate-pdfdocker exec -it grafana-export-to-pdf /usr/src/app/generate-pdf.sh GF_DASH_URL 'http://your-grafana-server/d/your-dashboard-id' GF_FROM 'now-1y/y' GF_TO 'now-1y/y'The injected HTML button already retrieves the values of the selected time range in Grafana. You do not need to specify them manually. It also retrieves the theme selected.
To generate a PDF with a fixed width and height, you can adjust the PDF_WIDTH_PX and PDF_HEIGHT_PX variables in the .env file.
PDF_WIDTH_PX=1920
PDF_HEIGHT_PX=1080To let the PDF match the full height of the dashboard without specifying a fixed height, you can set the
PDF_HEIGHT_PXvariable toautoin the.envfile:PDF_HEIGHT_PX=auto
But you can also specify the width and height manually by specifying the pdfWidthPx and pdfHeightPx parameters in the request.
Using the
pdfWidthPxandpdfHeightPxparameters will override the values set in the.envfile.
curl \
-H "Content-Type: application/json" \
-X POST \
-d '{ "url": "http://your-grafana-server/d/your-dashboard-id", "pdfWidthPx": 1920, "pdfHeightPx": 1080}' \
http://localhost:3001/generate-pdfdocker exec -it grafana-export-to-pdf /usr/src/app/generate-pdf.sh GF_DASH_URL 'http://your-grafana-server/d/your-dashboard-id' GF_PDF_WIDTH_PX 1920 GF_PDF_HEIGHT_PX 1080By default, the server exports the entire dashboard. If you want to export a single panel, you can add the viewPanel parameter to the URL.
http://your-grafana-server/d/your-dashboard-id?viewPanel=2The script will try to extract the panel title and use it in the PDF filename.
To make filenames more user-friendly, you can extract title/date directly from HTML elements.
For this URL: http://localhost/d/ID/stats?from=now-1y%2Fy&to=now-1y%2Fy
- By default, the generated PDF will use the dashboard UID and raw time range values, like:
stats_now-1y_y_to_now-1y_y.pdf - With the custom configuration enabled, and by extracting formatted content from Grafana panels, the filename can be more user-friendly, such as:
Stats_Sunday_January_1_2025_-_Sunday_December_31_2025.pdf
To activate this feature, set the following variable to true in your .env file:
EXTRACT_DATE_AND_DASHBOARD_NAME_FROM_HTML_PANEL_ELEMENTS=trueAnd the following code is used to extract the dashboard title and date range from the HTML elements.
You can remove this section if you do not want to use this feature.
<!-- Dashboard title and date display -->
[...]
<style>
[...]
</style>
<div id="gfexp_display_container">
<p id="gfexp_display_actual_dashboard_title">${__dashboard}</p>
<p id="gfexp_display_actual_date"></p>
</div>
<script>
[...]
</script>
<!-- End of dashboard title and date display -->By default, FORCE_KIOSK_MODE is set to true. This means that if the url does not contain the kiosk parameter, the server will add it to the URL to ensure that the PDF is generated without any elements overlapping the dashboard content.
You can disable this behavior by setting the following variable to false in your .env file:
FORCE_KIOSK_MODE=falseDisabling this feature would have no effect if the
kioskparameter is already present in the URL given to the server.
By default, DEBUG_MODE is set to false. When activated, the server will save the HTML content of the page to a file in the debug folder.
There is also more verbose logging in the console, which can help you understand what is happening during the PDF generation process.
This can be useful for debugging purposes.
You can enable this behavior by setting the following variable to true in your .env file:
DEBUG_MODE=trueBy default, HIDE_DASHBOARD_CONTROLS is set to true. This means that the server will hide the dashboard controls (such as the time range selector, the share button, etc.) when generating the PDF. This can be useful to have a cleaner PDF output.
By default, EXPAND_COLLAPSED_PANELS is set to true. This means that the server will expand all collapsed panels when generating the PDF. This can be useful to ensure that all panels are visible in the PDF output.
⚠️ Experimental feature!Intended as a future replacement for
NAVIGATION_TIMEOUT.
When activated, the server will wait for all queries to be completed before generating the PDF. This can be useful for dashboards with long queries.
You can enable this behavior by setting the following variable to true in your .env file.
CHECK_QUERIES_TO_COMPLETE=trueYou can also set the maximum time to wait for each single query to be completed, the interval between each check for the queries completion, and the maximum time to wait for all the queries to be completed, before generating the PDF.
CHECK_QUERIES_TO_COMPLETE_MAX_QUERY_COMPLETION_TIME=30000
CHECK_QUERIES_TO_COMPLETE_QUERIES_INTERVAL=1000
CHECK_QUERIES_TO_COMPLETE_QUERIES_COMPLETION_TIMEOUT=60000🧪 Only available in Grafana v11.4+
By default, EXPAND_TABLE_PANELS is set to false due to performance concerns. When enabled, the server will try to auto-adjust the height of table panels to fit all the rows when generating the PDF. This can be useful to ensure that all data is visible in the PDF output.
You can enable this feature via your .env file:
EXPAND_COLLAPSED_TABLES=true
⚠️ Important Note: When large tables are expanded, the generated PDF height can grow dramatically (several thousand pixels), which may:
- Slow down rendering or cause performance issues during export
- Produce unexpectedly long PDFs
Recommendation: Only enable table expansion for dashboards where full row visibility is critical, or consider adjusting panel sizes manually in Grafana to fit content naturally.
- The PDF generation is complete but the browser do not allow to open the popup window.



