Creation Workflow
Overview¶
Instead of manually creating dashboards through the UI, build complete dashboard JSON structures in Python and deploy via Grafana API. This approach enables:
- Version control for dashboard definitions
- Repeatable dashboard creation across environments
- Automated dashboard generation from metric inventories
- Consistent styling and organization
Step 1: Plan Dashboard Structure¶
Define dashboard organization based on metrics available:
# Example structure planning
dashboard_plan = {
"variables": ["datasource", "hostname", "time_range"],
"rows": [
{
"name": "Overview",
"panels": ["Total Events (stat)", "Current Rate (stat)", "Health (gauge)"]
},
{
"name": "Detailed Metrics",
"panels": ["Metric Timeline (timeseries)", "Distribution (histogram)"]
}
]
}
Step 2: Build Dashboard JSON Structure¶
Create complete dashboard JSON programmatically with proper structure including:
- Dashboard metadata (title, tags, timezone)
- Variables for dynamic filtering (datasource, hostname)
- Panels with queries and visualizations
- Grid layout positioning
Step 3: Deploy via Grafana API¶
Deploy dashboard to Grafana:
import json, subprocess
from pathlib import Path
def deploy_dashboard(dashboard, grafana_url="http://localhost:3000"):
password_file = Path.home() / '.secrets' / 'grafana.admin.pass'
password = password_file.read_text().strip()
dashboard_file = Path("/tmp/dashboard.json")
with dashboard_file.open("w") as f:
json.dump(dashboard, f, indent=2)
cmd = [
"curl", "-s", "-X", "POST",
"-H", "Content-Type: application/json",
"-u", f"admin:{password}",
"-d", f"@{dashboard_file}",
f"{grafana_url}/api/dashboards/db"
]
result = subprocess.run(cmd, capture_output=True, text=True)
response = json.loads(result.stdout)
if response.get("status") == "success":
print(f"✅ Dashboard deployed!")
print(f" URL: {grafana_url}/d/{response.get('uid')}")
return True
return False
Step 4: Verify Dashboard Structure¶
After deployment, verify the dashboard was created correctly:
def verify_dashboard(uid, grafana_url="http://localhost:3000"):
password_file = Path.home() / '.secrets' / 'grafana.admin.pass'
password = password_file.read_text().strip()
cmd = ['curl', '-s', '-u', f'admin:{password}',
f'{grafana_url}/api/dashboards/uid/{uid}']
result = subprocess.run(cmd, capture_output=True, text=True)
data = json.loads(result.stdout)
dashboard = data.get('dashboard', {})
print(f'✅ Dashboard: {dashboard.get("title")}')
print(f' Panels: {len(dashboard.get("panels", []))}')
Grid Layout System¶
Grafana uses 24-column grid with height units:
# Row spans full width
{"h": 1, "w": 24, "x": 0, "y": 0}
# Four equal panels in a row
{"h": 8, "w": 6, "x": 0, "y": 1} # First panel
{"h": 8, "w": 6, "x": 6, "y": 1} # Second panel
{"h": 8, "w": 6, "x": 12, "y": 1} # Third panel
{"h": 8, "w": 6, "x": 18, "y": 1} # Fourth panel
# Two panels: 2/3 and 1/3 width
{"h": 8, "w": 16, "x": 0, "y": 9} # 2/3 width
{"h": 8, "w": 8, "x": 16, "y": 9} # 1/3 width
Best Practices¶
- Plan Before Coding: Sketch dashboard layout on paper first
- Test Queries First: Verify queries work via API before adding to dashboard
- Consistent Sizing: Use standard panel heights (4 for stats, 8 for graphs)
- Use Variables: Enable filtering with datasource, hostname, time range variables
- Meaningful Thresholds: Set thresholds based on actual operational baselines
- Clear Titles: Panel titles should be descriptive and action-oriented
- Group by Function: Organize panels into rows by purpose (Overview, Details, Resources)
- Document Units: Always specify correct units (bytes, ops, percent, etc.)
- Version Control: Save dashboard creation scripts in git
- Verify After Deploy: Always verify panels show data after deployment