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 ExportGrafanaDashboardToPDF
Duplicate the example environment file and rename it.
cp .env.template .env
nano .env
Edit the values according to your configuration.
GRAFANA_USER=gfexp
GRAFANA_PASSWORD=gfexp
GRAFANA_SERVICE_ACCOUNT=false
GRAFANA_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 --build
By 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-pdf
docker 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_html
setting is set totrue
in your Grafana configuration to allow HTML/JS injection.
To add a PDF export button:
- Copy the content of
grafana-button.html
into 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%2Fy
Or 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-pdf
docker 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=1080
To let the PDF match the full height of the dashboard without specifying a fixed height, you can set the
PDF_HEIGHT_PX
variable toauto
in the.env
file: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
pdfWidthPx
andpdfHeightPx
parameters will override the values set in the.env
file.
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-pdf
docker 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 1080
By 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=2
The 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=true
And then add the following code to the Grafana panel where you want to display the dashboard name and the time range. You can customize the display format by modifying the formatTimestamp
function in the script below:
<div style="display:flex; align-items:center;justify-content:space-between;">
<p id="gfexp_display_actual_dashboard_title" style="margin: 0;line-height: 1rem;">${__dashboard}</p>
<p id="gfexp_display_actual_date" style="margin: 0;line-height: 1rem;text-transform:capitalize;"></p>
</div>
<script>
(function() {
function formatTimestamp(timestamp) {
const date = new Date(timestamp);
const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
return date.toLocaleDateString( window.gfexpLang === 'fr' ? 'fr-FR' : 'en-US', options);
}
let fromTimestampGrafana = ${__from};
let toTimestampGrafana = ${__to};
document.getElementById("gfexp_display_actual_date").innerHTML = formatTimestamp(fromTimestampGrafana) + " - " + formatTimestamp(toTimestampGrafana);
})();
</script>
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=false
Disabling this feature would have no effect if the
kiosk
parameter 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=true
By 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=true
You 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.