Dashboard Decompiling Guide: Converting Kibana JSON to YAML¶
This guide covers converting existing Kibana dashboards into kb-yaml-to-lens YAML. It is designed to be consumed by both humans and LLMs performing conversions.
Quick Reference¶
Complete Documentation: For full schema reference and examples, use llms-full.txt which contains all project documentation.
Workflow:
- Fetch the dashboard NDJSON (
kb-dashboard fetch) or download it from a repository. - Decompile into a YAML skeleton:
kb-dashboard decompile dashboard.ndjson -o dashboard.yaml. This gives you panel stubs with layout, titles, and panel types pre-filled. Each stub includesTODO(decompile)comments containing the original Kibana panel JSON. - Fill in panel configs using the TODO comments as reference. Translate the original Kibana JSON into the YAML schema using the current panel-specific keys (
type,data_view,metrics,dimension,breakdown,breakdowns, etc.). Use the Component Mapping section below and the panel type documentation as guides. - Compile to verify the YAML is valid:
kb-dashboard compile. - Round-trip validate: disassemble both the original and compiled NDJSON, then compare them.
What Decompile Produces¶
The decompile command generates a YAML skeleton, not a complete dashboard. Specifically:
- Dashboard-level metadata (name, id, description) is extracted.
- Every panel gets a stub with the correct
sizeandpositionfrom the grid layout. - Panel titles are preserved where present.
- Panel types are detected (lens, markdown, links, etc.), but for lens panels the inner configuration is left empty (
lens: {}). This means the decompiled YAML will not compile as-is. - Each panel stub includes
TODO(decompile)comments containing the original Kibana panel JSON. These comments are your primary reference for filling in the lens configuration.
You (or an LLM) need to translate the Kibana JSON from the TODO comments into the YAML schema. The Component Mapping section below shows how each Kibana construct maps to YAML.
JSON-to-YAML Conversion Prompt Scaffold¶
Use this prompt pattern when asking an LLM to complete a decompiled dashboard. Keep the request scoped to one dashboard at a time.
Complete the decompiled YAML stubs in <yaml_file>. Each panel has TODO comments
containing the original Kibana panel JSON — use these to fill in the lens
configuration (type, data_view, metrics, `dimension`, `breakdown`, `breakdowns`, and related panel-specific fields).
Requirements:
1. Preserve panel layout (size and position are already set).
2. Translate every panel's original JSON into the correct YAML schema.
3. Use the panel type reference and component mapping from the decompiling guide.
4. Omit fields that match default values.
5. After conversion, validate:
- kb-dashboard compile --input-dir <yaml_dir> --output-dir <compiled_dir>
- kb-dashboard disassemble <compiled_dir>/output.ndjson -o <compiled_disassembled_dir>
- kb-dashboard disassemble original.ndjson -o <original_disassembled_dir>
- kb-dashboard compare <original_disassembled_dir> <compiled_disassembled_dir>
6. Summarize any mismatches found during validation.
Fetching Dashboard from Kibana¶
Retrieve a dashboard directly from Kibana using a URL or ID:
# Using dashboard URL
kb-dashboard fetch "https://kibana.example.com/app/dashboards#/view/my-id" \
--output dashboard.ndjson
# Using dashboard ID
kb-dashboard fetch my-dashboard-id --output dashboard.ndjson
Input Types:
The fetch command accepts two types of input:
- Dashboard URL - Full Kibana dashboard URL (e.g.,
https://kibana.example.com/app/dashboards#/view/my-id) - Dashboard ID - Plain dashboard ID (e.g.,
my-dashboard-id)
How it works:
- If the input looks like a URL, the dashboard ID is extracted from the URL
- Otherwise, the input is treated as a plain dashboard ID
Authentication Options:
# API Key authentication (recommended)
kb-dashboard fetch my-dashboard-id --output dashboard.ndjson \
--kibana-api-key "your-api-key"
# Username/password authentication
kb-dashboard fetch my-dashboard-id --output dashboard.ndjson \
--kibana-username user --kibana-password pass
# Specific Kibana space
kb-dashboard fetch my-dashboard-id --output dashboard.ndjson \
--kibana-space-id "my-space"
Input Formats Supported:
- URL (standard):
https://kibana.example.com/app/dashboards#/view/{id} - URL (with space):
https://kibana.example.com/s/{space}/app/dashboards#/view/{id} - URL (with query params):
https://kibana.example.com/app/dashboards#/view/{id}?_g=... - Plain dashboard ID:
my-dashboard-idordashboard-123
Decompilation¶
Generate a YAML skeleton directly from NDJSON:
The output is a starting point, not a finished dashboard. See What Decompile Produces above for details on what you get and what still needs to be filled in.
Disassembly¶
The disassemble command breaks dashboard NDJSON into individual JSON files. It serves two purposes:
- Inspecting individual panels — When you need to read the raw Kibana JSON for a specific panel in isolation (e.g., to understand a complex configuration).
- Round-trip validation — Disassemble both the original and compiled NDJSON, then use
compareto check they match.
Disassembly is not the primary conversion path. Use decompile to get your YAML skeleton, then fill in the stubs.
Output Structure:
output_dir/
├── metadata.json # Dashboard title, description, id
├── options.json # Display options (margins, colors, etc.)
├── controls.json # Dashboard controls (if present)
├── filters.json # Dashboard-level filters (if present)
├── references.json # Data view references
└── panels/ # Individual panel JSON files
├── 000_panel-1_lens.json
├── 001_panel-2_markdown.json
└── ...
Conversion Strategy¶
Recommended Approach¶
With decompile, you get all panels at once as stubs. The recommended workflow is:
- Run
kb-dashboard decompileto get the full YAML skeleton with all panel stubs. - Fill in one panel at a time, starting with the simplest (markdown panels, metric panels).
- Compile incrementally to catch errors early: finish required fields in remaining stubs, or temporarily comment/remove unfinished panel entries before running
kb-dashboard compile. - Move on to more complex panels (XY charts, datatables) once simpler ones validate.
- Run the full round-trip validation when all panels are complete.
Minimal YAML¶
Omit fields that match defaults. Common defaults:
Dashboard Level:
settings.margins: truesettings.sync.colors: falsesettings.sync.cursor: truesettings.sync.tooltips: falsesettings.titles: true
Panel Level:
- Legend:
show: true,position: right - Values:
show_values: false - Breakdown:
size: 5
Reference the documentation in llms-full.txt for component-specific defaults.
Component Mapping¶
Dashboard Metadata¶
Input (metadata.json):
{
"id": "my-dashboard-id",
"title": "System Metrics Overview",
"description": "Dashboard showing system performance metrics"
}
Output (YAML):
---
dashboards:
- name: System Metrics Overview
description: Dashboard showing system performance metrics
panels: [] # Panels will be added incrementally
Markdown Panels¶
Input:
{
"type": "markdown",
"gridData": {"x": 0, "y": 0, "w": 48, "h": 3},
"panelConfig": {
"markdown": "# Title\n\nContent here"
}
}
Output:
Lens Metric Panels¶
Input:
{
"type": "lens",
"gridData": {"x": 0, "y": 3, "w": 24, "h": 15},
"embeddableConfig": {
"attributes": {
"title": "Total Documents",
"visualizationType": "lnsMetric",
"state": {
"datasourceStates": {
"formBased": {
"layers": {
"layer1": {
"columns": {
"col1": {
"operationType": "count",
"label": "Count"
}
}
}
}
}
}
},
"references": [
{
"type": "index-pattern",
"id": "logs-*",
"name": "indexpattern-datasource-layer-layer1"
}
]
}
}
}
Output:
- title: Total Documents
size: {w: 24, h: 15}
position: {x: 0, y: 3}
lens:
type: metric
data_view: logs-*
primary:
aggregation: count
Lens Pie Charts¶
Input:
{
"type": "lens",
"visualizationType": "lnsPie",
"state": {
"datasourceStates": {
"formBased": {
"layers": {
"layer1": {
"columns": {
"col1": {
"operationType": "terms",
"sourceField": "status",
"params": {"size": 5, "orderBy": {"type": "column", "columnId": "col2"}, "orderDirection": "desc"}
},
"col2": {
"operationType": "count"
}
}
}
}
}
}
}
}
Output:
- title: Status Breakdown
size: {w: 24, h: 15}
position: {x: 24, y: 3}
lens:
type: pie
data_view: logs-*
breakdowns:
- field: status
type: values
size: 5
metrics:
- aggregation: count
Dashboard Controls¶
Input (controls.json):
{
"panelsJSON": "[{\"type\":\"optionsListControl\",\"order\":0,\"width\":\"medium\",\"fieldName\":\"namespace\"}]",
"controlStyle": "oneLine"
}
Output:
Reference Dashboard Controls for complete options.
Dashboard Filters¶
Input (filters.json):
Output: Reference Filters & Queries for filter conversion.
Panel Type Reference¶
| Kibana Type | YAML Type | Documentation |
|---|---|---|
lnsMetric |
lens.type: metric |
Metric Charts |
lnsPie |
lens.type: pie |
Pie Charts |
lnsXY |
lens.type: line/bar/area |
XY Charts |
lnsGauge |
lens.type: gauge |
Gauge Charts |
lnsDatatable |
lens.type: datatable |
Datatable Charts |
markdown |
markdown |
Markdown Panels |
links |
links |
Links Panels |
For ES|QL-based panels, see ES|QL Panels.
Validation¶
Compile¶
Compare Structure¶
Use the compare command to quickly check panel counts and types:
Verification Workflow (Round-Trip Testing)¶
For thorough validation, use this round-trip workflow to verify the compiled output matches the original:
- Compile YAML to JSON:
IMPORTANT: Fix any compilation errors before proceeding. The YAML must compile successfully.
- Disassemble both original and compiled dashboards:
# Disassemble original
kb-dashboard disassemble original.ndjson -o /tmp/original_disassembled/
# Disassemble compiled
kb-dashboard disassemble /tmp/compiled/output.ndjson -o /tmp/compiled_disassembled/
- Compare panel structures:
Use the compare command to analyze differences:
This will show panel counts, types, and identify any mismatches.
- Verify panel structure and configuration:
Use jq to compare specific panel configurations between original and compiled:
# Compare specific panel JSON structures
diff -u \
<(jq '.embeddableConfig.attributes.state' /tmp/original_disassembled/panels/003_panel-4_lens.json) \
<(jq '.embeddableConfig.attributes.state' /tmp/compiled_disassembled/panels/003_panel-4_lens.json)
What to verify for each panel type:
XY Charts (line, bar, area):
- Chart type matches (seriesType in original -> type in YAML)
- Stacking mode preserved (if yConfig[].axisMode: stacked exists)
- Legend configuration matches (legend.isVisible, legend.position)
- Dimensions properly mapped (count columns by isBucketed: true)
- Breakdown configurations match (field names, size parameters)
Datatables: - All bucketed columns appear as row dimensions - Size parameters match for each dimension - Metric columns preserve aggregation functions
All Lens panels: - Aggregation functions match (median, average, sum, etc.) - Field names are exact (including namespace prefixes) - Format settings preserved (percent, bytes, number, etc.)
Understanding discrepancies:
When comparing original and compiled dashboards, some differences are expected:
- Expected (safe): Panel IDs differ, minor query formatting, panel order variations
- Needs investigation: Panel count mismatch, visualization type changes, missing dimensions/metrics, field name differences
Verification checklist:
Before considering a conversion complete:
- [ ] YAML compiles without errors
- [ ] Panel counts match (or differences are documented)
- [ ] Panel types match (lens, esql, links, markdown, image, search, vega, section for Kibana 9.1+)
- [ ] Chart configurations preserved (type, stacking, legends)
- [ ] All dimensions and breakdowns accounted for
- [ ] Size parameters match original values
- [ ] Field names and aggregations verified
Note: Section is only available in Kibana 9.1+
Common Patterns¶
Lens Operation Mapping¶
| Kibana Operation | YAML Aggregation | Notes |
|---|---|---|
count |
aggregation: count |
Document count |
sum |
aggregation: sum |
Sum of field values |
avg |
aggregation: average |
Average of field values |
min |
aggregation: min |
Minimum value |
max |
aggregation: max |
Maximum value |
median |
aggregation: median |
Median value |
percentile |
aggregation: percentile |
Requires percentile param |
terms |
type: values |
Used in breakdowns/slices |
date_histogram |
type: date_histogram |
Time-based dimension |
range |
type: range |
Range-based dimension |
XY Chart Dimensions¶
Input (Kibana lens state):
{
"columns": {
"col1": {
"operationType": "date_histogram",
"sourceField": "@timestamp",
"params": {"interval": "auto"}
},
"col2": {
"operationType": "avg",
"sourceField": "system.cpu.total.norm.pct"
}
}
}
Output:
lens:
type: line
data_view: metrics-*
dimension:
field: '@timestamp'
type: date_histogram
metrics:
- field: system.cpu.total.norm.pct
aggregation: average
Multi-Dimension Breakdowns¶
Input:
{
"columns": {
"col1": {"operationType": "date_histogram", "sourceField": "@timestamp"},
"col2": {"operationType": "terms", "sourceField": "host.name"},
"col3": {"operationType": "avg", "sourceField": "cpu.usage"}
}
}
Output:
lens:
type: line
data_view: metrics-*
dimension:
field: '@timestamp'
type: date_histogram
breakdown:
field: host.name
type: values
metrics:
- field: cpu.usage
aggregation: average
Error Resolution¶
Schema Validation Errors¶
Solution: Check required fields in panel type documentation. Each panel type has specific required fields.
Type Errors¶
Solution: Verify field types match schema. Common issues:
- Numbers as strings: use
100not"100" - Booleans as strings: use
truenot"true"
Unsupported Panel Types¶
Solution: See supported panel types. For unsupported panels, either:
- Create placeholder markdown panel
- Skip the panel and document it
Reference Resolution¶
Solution: Ensure data views exist in target Kibana instance or are defined in YAML.
Complete Example¶
Decompiled YAML stub (before filling in):
```yaml skip¶
dashboards: - name: Application Monitoring description: Real-time application metrics panels: - title: Total Documents size: {w: 24, h: 15} position: {x: 0, y: 3} lens: {} # TODO(decompile): {"type":"lens","embeddableConfig":{"attributes":{"title":"Total Documents","visualizationType":"lnsMetric","state":{"datasourceStates":{"formBased":{"layers":{"layer1":{"columns":{"col1":{"operationType":"count","label":"Count"}}}}}}}},"references":[{"type":"index-pattern","id":"logs-*"}]}}
**Completed YAML (after translating the TODO comment):**
```yaml
---
dashboards:
- name: Application Monitoring
description: Real-time application metrics
panels:
- title: Total Documents
size: {w: 24, h: 15}
position: {x: 0, y: 3}
lens:
type: metric
data_view: logs-*
primary:
aggregation: count
Validation:
Additional Resources¶
- Complete Documentation: llms-full.txt
- Examples: Complete Examples
- Aerospike Examples: Complex real-world dashboards
- Panel Type Docs: Panel Types Overview
- Controls: Dashboard Controls
- Filters: Filters & Queries
- Advanced Topics: ES|QL Views, Color Assignments