Docs/Build & operate
Operator handbook

Operations

Operate Probara day to day: service lifecycle, health and readiness, Prometheus metrics, logs, migrations, retention, backup and restore, queue inspection, testing, and incident troubleshooting.

Service dependency map

ServiceHard dependenciesDegraded behavior
APIPostgreSQL; valid port/JWT configurationNATS publication/subscription is optional unless private-location authorization is enabled. Run-now checks and live status/alert updates degrade without NATS.
SchedulerPostgreSQL and NATSNo useful degraded mode. It schedules checks, ingests results, builds rollups, purges deleted monitors, enforces retention, and schedules mesh probes.
WorkerNATSCan execute checks without PostgreSQL. Database-free location workers intentionally omit DB; AI-RCA and notification consumers require DB.
AlerterPostgreSQLNATS is optional for synchronous evaluation/dispatch, but required for async notification publication and alert-event fan-out.
Status pagePostgreSQLNATS subscriber is optional; without it, cache invalidation waits for STATUS_PAGE_CACHE_TTL.
FrontendReachable API proxy targetStatic shell may load, but authenticated product workflows fail when API is unreachable.
Collector agent (probara-collector)Reachable HTTPS API and valid API keyScrapes host metrics locally but cannot export while disconnected; there is no remote disable — uninstall is operator-run.

Start, stop, inspect, and restart

Local-process workflow
make start-all-local
make restart-all-local
make stop-all-local

tail -f /tmp/probara-*.log
tail -f /tmp/probara-ui.log
Docker-backed workflow
make start-all
make ps
make logs
make logs-api
make logs-scheduler
make logs-worker
make logs-status
make restart-all
make stop-all
Kubernetes workload inspection
kubectl -n probara get pods,deployments,statefulsets,jobs
kubectl -n probara get events --sort-by=.lastTimestamp
kubectl -n probara logs deployment/probara-api --all-containers --tail=200
kubectl -n probara logs deployment/probara-scheduler --all-containers --tail=200
kubectl -n probara rollout status deployment/probara-api
kubectl -n probara rollout status deployment/probara-worker

Health, readiness, and listener truth

/healthz answers liveness, /readyz checks required dependencies, and /metrics exposes Prometheus data. The port carrying those routes differs by service.

ServiceLocal/Compose URLLiveness semanticsReadiness semantics
APIhttp://localhost:8080Process HTTP handler is alive.PostgreSQL responds within five seconds. NATS is not part of normal API readiness.
Schedulerhttp://localhost:9091Process operational listener is alive.Both PostgreSQL and NATS respond within five seconds.
Workerhttp://localhost:9092Healthy while NATS is connected or reconnecting; unhealthy after permanent closure.NATS responds, plus PostgreSQL when the worker was configured with DB access.
Alerterhttp://localhost:9094Alive; nil/optional NATS is accepted, but a permanently closed configured connection is unhealthy.PostgreSQL responds; NATS also responds when configured.
Status pagehttp://localhost:9093Operational listener is alive.PostgreSQL responds within five seconds.
NATShttp://localhost:8222/healthzBroker monitor endpoint.Use JetStream/consumer checks in addition to HTTP liveness.
Probe manually
curl -fsS http://localhost:8080/healthz
curl -fsS http://localhost:8080/readyz
curl -fsS http://localhost:9091/readyz
curl -fsS http://localhost:9092/readyz
curl -fsS http://localhost:9093/readyz
curl -fsS http://localhost:9094/readyz

make healthcheck

Worker also serves /mesh/echo on the metrics listener. When its HTTP_PORT differs, it starts a second listener containing only mesh echo and /healthz, allowing mesh exposure without publishing /metrics.

Prometheus metrics

Custom metrics use namespace probara and a sanitized service subsystem, for example probara_scheduler_loops_total. Go runtime and process collectors are also registered.

AreaKey metricsWhat to alert on
Scheduler loopprobara_scheduler_loops_total, monitors_scheduled_total, loop_duration_seconds, monitors_in_batchNo loop increments, duration approaching schedule interval, or sustained full batches.
Scheduler failuresjobs_publish_errors_total, db_errors_total, mesh_publish_errors_totalAny sustained nonzero rate.
Result ingestresults_ingested_total, result_ingest_errors_total, result_ingest_duplicates_total, mesh_results_ingested_totalPublish success without ingest growth, ingest errors, or unexpectedly high duplicates.
Retention and rollupsretention_cleanup_runs_total, retention_cleanup_rows_total, rollup_runs_total, rollup_rows_total, rollup_errors_total, rollup_duration_seconds, rollup_cursor_unixStale completeness watermark, error growth, or runs exceeding the maintenance window.
Monitor purgemonitor_purge_runs_total, monitor_purge_rows_total, monitor_purge_monitors_total, monitor_purge_errors_total, monitor_purge_run_duration_secondsGrowing errors or deleted monitors never becoming fully purged.
Worker executionprobara_worker_jobs_total{status=…}, job_duration_seconds, http_request_duration_seconds, http_errors_total{reason=…}Failure-rate changes, high latency, timeouts, and policy blocks.
Worker NATS/resultsnats_ack_total, nats_nak_total, result_publish_errors_totalNAK or publish-error growth and falling ACK throughput.
Status live updatesprobara_status_page_sse_subscriber_connectedZero when low-latency status invalidation is expected.
Inspect metrics locally
curl -fsS http://localhost:8080/metrics | grep '^probara_'
curl -fsS http://localhost:9091/metrics | grep '^probara_scheduler_'
curl -fsS http://localhost:9092/metrics | grep '^probara_worker_'
curl -fsS http://localhost:9093/metrics | grep '^probara_status_page_'

Logs and diagnostic context

  • Use LOG_LEVEL=debug temporarily for connection, scheduling, and readiness diagnosis; restore info after the incident.
  • Correlate monitor ID, tenant ID, location ID, alert ID, NATS stream/subject, and timestamps across services.
  • Never paste location-authenticated NATS URLs, API keys, SMTP passwords, webhook secrets, OIDC secrets, JWT secrets, or encrypted configuration keys into tickets or chat.
  • A warning that status-update publishing failed means core CRUD may work while public live invalidation is degraded.
  • A scheduler warning that RESULT_INGEST_ENABLED=false means workers can run checks but results will not enter PostgreSQL.
  • A plaintext/NoOp encryption warning requires immediate review before storing or updating sensitive configurations.
Focused Compose logs
docker compose logs --since=15m api scheduler worker alerter status-page
docker compose logs -f --tail=200 scheduler worker

Database migrations and administrator bootstrap

Run migrations
make migrate

# Direct local migration command
POSTGRES_URL='postgres://probara:probara@localhost:5432/probara?sslmode=disable' \
MIGRATIONS_PATH='./shared/db/migrations' \
go run ./cmd/migrate

The migration CLI requires POSTGRES_URL, defaults MIGRATIONS_PATH to ./migrations, and retries the initial database connection up to 30 times at one-second intervals. API startup also runs the shared migration set. The Helm migration Job is a post-install/post-upgrade hook.

Create or update the initial administrator
POSTGRES_URL='postgres://probara:probara@localhost:5432/probara?sslmode=disable' \
ADMIN_USERNAME='admin' \
ADMIN_PASSWORD='use-a-password-manager-generated-value' \
ADMIN_BCRYPT_COST='12' \
go run ./cmd/admin

Backup and restore

Compose PostgreSQL backup and restore
make db-backup       # writes ./backup.sql

# Inspect and copy the backup away from the workstation before maintenance.
ls -lh backup.sql

# Restore into the configured Compose database.
make db-restore
  1. Quiesce writes or capture a database-consistent snapshot appropriate to your PostgreSQL topology.
  2. Back up PostgreSQL, including schema migration state, tenants, monitor configuration, alert state, users, API keys, audit data, and encrypted ciphertext.
  3. Back up the complete encryption keyring separately. A database backup without the historical keys may be unrecoverable.
  4. Protect JetStream state or accept that queued checks/results/notifications may be replayed or lost after recovery.
  5. Preserve browser artifacts separately if they are part of your incident evidence policy.
  6. Restore into an isolated environment, run readiness and data-integrity checks, then perform a documented cutover.

The bundled PostgreSQL and NATS StatefulSets use PVCs but provide no scheduled backup controller, point-in-time recovery, replication, or cross-zone failover. Production recovery objectives require managed services, an operator, or external backup automation.

Retention, rollups, and deletion

Tenant telemetry retention
Each tenant’s data_retention_days controls raw check results and mesh results, and can tighten (never extend) raw agent metric-sample retention below METRIC_RAW_RETENTION_DAYS. A value of 0 preserves check/mesh results; the public validator otherwise accepts 30–3650 days. It is configured in tenant settings.
Audit retention
AUDIT_RETENTION_DAYS is a separate compliance control, defaults to 365 days, and uses 0 to retain forever.
Monitor purge
Soft-deleted monitors are later purged with dependent rows in bounded batches.
Rollups
Result ingestion marks each affected hour in a dirty-bucket ledger within the same transaction, and scheduler maintenance rebuilds marked buckets wholesale from raw results — late or redelivered rows can never be permanently skipped. A failed batch leaves its marks for the next run.

Scheduler checks once per minute whether the configured UTC cleanup hour is due, runs at most once per UTC date, uses a PostgreSQL advisory lock, and applies a 30-minute cleanup timeout. Batch size and maximum rows per run bound database pressure.

ControlDefaultOperator effect
RETENTION_CLEANUP_ENABLEDtrueEnable daily tenant telemetry cleanup.
RETENTION_CLEANUP_HOUR_UTC2Daily target hour, 0–23 UTC.
RETENTION_CLEANUP_BATCH_SIZE5000Delete batch size.
RETENTION_CLEANUP_MAX_ROWS_PER_RUN200000Per-run cap; backlog can require multiple days/runs.
AUDIT_RETENTION_DAYS365Separate hourly API audit pruner; 0 disables pruning.
METRIC_RAW_RETENTION_DAYS30Raw OTLP metric samples (daily partitions); a tenant's data_retention_days can tighten it. Hourly metric rollups are kept 400 days.
OTLP_MAX_SERIES_PER_MONITOR2000Metric-series cardinality cap per agent monitor; overflow points are rejected via OTLP partial_success.
OTLP_MONITOR_RATE_PER_MIN60OTLP ingest requests per minute per monitor; excess returns 429 with Retry-After.
MONITOR_PURGE_INTERVAL_SECONDS30Soft-delete purge cadence.
MONITOR_PURGE_BATCH_SIZE5000Child-row purge batch size.
MONITOR_PURGE_MAX_ROWS_PER_RUN200000Purge safety cap.

NATS and JetStream operations

ContractCode defaultPurpose
CHECK_JOBS / check.jobsWork-queue stream/subjectScheduler and API publish; workers consume.
CHECK_RESULTS / check.resultsWork-queue stream/subjectWorkers publish; scheduler persists.
ALERTS / alertsAlert event stream/subjectAlerter publishes; API live subscriber consumes.
NOTIFICATIONS / alerts.dispatch.>Optional work queueAsync alerter-to-worker dispatch.
AI_RCA / ai.rca.jobsAI work queueAPI publishes tenant RCA work; DB-connected workers consume.
Core NATS statuspage.updatesNon-JetStream fan-outCache/status update invalidation.
Inspect with the NATS CLI
nats --server "$NATS_URL" server check connection
nats --server "$NATS_URL" stream ls
nats --server "$NATS_URL" stream info CHECK_JOBS
nats --server "$NATS_URL" consumer ls CHECK_JOBS
nats --server "$NATS_URL" stream info CHECK_RESULTS
nats --server "$NATS_URL" consumer info CHECK_RESULTS result-ingest
  • Compare the actual stream subjects with every publisher and consumer environment before deleting or recreating streams.
  • Watch pending, redelivered, and unacknowledged counts; queue depth without scheduler/worker progress indicates a contract or connectivity failure.
  • CHECK_JOB_LEGACY_CONSUMERS causes scheduler startup to delete named filterless consumers. Review the list before renaming durable consumers.
  • JetStream PVC persistence does not replace a broker backup/replication strategy.
  • Core NATS status updates are ephemeral by design; status cache TTL is the fallback.

Synthetic-browser artifacts

  • Workers write failure screenshots beneath SYNTHETIC_BROWSER_ARTIFACTS_DIR; API reads them for authenticated retrieval. Trace and HAR requests currently emit warnings and do not create artifacts.
  • Compose mounts a shared named volume into API and worker.
  • Local-process services share the same host directory.
  • The current Helm workloads do not mount shared artifact storage, so cross-pod and cross-node retrieval is unreliable.
  • Set retention and access controls appropriate for screenshots because they can capture page content, tokens, personal data, or internal application state.

Verification and tests

Main Go module
make test
make lint

# make fmt is a check, not a formatter.
gofmt -w path/to/changed.go
make fmt
make vet
Frontend and chart
(cd web && npm run lint && npm run build)
(cd website && npm run typecheck && npm run build)

helm lint ./helm/monitoring-platform \
  --set secrets.adminJwtSecret='replace-with-32-plus-char-secret-value' \
  --set api.publicBaseURL='https://probara.example.com'
  • make test runs the Go module with race detection and writes coverage.out; it excludes script packages. There is no separate agent module or test suite — the collector is a prebuilt OpenTelemetry distribution (make build-collector-static).
  • Database integration tests use Testcontainers and require a working Docker daemon capable of starting PostgreSQL 16.
  • Run make test-cover to open the HTML coverage report.
  • Frontend changes require both lint and a production build; documentation site changes should at least run npm run typecheck and npm run build from website/.
  • Helm lint requires the chart’s mandatory JWT and public base URL values.
  • For a release candidate, verify scheduled checks, on-demand checks, all touched monitor types, result ingest, alerts, notification delivery, status pages, OIDC, imports, private locations, and backup restore.

Troubleshooting guide

SymptomLikely causeChecks and resolution
Configuration exits immediatelyMissing ports/JWT, short or placeholder JWT, invalid bool/int/CIDR, incomplete OIDC, or location ID/credential mismatchRead the first fatal line; compare the process environment with the configuration reference.
Compose command fails before containers startRequired ${ADMIN_JWT_SECRET:?…} or ${PUBLIC_BASE_URL:?…} interpolation is missingPopulate .env before any Compose-backed Make target.
PostgreSQL authentication fails after old local experimentsExisting volume was initialized with different credentialsBack up first. If disposable, docker compose down -v reinitializes all repo-scoped volumes and destroys local data.
Scheduled checks work, Run now does notAPI publishes on a different CHECK_JOB_SUBJECT than the workers consume (a custom override applied to only some services)Align CHECK_JOB_SUBJECT and stream configuration on API, scheduler, and worker; the shipped Compose file and Helm chart set all three identically. Inspect actual JetStream subjects.
Checks execute but no history appearsResult-ingest disabled, scheduler unavailable, result subject mismatch, DB failure, or ingest backlogCheck scheduler readiness/logs and results_ingested_total / result_ingest_errors_total; inspect CHECK_RESULTS consumer state.
Private location does not startMissing credential, invalid UUID, unsafe public NATS URL, broker authentication/TLS issue, or no external broker routeRegenerate deployment info; require both location ID and credential; test the TLS/WSS endpoint from that network.
Encrypted monitor fails only in schedulerScheduler lacks the current/historical encryption keyringProvide the identical PROBARA_SECRETS_KEY* set to scheduler. The shipped Compose file and Helm chart wire the base key; add rotation keys (_V2+) through the chart's top-level extraEnv so every service receives the same keyring.
Browser screenshot is 404/missingAPI and worker use different filesystems/pods or artifact expiredVerify identical artifact directory and shared storage. Helm does not currently provide it.
Email fails on port 587SMTP_USE_TLS=true selects implicit TLS rather than STARTTLSConfirm provider protocol. Use the correct implicit-TLS port or a supported non-TLS/private relay setting.
OIDC redirects or callback failsIssuer/client secret/callback mismatch, bad public base URL, or JIT tenant/role errorCompare the exact HTTPS callback with the IdP registration and inspect OIDC discovery.
SSO user loses roles (or gains none) after loginOIDC group mappings exist but the ID token carries no groups claim — groups scope not requested, wrong OIDC_GROUPS_CLAIM, or the IdP does not embed groups in the ID tokenCheck API logs for the missing-groups warning, add groups to OIDC_SCOPES, and verify the group-mapping requirements. Deleting all mappings restores manual role management.
Status page updates slowlyNATS status subscriber disconnected or update subject differsCheck sse_subscriber_connected, subscriber logs, shared subject, and cache TTL.
Frontend links point to old originNEXT_PUBLIC_* changed only at runtimeRebuild the frontend client bundle with the desired public values or use stable relative routing.
Prometheus cannot scrape API on 9090API has no separate metrics listenerScrape /metrics on API HTTP port 8080 or fix the workload/service topology.

Incident response checklist

  1. Identify the affected tenant, monitor/location, time window, and whether the failure is control plane, check execution, result persistence, alerting, or presentation.
  2. Check liveness and readiness on the real service listeners; preserve logs and relevant metrics before restarting.
  3. Inspect PostgreSQL availability and NATS stream/consumer state without deleting consumers or messages.
  4. Compare deployed queue variables and encryption-key versions across all replicas.
  5. Reduce blast radius with reversible actions: pause a broken monitor/policy, scale a healthy worker fleet, or disable optional async paths.
  6. Restore service, verify end-to-end from schedule through status/alert delivery, then reconcile delayed or duplicate jobs.
  7. Record the operator action in incident notes and retain the audit/log/artifact evidence according to policy.