Parent guide: AGENTS.md — product overview, architecture, domain model, global conventions Related: api/AGENTS.md (API definitions) · agent/AGENTS.md (client agent) · qan-api2/AGENTS.md (QAN backend)
pmm-managed is the core backend service of PMM Server. It manages configuration of server-side components (VictoriaMetrics, Grafana, QAN, VMAlert, Alertmanager), maintains the inventory of monitored nodes/services/agents, orchestrates backups, runs advisor checks, handles HA consensus, and exposes gRPC/REST APIs consumed by pmm-admin, pmm-agent, and the UI.
pmm-admin (CLI) ──→ gRPC/REST API ──→ pmm-managed ──→ PostgreSQL (inventory, settings)
PMM UI ───────────→ gRPC-Gateway ──→ ──→ VictoriaMetrics (scrape config)
pmm-agent ────────→ bidirectional gRPC stream ──→ ──→ Grafana API (dashboards, users)
──→ Supervisord (process management)
──→ qan-api2 (QAN forwarding)
──→ VMAlert (alerting rules)
Services follow a consistent dependency-injection pattern:
type Service struct {
db *reform.DB
l *logrus.Entry
// other dependencies as interfaces
}
func New(db *reform.DB, logger *logrus.Entry, ...) *Service {
return &Service{db: db, l: logger, ...}
}All services are composed and wired in managed/cmd/pmm-managed/main.go.
- gRPC (port 7771) — primary API protocol
- REST/JSON (port 7772) — gRPC-Gateway, auto-generated from proto definitions
- Debug (port 7773) —
/debug/metrics,/debug/pprof,/debug/vars
gRPC server implementations live in services/*/grpc/ subdirectories. They delegate to the parent service package for business logic.
| Entity | Table | Model File | Description |
|---|---|---|---|
| Node | nodes |
node_model.go |
Host or target: generic, container, remote, remote_rds, remote_azure_database |
| Service | services |
service_model.go |
DB/app: mysql, mongodb, postgresql, proxysql, haproxy, external, valkey |
| Agent | agents |
agent_model.go |
Monitoring agent: pmm-agent, exporters, QAN agents, vmagent, etc. |
| Settings | settings |
settings_model.go |
Server configuration (JSONB, singleton row) |
| BackupLocation | backup_locations |
— | S3/local backup storage targets |
| Artifact | artifacts |
— | Backup artifacts |
| ScheduledTask | scheduled_tasks |
— | Scheduled backup tasks |
| RestoreHistory | restore_history |
— | Backup restore records |
| Role | roles |
— | Access control roles |
Node (1) ──→ (N) Service
Service (1) ──→ (N) Agent (via service_id)
Node (1) ──→ (N) Agent (via runs_on_node_id)
PMM Agent (1) ──→ (N) Child Agent (via pmm_agent_id)
PMM uses reform (NOT gorm) for PostgreSQL:
//go:generate go tool reform
//reform:nodes
type Node struct {
NodeID string `reform:"node_id,pk"`
NodeName string `reform:"node_name"`
}Key conventions:
- Models:
managed/models/*_model.gowith//go:generatedirectives - Generated:
managed/models/*_reform.go(never edit) - CRUD helpers:
managed/models/*_helpers.go - Always accept
reform.Querierparameter (works with both*reform.DBand*reform.TX) - Check
reform.ErrNoRowsexplicitly for "not found" - Use
models.Find*()helpers, notq.Reload()orq.SelectOneFrom()directly - Transactions:
db.InTransactionContext(ctx, nil, func(tx *reform.TX) error { ... }) - Schema migrations in
models/database.go(databaseSchemamap, versioned)
- Environment variables:
utils/envvars.ParseEnvVars()parsesPMM_*vars;server.UpdateSettingsFromEnv()persists to DB - Database settings:
settingstable (JSONB);models.GetSettings(),models.UpdateSettings() - YAML config:
services/configloads/etc/percona/pmm/pmm-managed.yml(deprecated, mainly telemetry) - CLI flags: Kingpin flags for PostgreSQL DSN, VictoriaMetrics URL, HA config, debug ports
| Package | Responsibility |
|---|---|
models |
Domain types, reform models, DB schema migrations, CRUD helpers |
services/agents |
Agent registry, bidirectional gRPC handler, state tracking |
services/inventory |
Nodes, Services, Agents CRUD with validation |
services/management |
High-level add/remove operations (combines inventory + agent setup) |
services/server |
Settings, version, update logic, logs |
services/backup |
Backup orchestration, compatibility checks, PBM PITR |
services/checks |
Advisor check execution via Starlark |
services/alerting |
Alert template management |
services/victoriametrics |
VictoriaMetrics scrape config generation from agent/service inventory |
services/vmalert |
VMAlert alerting rules generation |
services/grafana |
Grafana API client (users, dashboards, annotations) |
services/supervisord |
Supervisord config file generation and process control |
services/ha |
Raft consensus, gossip protocol, leader election |
services/telemetry |
Telemetry data collection and reporting to Percona |
PMM supports HA via Raft consensus (services/ha/):
- Distributed state using
hashicorp/raft - Agent states synchronized across nodes via gossip
- Leader election determines which node runs certain operations (e.g., scheduled backups)
- Prefer modern Go idioms (context, error wrapping with
%w) - Use modern slice helpers (
slices.Contains), range loops - Use
anyinstead ofinterface{} - Define small interfaces in
deps.gofiles for dependency injection and mocking - Use
status.Error()with proper gRPC codes for API errors - Check
reform.ErrNoRowsfor "not found" scenarios - Wrap errors:
fmt.Errorf("descriptive context: %w", err) - Return early on errors to avoid deep nesting
- Use
errors.Is()/errors.As()for error type checking - For new or updated code, prefer the standard
errorspackage overgithub.com/pkg/errors(existing uses may remain until refactored) - Use structured logging:
s.l.WithField("key", value).Error("message") - Pass
*logrus.Entry(not*logrus.Logger) - Use RESTful conventions in proto HTTP annotations
- Don't connect to a real database in unit tests — use
github.com/DATA-DOG/go-sqlmockto mock SQL queries; reservetestdb.Openfor integration tests that genuinely require fixtures or migrations - Don't use
gormor other ORMs — onlyreform - Don't edit generated files (
*_reform.go,*.pb.go,*.pb.gw.go, swagger specs) - Don't skip
make genafter proto/model changes - Don't comment on every line — only where clarity is needed
- Don't inline comments (
code // comment) — put comments on separate lines - Don't use named return values in functions
- Don't commit test binaries or artifacts
- Don't create subshells in Makefiles without reason
- Use
testify/assertandtestify/require - Mock generation via
mockery(config in.mockery.yaml) - Interface-based deps in
deps.gofiles enable mocking mock_*_test.gofiles generated by mockery- Mock DB with
go-sqlmock(wraps areform.DB) for unit tests; usetestdb.Openonly when fixtures or migrations are required - Run:
make test(in managed/) ormake test-common(from root)
- Located in
/api-tests/(separate directory) - Run against live PMM Server:
make api-test
testdata/pg/— PostgreSQL fixturestestdata/victoriametrics/— VictoriaMetrics configstestdata/updater/— Update test fixtures
- Protocol Buffers —
make genfrom repo root - reform —
//go:generate go tool reformon model files - mockery — mock generation per
.mockery.yaml - swagger — API docs from proto annotations
Always run make gen after modifying .proto files, reform models, or interface signatures.
managed/cmd/pmm-managed/main.go— application bootstrap, all service wiringmanaged/models/database.go— database schema and migrationsmanaged/models/node_model.go,service_model.go,agent_model.go— core domain modelsmanaged/services/agents/registry.go— agent registration and lifecyclemanaged/services/agents/grpc/agent_server.go— bidirectional agent stream handlermanaged/services/inventory/grpc/— inventory API implementationsmanaged/services/ha/— HA/Raft implementationmanaged/utils/envvars/parser.go— environment variable parsingdocker-compose.yml— development environmentMakefile,Makefile.include— build and development targets