Millions of lines of code, hundreds of services, dozens of packages — distilled into a single page that gets you productive on day one.
How the entire system fits together — from browser to data source.
graph TB
subgraph Client["Browser"]
UI["React SPA
(public/app/)"]
end
subgraph Server["Go Backend (pkg/)"]
API["HTTP API Layer
pkg/api/"]
MW["Middleware
(Auth, CSRF, Logging)"]
SVC["Service Layer
pkg/services/"]
APPS["Modular Apps
apps/"]
PLUG["Plugin Host
pkg/plugins/"]
end
subgraph Data["Data Layer"]
DB["SQL Database
(SQLite / Postgres / MySQL)"]
CACHE["Cache
(In-memory / Redis)"]
end
subgraph External["External"]
DS["Data Sources
(Prometheus, Loki, etc.)"]
BP["Backend Plugins
(gRPC)"]
end
UI -- "HTTP / WebSocket" --> MW
MW --> API
API --> SVC
API --> APPS
SVC --> DB
SVC --> CACHE
SVC --> PLUG
PLUG -- "gRPC" --> BP
API -- "Proxy" --> DS
style Client fill:#1a2332,stroke:#3b82f6,color:#e0e0e6
style Server fill:#1a2520,stroke:#10b981,color:#e0e0e6
style Data fill:#2a2518,stroke:#fbbf24,color:#e0e0e6
style External fill:#2a1a24,stroke:#f472b6,color:#e0e0e6
The big picture in 30 seconds.
Grafana is a universal dashboard for your infrastructure. Imagine you have 10 different databases, monitoring tools, and log systems. Instead of checking each one separately, Grafana connects to all of them and lets you build visual dashboards, set up alerts ("tell me if the server CPU goes above 90%"), and explore data interactively.
Users connect data sources (Prometheus, Loki, PostgreSQL, etc.), build dashboards with visual panels (graphs, tables, gauges), and create alerting rules that fire notifications when thresholds are crossed.
How to think about this codebase without panicking.
You are not expected to understand all of it. Most engineers work in one area at a time. The codebase is a modular monolith: many independent services in the same repository. Think of it like an apartment building — you live in one apartment, you share hallways and utilities, but you don't need to know what's inside every unit.
Your first task will probably touch 2-5 files. Start there. Expand outward only when you need to.
What users see and interact with. React components, pages, forms. Lives in public/app/.
Handles data, auth, and business logic. Go services in pkg/.
Self-contained pieces for specific data sources or panel types. In public/app/plugins/ and pkg/tsdb/.
What you need to know about the product — no code required.
As a PM, your job is to understand what the product does, who uses it, how features are shipped, and where to find what you need. This section gives you that map.
The core experience. Users build visual dashboards with panels (graphs, tables, gauges). Each panel queries a data source. Dashboards are saved as JSON. This is what most users interact with daily.
Ad-hoc querying without dashboards. Engineers use this to investigate incidents, debug issues, and explore metrics/logs/traces. It's the "search engine" for infrastructure data.
Rules that fire when conditions are met ("CPU > 90% for 5 min"). Notifications go to Slack, email, PagerDuty, etc. This is what wakes people up at 3 AM.
Connections to external systems. Grafana supports 100+ data sources through plugins. Each has its own query editor UI. This is the "integration layer."
Extend Grafana without modifying core. Plugin types: panels (new visualizations), data sources (new integrations), apps (full sub-applications). Community + internal plugins.
User management, orgs, teams, RBAC permissions. Supports SSO (SAML, OAuth, LDAP). Organizations isolate tenants. Roles: Viewer, Editor, Admin.
graph LR
A["Feature flag
created in code"] --> B["Dev builds behind
flag (off by default)"]
B --> C["Flag enabled for
internal/canary"]
C --> D["Gradual rollout
to users"]
D --> E["Flag removed,
feature is GA"]
style A fill:#201a2e,stroke:#a78bfa,color:#e0e0e6
style E fill:#1a2c2a,stroke:#10b981,color:#e0e0e6
pkg/services/featuremgmt/. When you want to know "is feature X shipped?" — check if its flag is still behind a toggle or if it's been removed (meaning it's GA).
| What you need | Where to look |
|---|---|
| List of all feature flags | pkg/services/featuremgmt/ — every flag with name and description |
| What the UI looks like | Run the app locally (make run + yarn start) or check staging |
| What API endpoints exist | pkg/api/api.go — every HTTP endpoint registered |
| What data we store | pkg/services/sqlstore/migrations/ — database schema history |
| User-facing configuration | conf/defaults.ini — every setting with defaults and comments |
| Frontend routes / pages | public/app/routes/routes.tsx — URL to page mapping |
| User docs | docs/ directory — user-facing documentation source |
OSS = open-source, free. Enterprise = on-prem paid features (RBAC, SAML, reporting). Cloud = Grafana-hosted SaaS. The codebase uses build tags (oss, enterprise) to separate features.
Loading dashboards, data sources, and alerts from YAML files instead of the UI. Used for "infrastructure as code" — teams version-control their Grafana setup alongside their app code.
The next-generation dashboard engine. Replaces the older rendering system with a more composable, flexible framework. Active migration — new dashboards use Scenes, old ones are being ported.
If you're new to backend development or these specific technologies, start here.
A set of URLs ("endpoints") the frontend calls to get or send data. Example: GET /api/dashboards/uid/abc123 returns a dashboard as JSON.
Code that runs before your request reaches the handler. Like airport checkpoints: identity (auth), luggage scan (CSRF), then logging.
Instead of a service building its own dependencies, they're passed in from outside. Makes testing and swapping easier. Grafana uses Google Wire.
Lets you work with database tables using Go structs instead of raw SQL. Grafana uses xorm.
An efficient way for two programs to talk. Backend plugins run as separate processes and communicate with the main server over gRPC.
Central state store for the frontend. Instead of passing data through 15 component levels, components read/write to one shared store.
One Git repo containing multiple projects — backend, frontend, packages, plugins, docs, and tests together. Cross-cutting changes in one PR.
On/off switches for features. Code gets merged to main but only activates when the flag is on. Defined in pkg/services/featuremgmt/.
Versioned database schema changes. On startup, Grafana checks which migrations ran and applies new ones. Keeps every instance's DB in sync.
High-level system design — how the pieces fit together.
graph TB
subgraph Client["Browser"]
UI["React SPA
(public/app/)"]
end
subgraph Server["Go Backend (pkg/)"]
API["HTTP API Layer
pkg/api/"]
MW["Middleware
(Auth, CSRF, Logging)"]
SVC["Service Layer
pkg/services/"]
APPS["Modular Apps
apps/"]
PLUG["Plugin Host
pkg/plugins/"]
end
subgraph Data["Data Layer"]
DB["SQL Database
(SQLite / Postgres / MySQL)"]
CACHE["Cache
(In-memory / Redis)"]
end
subgraph External["External"]
DS["Data Sources
(Prometheus, Loki, etc.)"]
BP["Backend Plugins
(gRPC)"]
end
UI -- "HTTP / WebSocket" --> MW
MW --> API
API --> SVC
API --> APPS
SVC --> DB
SVC --> CACHE
SVC --> PLUG
PLUG -- "gRPC" --> BP
API -- "Proxy" --> DS
style Client fill:#1a2332,stroke:#3b82f6,color:#e0e0e6
style Server fill:#1a2520,stroke:#10b981,color:#e0e0e6
style Data fill:#2a2518,stroke:#fbbf24,color:#e0e0e6
style External fill:#2a1a24,stroke:#f472b6,color:#e0e0e6
pkg/services/ are wired together via Google Wire in pkg/server/wire.go. Newer services are being extracted into apps/ using the Grafana App SDK.
The major building blocks.
200+ RESTful endpoints under /api. Routes in pkg/api/api.go, handlers in pkg/api/.
Domain services in pkg/services/ — dashboards, users, alerting, data sources. Each exposes an interface, injected via Wire.
Backend plugins (gRPC), frontend plugins (JS bundles). Management in pkg/plugins/.
Newer services in apps/ with Kubernetes-style APIs. Dashboard, folder, alerting, IAM.
React app in public/app/. Redux Toolkit, React Router, Emotion CSS-in-JS.
@grafana/ui, @grafana/data, @grafana/runtime, @grafana/scenes.
xorm ORM — SQLite (default), PostgreSQL, MySQL. Auto-migrations on startup.
INI-based in conf/defaults.ini. Env var overrides. Parsed by pkg/setting/.
How a user request travels through the system — follow the numbers.
sequenceDiagram
participant B as Browser (React)
participant M as Middleware
participant A as API Handler
participant S as Service Layer
participant DB as SQL Database
participant P as Plugin / Data Source
B->>M: 1. HTTP Request (e.g. POST /api/ds/query)
M->>M: 2. Auth check, CSRF, logging
M->>A: 3. Route to handler (pkg/api/)
A->>S: 4. Call domain service
S->>DB: 5. Read config (data source settings)
DB-->>S: 6. Config response
S->>P: 7. Forward query (gRPC / HTTP proxy)
P-->>S: 8. Query results (data frames)
S-->>A: 9. Return data frames
A-->>B: 10. JSON response → rendered as panels
Step 1-3: Your browser sends a request. Middleware checks "is this user logged in?" and "is this request safe?" If yes, it reaches the API handler.
Step 4-6: The handler asks the service layer for the data. The service checks the database for settings (like which Prometheus server to query).
Step 7-10: The service forwards the data query to the external data source, gets back time series, and returns it as JSON to the browser for rendering.
How your daily edit-build-test cycle works.
graph LR
A["Edit code"] --> B{"Frontend
or Backend?"}
B -->|Frontend| C["Webpack auto-reloads
(yarn start)"]
B -->|Backend| D["Air auto-rebuilds
(make run)"]
C --> E["Check browser
localhost:3000"]
D --> E
E --> F["Run tests"]
F --> G{"Tests pass?"}
G -->|Yes| H["Commit & push"]
G -->|No| A
style A fill:#1a2332,stroke:#3b82f6,color:#e0e0e6
style H fill:#1a2c2a,stroke:#10b981,color:#e0e0e6
Edit a .tsx file. Webpack detects the change and reloads your browser automatically. Results in seconds.
Edit a .go file. Air detects the change, recompiles, and restarts the server. Takes 10-30 seconds.
You need two terminals. Terminal 1: make run. Terminal 2: yarn start. The backend proxies frontend requests.
Team structure, code ownership, PR patterns, and deployment — what you need to lead here.
You need to know: which teams own which parts of the code, how changes flow from PR to production, where risk concentrates, and what your engineers' daily experience looks like. You don't need to write Go.
The codebase is divided into domain areas. Each area typically maps to a squad or team. Here's the territory:
| Domain Area | Backend Code | Frontend Code |
|---|---|---|
| Dashboards | apps/dashboard/, pkg/services/dashboards/ | public/app/features/dashboard/ |
| Alerting | apps/alerting/, pkg/services/ngalert/ | public/app/features/alerting/ |
| Data Sources | pkg/tsdb/, pkg/plugins/ | public/app/features/datasources/ |
| Explore | pkg/services/explore/ | public/app/features/explore/ |
| Auth & IAM | apps/iam/, pkg/services/auth*/ | public/app/features/auth/ |
| Admin / Users | pkg/services/user/, org/ | public/app/features/admin/ |
| Plugins | pkg/plugins/ | public/app/features/plugins/ |
| Infrastructure | pkg/infra/ (logging, DB, cache) | packages/ (@grafana/*) |
If a dashboard PR touches alerting code, it's a red flag. Services should be independent. Cross-domain changes need extra review and coordination.
New features should be behind a toggle. Check that risky changes can be turned off without a deploy. Look for featuremgmt.Flag* usage.
Database migrations are irreversible in production. PRs touching sqlstore/migrations/ deserve careful review. Ask: can this be rolled back?
Changes to pkg/server/wire.go affect service startup order and dependencies. They impact everyone. Make sure make gen-go was run.
oss (open-source), enterprise (paid on-prem), and pro (additional features). Enterprise features compile only when the tag is active. Most dev work is oss.
Backend: go test ./... with coverage flags. Frontend: yarn jest --coverage. CI runs tests on every PR. No merge without green CI.
Go: make lint-go. TypeScript: yarn typecheck + yarn lint. These catch common bugs before review. Pre-commit hooks available via make lefthook-install.
Common tasks mapped to the files you need.
Step-by-step for the tasks you'll do most often.
pkg/api/api.gopkg/api/pkg/services/{domain}/pkg/api/http_server.gomake gen-go to regenerate Wirepkg/api/{handler}_test.gopublic/app/features/{feature}/public/app/routes/routes.tsxuseStyles2 for Emotion CSS-in-JS stylinggetBackendSrv().fetch() or RTK Querypkg/services/featuremgmt/make gen-feature-togglesfeatures.IsEnabled(ctx, featuremgmt.FlagMyFeature)config.featureToggles.myFeatureconf/custom.ini under [feature_toggles]pkg/services/sqlstore/migrations/make devenv sources=postgresWhere to find things.
| Path | What lives here |
|---|---|
| pkg/api/ | HTTP route registration and API handlers |
| pkg/services/ | Backend domain services (dashboards, users, alerting, auth, etc.) |
| pkg/server/ | Server bootstrap and Wire dependency injection |
| pkg/tsdb/ | Built-in time series data source query backends |
| pkg/plugins/ | Plugin system — loader, registry, gRPC host |
| pkg/infra/ | Infrastructure — logging, metrics, DB access, caching |
| apps/ | Modular backend services (App SDK pattern) |
| public/app/features/ | Frontend feature code — dashboard, explore, alerting, etc. |
| public/app/core/ | Shared frontend services, components, and utilities |
| packages/ | Shared npm packages (@grafana/ui, @grafana/data, etc.) |
| conf/ | Default and custom configuration files |
Start here when you need to understand something specific.
Your first day — follow these steps to get up and running.
Node.js v24 (nvm use), Go 1.25, GCC, Corepack (corepack enable). Then yarn install --immutable.
make run — builds and starts on localhost:3000. Login: admin / admin. First build ~3 min.
Second terminal: yarn start. Webpack dev server with hot reload. First compile ~45s.
Open localhost:3000, create a dashboard, add a panel, try Explore. Map code to features.
pkg/server/wire.go, pkg/api/api.go, public/app/routes/routes.tsx. Get a feel for the patterns.
Backend: go test ./pkg/services/xxx/. Frontend: yarn jest --no-watch path/to/file.
Find something safe — a label, tooltip, log message. Build, test, see it work. Build confidence before big tasks.
# Quick reference — common commands make run # Backend with hot reload yarn start # Frontend dev server go test -run TestName ./pkg/services/xxx/ # Run specific backend test yarn jest --no-watch path/to/file # Run specific frontend test make lint-go # Go linter yarn lint:fix # ESLint auto-fix yarn typecheck # TypeScript type checking make gen-go # Regenerate Wire DI make gen-cue # Regenerate CUE schemas make gen-feature-toggles # Regenerate feature flag code make devenv sources=postgres,loki # Start backing services
A checklist for getting oriented without drowning in code.
make run + yarn start) and explore the UI for 30 minmake lefthook-installpkg/server/wire.go — not every line, but notice how services are registered. This is the dependency map.pkg/services/featuremgmt/ — which features are behind toggles?conf/defaults.ini — understand what's configurable and what the defaults aredocs/ — these are the docs your users seeWhen something breaks, here's where to look.
| Symptom | Where to look | What to try |
|---|---|---|
| Backend won't compile | Terminal (make run) | Read the Go error — it gives file and line number. Common: missing import or typo. |
| Frontend won't compile | Terminal (yarn start) | Webpack shows the error. Also try yarn typecheck. |
| API returns 401/403 | pkg/middleware/ | Check auth. Logged in? API key valid? Check middleware logs. |
| API returns 500 | Backend terminal logs | Search for "error" in the Go output — full stack trace. |
| Blank page / JS errors | Browser DevTools Console | F12 > Console for JS errors, Network tab for failed API calls. |
| Data source query fails | pkg/tsdb/{datasource}/ | Use the Query Inspector in the Grafana UI (panel title > Inspect > Query). |
| Wire generation fails | pkg/server/wire.go | Usually a circular dependency. Wire's error message shows the cycle. |
| Test fails unexpectedly | The test file itself | Run with -v for verbose. Snapshot test? Try yarn test -u. |
Terms you'll encounter in conversation and in the codebase.
make gen-go.make gen-cue.@grafana/scenes). Replacing the legacy dashboard engine.pkg/services/ngalert/.apps/. Kubernetes-style APIs. The architectural direction.featuremgmt. Enables gradual rollout without deploys.Questions every new hire asks in the first week.
yarn start will run), but most pages will show errors because they need API data. You almost always need both make run (backend) and yarn start (frontend) running simultaneously in two terminals.make devenv sources=postgres.data/grafana.db (relative to the repo root) and restart the server. It will recreate a fresh database with migrations applied. Login resets to admin / admin.make run compiles the entire Go binary with debug symbols (-gcflags all=-N -l), which takes ~3 minutes. Subsequent rebuilds via hot-reload (Air) only recompile changed packages and are much faster. Frontend first compile (yarn start) takes ~45 seconds.conf/custom.ini and add:[feature_toggles]myFeatureName = truepkg/services/featuremgmt/./explore maps to public/app/features/explore/. Routes are defined in public/app/routes/routes.tsx — search for the URL path and it will point you to the component.pkg/api/api.go for the URL path (e.g., /api/dashboards). The route registration points to the handler function. The handler lives in pkg/api/.make gen-go — after changing service constructors or Wire dependenciesmake gen-cue — after changing CUE schemas in kinds/make gen-feature-toggles — after adding/changing feature flagspkg/services/ is the original pattern — services wired together in one process via Google Wire. apps/ is the newer pattern using the Grafana App SDK with Kubernetes-style APIs. New services go in apps/, but most existing code is still in pkg/services/. Both coexist.This guide was generated by AI. Here's what that means.
Claude Code analyzed the repository structure, file contents, and code patterns to produce this guide. It's a best-effort interpretation — not a reviewed specification.
Things that trip up new engineers.
make gen-go. Skip it and the code won't compile.
yarn test never finishes. Use yarn jest --no-watch path/to/file.
make gen-feature-toggles after adding flags. Enable in conf/custom.ini.
make run AND yarn start for the full dev experience.
oss (default), enterprise, pro. You'll mostly work with oss.
yarn workspace @grafana-plugins/<name> dev.