Generating PNG images from a Grafana chart – Correct URL, Settings and Authentication

2024/05/03

Lately I’v been toying with some sensors at home, for which data ends up in a VictoriaMetrics instance and gets displayed using Grafana dashboards.

Setting this up was surprinsingly easy. At some point I wondered whether I could get the charts to be displayed on a Pimoroni e-ink display that I own.

Again, surprisingly, I found that Grafana supposedly allows you to genereate a PNG for any chart extremely easily. I write supposedly because it ended up taking a bit longer than expected:

Out of bad luck or plain lack of skill, I could not find any place clearly lining out the required steps.

So here’s how to get PNG images out of your Grafana charts, assuming you have a running Grafana instance:

Generate Images from a Grafana chart

Here are the steps. Details in links or below:

  1. Enable image generation
  1. Find the dashboard and view ID:
  2. Decide on the authentication approach: (anonymous, user/password or token)
  3. query the right url: the annoying part that took the longest

1. Enable Image generation

It’s not obviously clear that, in order to have Grafana generate fixed images, you need to either enable the right plugin or tell Grafana to query a separate rendering endpoint

If your Grafana instance is running on a compatible os+architecture combination and you’re Ok with and able to install a plugin, follow the steps to obtain grafana-image-renderer.

If you’re stuck on an incompatible os+architecture (such as certain Synology versions), your only other option is to run the renderer as a separate docker image and tell Grafana how to use it.

That second option is very easy to setup if you have a minimum of familiarity with docker. Grafana’s repo has a minimal sample docker-compose.yaml file.

Below is a slightly extended one that includes the flags to allow anonymous access if your main interest is to get to the images quickly:

version: '2'

services:
  grafana:
    image: grafana/grafana:latest
    ports:
      - 3000
    environment:
      GF_RENDERING_SERVER_URL: http://renderer:8081/render   # <-- tells Grafana where to find the rendere
      GF_RENDERING_CALLBACK_URL: http://grafana:3000         # <-- tells the renderer where to access Grafana
      GF_LOG_FILTERS: rendering: debug                       # <-- useful for troubleshooting
      GF_AUTH_ANONYMOUS_ENABLED: true                        # <-- If you want to skip setting/using credentials for generating images 
      GF_RENDERING_AUTH_TOKEN: changeThisToken               # <-- authentication token the renderer will use
  renderer:
    image: grafana/grafana-image-renderer:latest             # <-- renderer image. Probably works on your ARM or Synology!
    ports:
      - 8081
    volumes:
      - ./config.json:/usr/src/app/config.json               # <-- config that needs to contain the token under GF_RENDERING_AUTH_TOKEN above

If your Grafana instance is already running as a docker container, starting a grafana/grafana-image-renderer next to it and adding the relevant environment variables is all that is needed.

2. Find the dashboard and view ID

First, navigate to the dashboard that contains the chart or panel you’d like to render an image of. The URL will ressemble this:

http://<host>/d/b4e5c9b9-bff7-4545-8ab2-6b3466b57e6a/home?orgId=1

Above, the uuid (here b4e5c9b9-bff7-4545-8ab2-6b3466b57e6a) corresponds to your dashboard ID. You’ll also want to remember the orgId.

To figure out the panel Id, find the share option for the panel you want to render, and click on the embed tab: the displayed HTML code will contain an URL with a &panelId= parameter: that’s what you are looking for.

(Alternatively, the link tab provides you with a link that has a &viewPanel= parameter, which also contains the ID we’re after)

The /home in the URL is the dashboard’s name. It is not required for generating PNGs.

3. (Optional) Using Credentials

As for passing credentials to Grafana when generating the image, you have multiple options:

Note that if you’re not using SSL because, for example, you’re on your local home network, it might be worth considering not to use your admin credentials in cleartext with each request…

4. Query the right URL

And here’s the easiest part that, nonetheless took me the longest to figure out: querying the right URL

It is in the form http://<grafana_host>/render/d-solo/<dashboard_id>?orgId=<orgId>&from=<from_timestamp_millis>&to=<to_timestamp_millis>&panelId=<panelId>&width=600&height=448&tz=Europe%2FZurich&theme=light

(If you prefer the dark theme, you may set &theme=dark at the end.)

As an example, Packing it all together with curl, you’d end up with something like:

curl "http://192.168.1.5:3030/render/d-solo/b4e5c9b9-bff7-4545-8ab2-6b3466b57e6a?orgId=1&from=1714240164000&to=1714248164000&panelId=6&width=600&height=448&tz=Europe%2FZurich&theme=light" -o chart.png

Note: this worked for me using Grafana version 10.3.5, running via Docker on Synology DS918+

Happy PNG rendering!