Skip to content

Simple guide · Overview

Page status: Active

NutsNews Grafana Cloud Observability

Architecture status: Grafana Cloud resources are centralized in

Visual overview

Primary diagram

System map

Protected source changes feed Grafana and host telemetry controls; bounded notification repeats and selected logs preserve useful alerts while real backup failures remain visible.

Render the repository-owned system map when you need it.

Diagram is not rendered yet.

View as text
NutsNews Grafana Cloud observability controls

Protected source changes feed Grafana and host telemetry controls; bounded notification repeats and selected logs preserve useful alerts while real backup failures remain visible.

flowchart LR
  accTitle: NutsNews Grafana Cloud observability controls
  accDescr: Protected source changes feed Grafana and host telemetry controls; bounded notification repeats and selected logs preserve useful alerts while real backup failures remain visible.
  inputs["Protected synthetic inputs"] --> validator["Value-free validator\nregion endpoint + API bounds + quota ceiling"]
  source["Infra OpenTofu source"] --> plan["Protected read-only plan\nand drift check"]
  validator --> plan
  plan --> evidence["Immutable evidence\ninputs + dashboards + alerts\nPrometheus + Loki"]
  plan --> review{"Reviewed mutation needed?"}
  review -->|"No"| stop["Remain read-only"]
  review -->|"Yes"| apply["Protected Grafana apply"]
  apply --> grafana["Grafana folders, dashboards,\nalerts, quotas, synthetics"]
  grafana --> notify["Prompt firing and recovery mail\n4h critical, 12h major, 24h warning"]
  notify --> operator["Operator investigates actionable signal"]
  producers["VPS and backend Alloy"] --> selected["Selected metrics and logs\ncanary metrics, broker logs"]
  selected --> grafana
  backup["VPS backup provider write failure"] --> realAlert["Failed, unverified, and stale\nbackup alerts stay active"]
  realAlert --> notify

NutsNews Grafana Cloud observability controls

Fullscreen diagram view.

Architecture status: Grafana Cloud resources are centralized in ramideltoro/nutsnews-infra. Backend and VPS hosts are telemetry producers; they do not own Grafana resource provisioning. The same boundary is reflected in Architecture and Worker-Uplift Operation Map.

This explains the Grafana Cloud observability layer for NutsNews hosts: Alloy on hosts, Grafana-managed dashboards and alerts, bounded Synthetic Monitoring, and free-tier guardrails.

NutsNews has a planned Grafana Cloud observability path that stays GitOps-managed.

There are two halves:

  1. ramideltoro/nutsnews-infra installs and configures Grafana Alloy on the VPS through the protected Ansible workflow.
  2. The same infra repo manages Grafana Cloud folders, dashboards, alert rules, quota alerts, backend imports, and optional Synthetic Monitoring checks through OpenTofu.

The VPS side is read-only. Alloy collects host metrics, systemd state, selected service logs, auth/security logs with redaction, Caddy JSON access/error logs, Docker/Compose logs for NutsNews runtime containers, backup/reporting logs, Ops Portal logs, and a small set of NutsNews status metrics derived from the existing read-only Ops Portal JSON. Docker/cAdvisor container metrics stay disabled by default because the previous container metrics path tried to reach containerd.sock and produced permission errors.

The backend host is also a telemetry producer. ramideltoro/nutsnews-backend keeps only backend Prometheus remote_write and Loki push credentials for its host collector. Its existing NutsNews Backend Ops dashboards and NutsNews Backend Guardrails alert group are imported and managed from ramideltoro/nutsnews-infra; backend direct Grafana provisioning is retired after import plus live query/alert verification passes.

The backend retirement record is ramideltoro/nutsnews-backend/docs/backend-grafana-handoff.json. Backend CI runs scripts/validate_backend_grafana_handoff.py to confirm the backend keeps only telemetry producer duties, preserves the existing dashboard and alert UIDs, and does not regain a workflow path that can create, update, or delete Grafana Cloud resources.

The worker-uplift telemetry scope is approved separately in NutsNews Worker-Uplift Telemetry Scope. RabbitMQ metrics, worker service metrics, and structured logs are required. Full trace export and exemplars are deferred, and article/model payloads are forbidden in telemetry.

This does not add a shell button, restart button, package installer, portal mutation path, or broad workflow command runner. Production changes still go through commits, PRs, checks, merge, and protected apply.

The rollout has separate credentials for separate jobs:

Credential typeUsed byPurpose
Grafana Cloud Access Policy tokenAnsible-managed Alloy on each producing hostWrite telemetry to Grafana Cloud metrics and logs
Grafana service account tokenOpenTofu in GitHub ActionsManage folders, dashboards, alert rules, and Synthetic Monitoring checks

Grafana management/service-account credentials stay only in ramideltoro/nutsnews-infra. Do not reuse the service account token for telemetry writes. Do not commit Grafana URLs, usernames, tokens, tenant IDs, backend config, Synthetic Monitoring targets, or tfvars.

The high-level flow:

flowchart LR
pr["Infra PR"] --> ci["CI validation"]
ci --> merge["Merge to main"]
merge --> tofu["Protected Grafana Cloud OpenTofu apply"]
tofu --> grafana["Folders, dashboards,\nalerts, quota guardrails,\nbackend imports, synthetics"]
merge --> ansible["Protected Ansible apply"]
ansible --> alloy["Grafana Alloy on VPS"]
backend["Backend host Alloy"] --> metrics
backend --> logs
alloy --> metrics["Grafana Cloud Metrics"]
alloy --> logs["Grafana Cloud Logs"]
metrics --> dashboards["NutsNews Observability dashboards"]
logs --> dashboards
dashboards --> operator["Maintainer investigates\nfrom Grafana and runbooks"]

The infra implementation keeps observability useful without making Grafana Cloud a cost surprise:

  • Alloy scrape interval defaults to 60 seconds.
  • Host metrics come from Alloy’s Unix exporter.
  • Container metrics do not come from cAdvisor by default.
  • Docker logs are collected for the nutsnews-service-foundation and nutsnews-app Compose projects through the Docker API socket.
  • Docker state still appears through the Ops Portal collector and low-cardinality textfile metrics.
  • High-cardinality labels such as container IDs, image IDs, request IDs, user IDs, raw IPs, and full dynamic paths are dropped or avoided.
  • Logs are redacted, size-limited, and rate-limited before leaving the VPS.
  • Debug and trace logs are intentionally dropped.
  • Rotated compressed logs and stale logs are ignored.
  • Synthetic Monitoring checks are disabled until protected variables provide target URLs and probe IDs.
  • Synthetic API checks must stay within Grafana’s 10-second through 60-minute interval range.
  • A value-free validator and OpenTofu both block plan/apply if configured API checks exceed 90% of the current free API execution assumption, preserving a 10% hard buffer.
  • Browser Synthetic Monitoring and Grafana Cloud k6 execution are not enabled by default.
  • Worker-uplift telemetry uses the approved scope in ramideltoro/nutsnews-infra/terraform/grafana-cloud/catalog/worker-uplift-telemetry-scope.json.

Grafana’s current public free-tier assumptions used by the docs and module are:

AreaCurrent assumption
Metrics10,000 active series per month
Logs50 GB ingested per month with 14-day retention
Synthetic API tests100,000 executions per month
Synthetic browser tests10,000 executions per month
k6500 virtual user hours per month

Always verify the live Grafana pricing page before adding more telemetry: https://grafana.com/pricing/

Grafana Cloud usage and limit metrics are queried through the grafanacloud-usage datasource. Grafana documents the grafanacloud_instance_metrics_limits, grafanacloud_logs_instance_limits, and related usage metrics here: https://grafana.com/docs/grafana-cloud/cost-management-and-billing/manage-invoices/understand-your-invoice/usage-limits/

flowchart TB
vps["NutsNews VPS"] --> host["Linux host metrics\nCPU, load, memory, swap,\nfilesystem, disk IO, network,\nfile descriptors, conntrack,\nprocesses, boot time, time sync"]
vps --> systemd["systemd services and timers"]
vps --> docker["Docker and Compose logs\nthrough Alloy Docker API discovery"]
vps --> dockerState["Docker and Compose state\nthrough Ops Portal/textfile metrics"]
vps --> files["journald, auth, Caddy JSON,\napp/service, backup,\nreporting, Ops Portal logs"]
vps --> portal["Ops Portal status JSON"]
portal --> textfile["low-cardinality\nNutsNews textfile metrics"]
host --> alloy["Grafana Alloy"]
systemd --> alloy
docker --> alloy
dockerState --> textfile
files --> alloy
textfile --> alloy
alloy --> gc["Grafana Cloud"]

Alloy leaves vps_service_foundation_grafana_alloy_collect_docker set to false by default. That disables the cAdvisor exporter and avoids the containerd metrics path that previously produced permission errors. The current production model is:

  • Alloy host, systemd, journald/file, and textfile telemetry.
  • Alloy Docker log collection for NutsNews Compose projects only.
  • Docker container state, health, restart counts, and storage pressure from the root-run Ops Portal collector.
  • Low-cardinality Docker state exported through /var/lib/nutsnews/alloy/textfile/nutsnews.prom.

Docker log shipping is controlled separately by vps_service_foundation_grafana_alloy_collect_docker_logs, which is enabled by default. It grants the non-root alloy user membership in the docker group so Alloy can read /var/run/docker.sock and discover only containers labeled with the nutsnews-service-foundation or nutsnews-app Compose project. That is the accepted log-collection privilege boundary today.

Do not make /run/containerd/containerd.sock world-readable, chmod host sockets, or run Alloy as root to silence cAdvisor. If container-level CPU/memory metrics become necessary later, add them through an infra PR that documents the exact socket, mounts, supplementary groups, and rollback path. The accepted metrics boundary today is no cAdvisor/containerd access from Alloy.

The custom NutsNews textfile metrics cover state that already exists locally:

  • Ops Portal status feed availability and age.
  • Alert counts by severity.
  • Backup enabled/configured state, latest snapshot age, stale threshold, last backup/prune/verify result, missing paths, and missing configuration.
  • Email reporting enabled/configured state, pending/suppressed alert counts, recipient count, and last report timestamps.
  • App enablement, route enablement, container running/healthy state, and route readiness.
  • Selected systemd service active/enabled state.
  • Docker container running/health/restart count with low-cardinality labels.
  • Snapshot resource percentages and recent failed-login counters.

Log collection is intentionally selective:

SourceTreatment
journald priorities 0-4Collected with rate limiting
auth/security logsCollected with secret and IP redaction
Caddy logsJSON access/error logs collected from Docker stdout
app/service logsCollected from managed NutsNews log directories
backup/reporting logsCollected for operations visibility
Ops Portal logsCollected for collector/reporting diagnosis
Docker logsCollected for the NutsNews Compose projects through the Docker API socket

Intentionally excluded:

  • debug and trace noise
  • very large log lines
  • old compressed rotations
  • raw IP addresses
  • request IDs, user IDs, container IDs, image IDs, and full dynamic paths as labels
  • secrets, authorization headers, tokens, passwords, API keys, and credentials

This is a practical observability feed, not a copy of every byte the server has ever muttered.

Alert notifications preserve prompt first delivery and recovery messages while limiting reminders for an unchanged condition:

SeverityInitial group waitUpdate groupingUnchanged reminder
critical30 seconds5 minutes4 hours
major2 minutes10 minutes12 hours
warning, minor, or low5 minutes15 minutes24 hours
unmatched5 minutes15 minutes24 hours

The scheduled health audit has two non-overlapping signals. NutsNews scheduled health audit non-success means the report ran and found a critical condition or delivery problem. NutsNews scheduled health audit run overdue means no report execution was observed within 30 hours or reporting telemetry is unavailable. A completed report with a critical backup result must not also be described as a missed schedule.

The local VPS email reporter uses the same 24-hour unchanged-alert cooldown. Swap consumption below the 25% warning threshold remains visible in the Ops Portal but does not send email merely because more than 64 MiB is in use. Optimized-image cache age also remains visible without email while the cache stays within its 10 GB capacity bound. Swap threshold breaches, kernel OOM evidence, and cache capacity breaches still alert.

The one-minute RabbitMQ canary remains active and keeps its dedicated Prometheus health metrics and alerts. Its routine service and timer journal entries are excluded from backend Loki ingestion because those entries duplicated the metric signal and accounted for roughly 1,300 log lines per hour. RabbitMQ broker logs remain collected. The backend log-volume alert keeps its existing 10,000-lines-per-hour threshold so a new volume increase remains visible instead of being hidden by a raised threshold.

Noise tuning never suppresses failed, unverified, or stale backup alerts. On 2026-09-05 the VPS Restic repository could still be read, but Microsoft Graph rejected even a tiny diagnostic write with HTTP 507 quotaLimitReached while the same drive reported a normal quota state and roughly 347 GiB free. The latest VPS backup snapshot remained from 2026-08-08. Treat this as a real backup-provider write incident: restore Microsoft account write capability or migrate to a reviewed encrypted off-server destination, then run backup and verification before closing the alerts.

2026-09-05 break-glass reconciliation record

Section titled “2026-09-05 break-glass reconciliation record”

During the alert-noise investigation, the VPS collector, reporter, and reporter cooldown were synchronized over SSH to the reviewed source change so false-positive mail would stop immediately. The prior files were retained as /usr/local/bin/nutsnews-ops-portal-collector.pre-noise-tuning-20260905, /usr/local/bin/nutsnews-ops-portal-reporter.pre-noise-tuning-20260905, and /etc/nutsnews/ops-reporter.env.pre-noise-tuning-20260905. The collector and alert-check services both completed successfully afterward; the live alert list contained only the genuine backup failure, verification failure, and stale snapshot. Infra pull request ramideltoro/nutsnews-infra#567 is the source-of-truth reconciliation and must be followed by the protected Grafana and Ansible applies.

OpenTofu manages these Grafana folders and resource addresses:

ScopeHostFolder UIDOpenTofu addressOwner
VPS observabilityvps.nutsnews.comnutsnews-observabilitygrafana_folder.observabilityramideltoro/nutsnews-infra
Backend observabilitybackend.nutsnews.comnutsnews-backend-opsgrafana_folder.backend_observabilityramideltoro/nutsnews-infra

The NutsNews Observability VPS folder contains:

  • NutsNews VPS Overview
  • NutsNews Logs Overview
  • NutsNews CPU Load Processes
  • NutsNews Memory Swap
  • NutsNews Disk Filesystem IO
  • NutsNews Network Caddy Edge
  • NutsNews Docker Compose Containers
  • NutsNews Systemd Services Timers
  • NutsNews Logs Security Auth
  • NutsNews Backups Restore Verification
  • NutsNews Ops Portal Reporting
  • NutsNews Application Service Health
  • NutsNews Synthetic Uptime API Checks
  • NutsNews Grafana Cloud Usage Quota

The imported NutsNews Backend Ops folder contains:

  • NutsNews Backend Host Overview
  • NutsNews Backend Docker and Runtime
  • NutsNews Backend Caddy and Edge
  • NutsNews Backend Service Health
  • NutsNews Backend Backups
  • NutsNews Backend PostgreSQL Failover
  • NutsNews Backend OS Updates
  • NutsNews Backend Metrics Quota
  • NutsNews Backend Alert and Synthetic Health
  • NutsNews Backend Logs

Backend dashboards use grafana_dashboard.backend_observability["<dashboard_uid>"], and backend alert rules are owned as a single Grafana rule group at grafana_rule_group.backend_guardrails. The import IDs are the existing backend UIDs, not new names, so OpenTofu can adopt live resources without duplicate UIDs. If a protected apply proves a catalog dashboard UID is missing remotely, the infra catalog may set importExisting to false with the apply-run evidence so OpenTofu creates that missing dashboard from source. This is currently used for nutsnews-backend-postgres-failover after Grafana Cloud Apply run 29984664724.

OpenTofu also manages quota alert rules at roughly 70%, 85%, and 95% for live Grafana Cloud usage/limit ratios, including metrics active series, log active streams, log ingestion rate, and trace ingestion rate. A separate log-pipeline rule group alerts on Alloy Loki dropped entries, Alloy Loki write retries, and high error log volume. Loki-backed alert queries declare the range query type explicitly so repeated plans stay convergent after apply. Contact points are not created in code because they often contain secrets. Instead, alert labels can route into existing Grafana notification policies.

Do not remove existing backend Grafana resources until import and query/alert verification pass. The protected apply workflow uploads a grafana-cloud-post-apply-verification report after checking folders, dashboards, backend alert rules, Prometheus query data, and backend host/source Loki query data. Grafana Cloud Apply run 29985024771 showed the source-managed backend journal selector had live Loki data while the older namespace-only VPS sample did not, so infra PR #385 aligned the required post-apply Loki gate with {host="backend.nutsnews.com"} and {host="backend.nutsnews.com",source="journal"}.

Synthetic Monitoring is optional and configured through protected variables, not committed target URLs.

Synthetic checks use a separate Grafana Synthetic Monitoring API token. The Grafana service account token manages folders, dashboards, and alert rules, but the Terraform provider needs GRAFANA_SM_ACCESS_TOKEN for grafana_synthetic_monitoring_check resources. In GitHub this is stored as NUTSNEWS_GRAFANA_SYNTHETIC_MONITORING_ACCESS_TOKEN.

The provider also needs the stack-region Synthetic Monitoring API endpoint as GRAFANA_SM_URL, stored in protected secret NUTSNEWS_GRAFANA_SYNTHETIC_MONITORING_URL. Copy it from Testing & synthetics > Synthetics > Config > General. The protected validator requires a bounded HTTPS grafana.net endpoint and maps it explicitly in plan, drift, and apply; a valid token sent to another region is rejected.

Set NUTSNEWS_GRAFANA_SYNTHETIC_HTTP_CHECKS_JSON to {} to temporarily disable Synthetic Monitoring resources while still applying dashboards and quota alerts.

The protected plan and apply workflows run the source-controlled input validator before OpenTofu. It enforces JSON shape, positive unique probe IDs, HTTPS targets, 10-second through 60-minute intervals, bounded timeouts, token and regional-endpoint presence, and the 90% execution-budget ceiling. Its report is deliberately value-free: it includes only counts, interval bounds, projected executions, endpoint-configured status, and the configured ceiling. It never emits target URLs, regional endpoints, check names, probe IDs, or credentials.

Recommended first checks:

Check typeWhat to verify
public homepagepublic reader surface answers successfully
public health routeVPS infrastructure health answers successfully
public read-only API routesafe API read returns expected status
Ops Portal availabilityauth-safe availability signal only, not private data
admin-safe status routeonly if the route is read-only and safe to hit repeatedly

Do not check refresh-triggering, ingestion-triggering, admin mutation, OAuth callback, controller, or Worker routes. Monitoring should observe production, not poke it into doing work.

Grafana’s monthly Synthetic Monitoring formula is:

probes x tests x rounded-duration-minutes x (43200 / frequency-minutes)

Example safe shape:

1 probe x 4 checks x 1 minute x (43200 / 30 minutes) = 5,760 executions/month

That is comfortably under the current 100,000 API execution free assumption. Adding more probes, more checks, browser checks, or faster intervals changes the math. The protected validator fails closed when the resulting projection exceeds the source-controlled ceiling; changing that ceiling requires a reviewed infra change rather than an ad hoc workflow override.

Grafana Cloud k6 is not enabled by default.

If a future smoke or performance test is added, use manual or low-frequency scheduling and calculate virtual user hours first:

(maximum VUs x test duration minutes) / 60 = VUh

Grafana states that the free tier and trial are limited to 500 VUh per month. Keep any initial test far below that and stop for approval before enabling a cloud run that could consume paid quota.

  1. Add Grafana Cloud OpenTofu state backend config to the protected production-vps Environment.
  2. Add the Grafana service account token and datasource UIDs to the same protected Environment.
  3. Run Grafana Cloud Plan and review the normal plan, refresh-only drift check, and grafana-cloud-read-only-evidence artifact. The artifact must contain the value-free input report plus current dashboard, alert, Prometheus, and Loki proof.
  4. Merge the infra PR after checks pass.
  5. Run Grafana Cloud Apply from main with confirm_apply=grafana-cloud.
  6. Review the grafana-cloud-post-apply-verification artifact.
  7. Add Alloy telemetry write secrets to production-vps.
  8. Keep backend telemetry write secrets in ramideltoro/nutsnews-backend; do not store backend Grafana service-account credentials there after the handoff.
  9. Confirm ramideltoro/nutsnews-backend/docs/backend-grafana-handoff.json maps every retained backend folder, dashboard, alert UID, and datasource dependency to the infra OpenTofu owner.
  10. Retire backend direct provisioning only after backend import and query/alert verification pass.
  11. Run Protected Ansible Apply in check mode with enable_grafana_alloy=true.
  12. Review the package, config, systemd, and Alloy validation diff.
  13. Run apply mode with confirm_apply=vps.nutsnews.com and enable_grafana_alloy=true.
  14. Verify metrics, logs, dashboards, alerts, and usage/quota panels in Grafana Cloud.

After apply, also verify the host-side Alloy state:

Terminal window
systemctl show alloy.service --property=ActiveState,SubState,User,SupplementaryGroups,DropInPaths --no-pager
curl -fsS http://127.0.0.1:12345/-/ready
sudo journalctl -u alloy.service --since "-30 min" --no-pager | grep -c "containerd.sock: connect: permission denied"
sudo find /var/lib/nutsnews/alloy/textfile -maxdepth 1 -type f -name '*.prom' -printf '%s %p\n'

The journalctl count must be 0 after the 30-minute post-apply window has aged out pre-fix lines.

enable_grafana_alloy=false means Alloy must be off. Protected apply now stops and masks the service, disables the textfile timer, and removes the managed credentials/config files instead of leaving an old agent running.

Use Protected Ansible Apply with run_mode=check first. The disabled-state diff should show removal of the managed Alloy env file, Alloy config, systemd drop-in, and textfile service/timer units, plus alloy.service moving to stopped/disabled/masked when the unit exists. Then rerun apply with confirm_apply=vps.nutsnews.com.

The package and Grafana apt repository can remain installed. That keeps rollback simple while still removing the managed credential and config artifacts that would let the service keep sending telemetry.

Disabled convergence is deliberately separate from the enabled installation block. This prevents a false disabled state where the Ops Portal says Alloy is disabled but an older alloy.service process, drop-in, and root-only env file are still present. Re-enabling is the rollback path: set enable_grafana_alloy=true, rerun check/apply, and Ansible un-masks alloy.service, recreates the managed env/config from protected Environment secrets, starts the textfile timer, and repeats readiness and journal validation.

flowchart TD
A[Protected Ansible Apply] --> B{enable_grafana_alloy}
B -->|true| C[Render env/config/drop-in]
C --> D[Unmask and start alloy.service]
D --> E[Validate readiness and journals]
B -->|false| F[Stop textfile timer]
F --> G[Stop, disable, and mask alloy.service]
G --> H[Remove managed env/config/drop-in/unit files]
H --> I[Ops Portal reports disabled runtime]

Use Loki Explore after apply:

{service_namespace="nutsnews", source="journal"}
{service_namespace="nutsnews", source="auth"}
{service_namespace="nutsnews", source="docker", compose_project=~"nutsnews-service-foundation|nutsnews-app"}
{service_namespace="nutsnews", source="docker", container="nutsnews-caddy"} | json
{service_namespace="nutsnews"} |~ "(?i)(error|critical|panic|failed|denied)"

Infra-owned Grafana management secrets live in ramideltoro/nutsnews-infra under Settings -> Environments -> production-vps.

Telemetry write secrets:

SecretPurpose
NUTSNEWS_GRAFANA_CLOUD_METRICS_URLGrafana Cloud metrics remote write endpoint
NUTSNEWS_GRAFANA_CLOUD_METRICS_USERNAMEGrafana Cloud metrics username
NUTSNEWS_GRAFANA_CLOUD_LOGS_URLGrafana Cloud logs push endpoint
NUTSNEWS_GRAFANA_CLOUD_LOGS_USERNAMEGrafana Cloud logs username
NUTSNEWS_GRAFANA_CLOUD_ACCESS_POLICY_TOKENAccess Policy token for telemetry writes

These VPS telemetry write values are infra-scoped because nutsnews-infra manages the VPS Alloy deployment. Backend telemetry write values remain in ramideltoro/nutsnews-backend under production-backend with the backend names GRAFANA_CLOUD_PROMETHEUS_URL, GRAFANA_CLOUD_PROMETHEUS_USERNAME, GRAFANA_CLOUD_PROMETHEUS_PASSWORD, GRAFANA_CLOUD_LOKI_URL, GRAFANA_CLOUD_LOKI_USERNAME, and GRAFANA_CLOUD_LOKI_PASSWORD.

OpenTofu automation secrets:

SecretPurpose
NUTSNEWS_GRAFANA_CLOUD_TOFU_BACKEND_CONFIGRemote state backend config
NUTSNEWS_GRAFANA_CLOUD_URLGrafana Cloud stack URL
NUTSNEWS_GRAFANA_CLOUD_SERVICE_ACCOUNT_TOKENGrafana service account token for IaC
NUTSNEWS_GRAFANA_CLOUD_PROMETHEUS_DATASOURCE_UIDMetrics datasource UID
NUTSNEWS_GRAFANA_CLOUD_LOKI_DATASOURCE_UIDLogs datasource UID
NUTSNEWS_GRAFANA_CLOUD_USAGE_DATASOURCE_UIDUsage datasource UID

Optional Synthetic Monitoring secrets:

SecretPurpose
NUTSNEWS_GRAFANA_SYNTHETIC_MONITORING_ACCESS_TOKENSynthetic Monitoring API token; required when probe IDs and enabled HTTP checks are configured
NUTSNEWS_GRAFANA_SYNTHETIC_MONITORING_URLStack-region Synthetic Monitoring API endpoint; required with the access token and mapped as GRAFANA_SM_URL
NUTSNEWS_GRAFANA_SYNTHETIC_PROBE_IDS_JSONJSON array of probe IDs
NUTSNEWS_GRAFANA_SYNTHETIC_HTTP_CHECKS_JSONJSON object of safe HTTP checks

Do not paste these values into chat, issues, PR bodies, docs, or committed files.

Metrics:

up{service_namespace="nutsnews"}
node_load1{service_namespace="nutsnews"}
node_memory_MemAvailable_bytes{service_namespace="nutsnews"}
node_filesystem_avail_bytes{service_namespace="nutsnews"}
nutsnews_ops_portal_status_available{service_namespace="nutsnews"}
nutsnews_backup_last_success{service_namespace="nutsnews"}
nutsnews_app_container_healthy{service_namespace="nutsnews"}

Logs:

{service_namespace="nutsnews"}
{service_namespace="nutsnews", log_source="auth"}
{service_namespace="nutsnews", log_source="journal"}

Docker log streams are expected only if vps_service_foundation_grafana_alloy_collect_docker is deliberately enabled in a later reviewed change.

Synthetics when configured:

probe_success{service_namespace="nutsnews"}
probe_duration_seconds{service_namespace="nutsnews"}

Quota:

grafanacloud_instance_metrics_limits
grafanacloud_logs_instance_limits

This infra change can observe container health, deployment state, service logs, Caddy routing, and the existing Ops Portal status feed. Deeper application telemetry belongs in the app or Worker repos.

Follow-up prompt for ramideltoro/nutsnews:

Add low-cardinality application metrics and structured health telemetry for the deployed NutsNews web service. Keep labels bounded, avoid request/user IDs, and document Grafana queries in nutsnews-docs.

Follow-up prompt for ramideltoro/nutsnews-worker:

Implement worker-uplift metrics and structured logs against NUTSNEWS_WORKER_UPLIFT_TELEMETRY_SCOPE.md. Keep traces and exemplars disabled until a later reviewed infra PR approves sampling and credentials.

This layer does not:

  • create paid Grafana Cloud features
  • enable browser Synthetic Monitoring
  • enable Grafana Cloud k6 runs
  • add application-code instrumentation
  • expose the Ops Portal publicly
  • add portal mutation controls
  • add arbitrary SSH or workflow command execution
  • store Terraform state in Git
  • commit Grafana Cloud secrets, URLs, usernames, tenant IDs, targets, or tokens