Grafana Image is a Home Assistant custom integration and Lovelace custom card for showing a rendered Grafana panel as an image inside a Home Assistant dashboard.
The browser never calls Grafana directly. Instead, the Lovelace card requests the image from a Home Assistant backend endpoint, and Home Assistant proxies the request to Grafana. This keeps Grafana credentials server-side.
Rendered images are now produced by a single background worker and kept in backend cache. The card first asks the backend for cache or queue status, then loads the cached PNG when one is available.
Once a panel has rendered successfully, the integration keeps showing that last successful image even when it is stale. The last cached image is also persisted so it can be restored after a Home Assistant restart.
- Home Assistant with support for custom integrations
- Grafana reachable from the Home Assistant host or container
- Grafana image rendering enabled and working
- Either:
- a Grafana API token with access to the dashboard and panel, or
- anonymous Grafana access configured for the target dashboards
- Open HACS in Home Assistant.
- Add this repository as a custom repository:
- Repository:
https://github.com/phaser4/grafana-image - Category:
Integration
- Repository:
- Install
Grafana Image. - Restart Home Assistant.
Add the integration to configuration.yaml:
grafana_image:
url: "http://grafana.local:3000"
api_token: !secret grafana_api_token
cache_seconds: 60
max_concurrent_renders: 2
timeout_seconds: 20| Option | Required | Default | Description |
|---|---|---|---|
url |
yes | none | Base Grafana URL reachable from Home Assistant |
api_token |
no | none | Bearer token for Grafana API access |
cache_seconds |
no | 60 |
In-memory cache duration for rendered PNGs |
max_concurrent_renders |
no | 2 |
Retained for backward compatibility; renders are currently processed one at a time by the background worker |
timeout_seconds |
no | 20 |
Timeout for upstream Grafana render requests |
If you do not use anonymous Grafana access, create a Grafana service account token:
- Sign in to Grafana with an account that can manage service accounts.
- Open
Administration. - Open
Users and access->Service accounts. - Click
Add service account. - Create a service account such as
home-assistant-grafana-image. - Open that service account and click
Add service account token. - Generate the token and copy it immediately.
- Store it in Home Assistant, for example:
grafana_image:
url: "http://grafana.local:3000"
api_token: !secret grafana_api_tokenAnd in secrets.yaml:
grafana_api_token: your_generated_token_hereFor this integration, a read-only role such as Viewer is the safest starting point.
Add the card resource manually:
resources:
- url: /api/grafana_image/static/grafana-image-card.js
type: moduleOr in the Home Assistant UI:
- Open
Settings - Open
Dashboards - Open
Resources - Add:
- URL:
/api/grafana_image/static/grafana-image-card.js - Type:
JavaScript module
- URL:
type: custom:grafana-image-card
dashboard_uid: aquarium
panel_id: 4
from: now-24h
to: nowtype: custom:grafana-image-card
dashboard_uid: aquarium
panel_id: 4
from: now-24h
to: now
slug: aquarium
org_id: 1
theme: dark
columns: 12
rows: 3
refresh_seconds: 600
fit: contain| Option | Required | Default | Description |
|---|---|---|---|
dashboard_uid |
yes | none | Grafana dashboard UID |
panel_id |
yes | none | Grafana panel ID |
from |
yes | none | Grafana time range start |
to |
yes | none | Grafana time range end |
title |
no | none | Home Assistant card header |
slug |
no | _ |
Cosmetic dashboard slug used in the render URL |
org_id |
no | 1 |
Grafana organization ID |
theme |
no | dark |
Grafana render theme |
width |
no | 900 |
Fallback width used only before the card can measure its real width |
columns |
no | 12 |
Default section-grid width for the card; accepts 1 to 12 or full |
rows |
no | 3 |
Default card height in Lovelace row units |
refresh_seconds |
no | 600 |
Background re-render interval for the image; cards can override it per panel |
fit |
no | contain |
CSS object-fit value for the image |
The card first requests status:
/api/grafana_image/status?dashboard_uid=...&panel_id=...&from=...&to=...
The backend then:
- checks whether a recent cached PNG already exists
- returns
ready,stale,queued,rendering, orerror - queues the render key for the single background worker when the image is older than
refresh_seconds - keeps recently requested panels refreshing in the backend for a while even while no dashboard is open
When a cached PNG is available, the card then requests:
/api/grafana_image/render?dashboard_uid=...&panel_id=...&from=...&to=...
The render endpoint serves only cached PNGs. It does not do long synchronous Grafana renders on cache miss anymore.
Successful PNG responses are cached based on the effective render parameters. If a stale image exists, the card keeps showing it while the worker refreshes it in the background. If no previous image exists yet, the card shows that rendering is queued.
The integration also persists the last successful image for each render key, so after a restart it can immediately show the previous image while a new background refresh is queued.
By default, the integration re-renders a panel every 10 minutes. Override refresh_seconds on a card when a panel should refresh faster or slower.
The card reports Lovelace grid sizing through columns and rows, with defaults of a full-width 12 x 3 card. It then measures the actual rendered image area inside the card and asks Grafana for a PNG at that exact width and height, so the image matches the displayed card size without browser-side upscaling or downscaling.
If the card shows backend errors or Grafana does not return PNGs, verify that Grafana image rendering is installed and working for panel renders.
Use a Grafana URL that is reachable from the Home Assistant environment, not just from your desktop browser. Docker and container setups often require a container hostname rather than localhost.
Verify that:
- the API token is valid
- the token has access to the dashboard
- anonymous access is configured if you are not using a token
If Grafana works in a browser but not through Home Assistant, the hostname is often the problem. Test with the hostname as seen from the Home Assistant container or VM.
If renders are slow:
- increase
timeout_seconds - remember the integration now renders one image at a time in the background
- lower the refresh frequency
- reduce
rowsor the section width - verify that Grafana rendering is healthy
- Do not expose the Grafana API token in Lovelace config.
- Do not expose Grafana anonymously outside a trusted network unless it is otherwise protected.
- The card does not accept a Grafana base URL, which prevents browser-side credential leaks and arbitrary upstream requests.