Kibana Dashboards and Visualizations
Overview
The Kibana dashboards and visualizations APIs provide a declarative, Git-friendly format for defining dashboards and
visualizations. Definitions are minimal, diffable, and suitable for version control and LLM-assisted generation.
Key Benefits:
- Minimal payloads (no implementation details or derivable properties)
- Easy to diff in Git
- Consistent patterns for GitOps workflows
- Designed for LLM one-shot generation
- Robust validation via OpenAPI spec
Version Requirement: Kibana 9.4+ (SNAPSHOT)
Important Caveats
Inline vs Saved Object References: When embedding Lens panels in dashboards, prefer inline attributes
definitions over savedObjectId references. Inline definitions are more reliable and self-contained.
Quick Start
Environment Configuration
Kibana connection is configured via environment variables. Run node scripts/kibana-dashboards.js test to verify the
connection. If the test fails, suggest these setup options to the user, then stop. Do not try to explore further until a
successful connection test.
Option 1: Elastic Cloud (recommended for production)
export KIBANA_CLOUD_ID="deployment-name:base64encodedcloudid"
export KIBANA_API_KEY="base64encodedapikey"
Option 2: Direct URL with API Key
export KIBANA_URL="https://your-kibana:5601"
export KIBANA_API_KEY="base64encodedapikey"
Option 3: Basic Authentication
export KIBANA_URL="https://your-kibana:5601"
export KIBANA_USERNAME="elastic"
export KIBANA_PASSWORD="changeme"
Option 4: Local Development with start-local
Use start-local to spin up Elasticsearch/Kibana locally, then source the
generated .env:
curl -fsSL https://elastic.co/start-local | sh
source elastic-start-local/.env
export KIBANA_URL="$KB_LOCAL_URL"
export KIBANA_USERNAME="elastic"
export KIBANA_PASSWORD="$ES_LOCAL_PASSWORD"
Then run node scripts/kibana-dashboards.js test to verify the connection.
Optional: Skip TLS verification (development only)
export KIBANA_INSECURE="true"
Basic Workflow
node scripts/kibana-dashboards.js test
node scripts/kibana-dashboards.js dashboard get <id>
echo '<json>' | node scripts/kibana-dashboards.js dashboard create -
echo '<json>' | node scripts/kibana-dashboards.js dashboard update <id> -
node scripts/kibana-dashboards.js dashboard delete <id>
node scripts/kibana-dashboards.js lens list
node scripts/kibana-dashboards.js lens get <id>
echo '<json>' | node scripts/kibana-dashboards.js lens create -
echo '<json>' | node scripts/kibana-dashboards.js lens update <id> -
node scripts/kibana-dashboards.js lens delete <id>
Dashboards API
Dashboard Definition Structure
The API expects a flat request body with title and panels at the root level. The response wraps these in a data
envelope alongside id, meta, and spaces.
{
"title": "My Dashboard",
"panels": [ ... ],
"time_range": {
"from": "now-24h",
"to": "now"
}
}
Note: Dashboard IDs are auto-generated by the API. The script also accepts the legacy wrapped format
{ id?, data: { title, panels }, spaces? } and unwraps it automatically.
Create Dashboard
echo '{
"title": "Sales Dashboard",
"panels": [],
"time_range": { "from": "now-7d", "to": "now" }
}' | node scripts/kibana-dashboards.js dashboard create -
Update Dashboard
echo '{
"title": "Updated Dashboard Title",
"panels": [ ... ]
}' | node scripts/kibana-dashboards.js dashboard update my-dashboard-id -
Dashboard with Inline Lens Panels (Recommended)
Use inline attributes for self-contained, portable dashboards:
{
"title": "My Dashboard",
"panels": [
{
"type": "lens",
"uid": "metric-panel",
"grid": { "x": 0, "y": 0, "w": 12, "h": 6 },
"config": {
"attributes": {
"title": "",
"type": "metric",
"dataset": { "type": "esql", "query": "FROM logs | STATS total = COUNT(*)" },
"metrics": [{ "type": "primary", "operation": "value", "column": "total", "label": "Total Count" }]
}
}
},
{
"type": "lens",
"uid": "chart-panel",
"grid": { "x": 12, "y": 0, "w": 36, "h": 8 },
"config": {
"attributes": {
"title": "Events Over Time",
"type": "xy",
"layers": [
{
"type": "area",
"dataset": {
"type": "esql",
"query": "FROM logs | STATS count = COUNT(*) BY bucket = BUCKET(@timestamp, 75, ?_tstart, ?_tend)"
},
"x": { "operation": "value", "column": "bucket" },
"y": [{ "operation": "value", "column": "count" }]
}
]
}
}
}
],
"time_range": { "from": "now-24h", "to": "now" }
}
Copy Dashboard Between Spaces/Clusters
node scripts/kibana-dashboards.js dashboard get source-dashboard > dashboard.json
node scripts/kibana-dashboards.js dashboard create dashboard.json
Dashboard Grid System
Dashboards use a 48-column, infinite-row grid. On 16:9 screens, approximately 20-24 rows are visible without
scrolling. Design for densityβplace primary KPIs and key trends above the fold.
| Width |
Columns |
Height |
Rows |
Use Case |
| Full |
48 |
Large |
14-16 |
Wide time series, tables |
| Half |
24 |
Standard |
10-12 |
Primary charts |
| Quarter |
12 |
Compact |
5-6 |
KPI metrics |
| Sixth |
8 |
Minimal |
4-5 |
Dense metric rows |
Target: 8-12 panels above the fold. Use descriptive panel titles on the charts themselves instead of adding
markdown headers.
Grid Packing Rules:
- Eliminate Dead Space: Always calculate the bottom edge (
y + h) of every panel. When starting a new row or
placing a panel below another, its y coordinate must exactly match the y + h of the panel immediately above it.
- Align Row Heights: If multiple panels are placed side-by-side in a row (e.g., sharing the same
y coordinate),
they should generally have the exact same height (h). If they do not, you must fill the resulting empty vertical
space before placing the next full-width panel.
Panel Schema
{
"type": "lens",
"uid": "unique-panel-id",