Skip to content

feat(monitoring): add optional SMTP configuration for Grafana - #2751

Closed
Sergei Makarov (SerjioTT) wants to merge 1 commit into
cozystack:mainfrom
SerjioTT:feat/monitoring-grafana-smtp
Closed

feat(monitoring): add optional SMTP configuration for Grafana#2751
Sergei Makarov (SerjioTT) wants to merge 1 commit into
cozystack:mainfrom
SerjioTT:feat/monitoring-grafana-smtp

Conversation

@SerjioTT

@SerjioTT Sergei Makarov (SerjioTT) commented May 27, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Add optional SMTP configuration for Grafana to enable email notifications
through Grafana Unified Alerting.

SMTP parameters are injected as GF_SMTP_* environment variables into the
Grafana container. Credentials are stored in a Kubernetes Secret and injected
via secretKeyRef.

Why

CozyStack's monitoring stack routes alerts through VMRule → vmalert → VMAlertmanager → Alerta → Telegram/Slack. This works well for infrastructure
alerts defined as YAML manifests.

However, Grafana Unified Alerting (default since Grafana v9) lets users
create custom alert rules directly in the Grafana UI — with visual threshold
editors, arbitrary PromQL queries, and per-dashboard alerting. These alerts
use Grafana's internal Alertmanager, which is separate from
VMAlertmanager/Alerta, and cannot send email without SMTP configured in Grafana.

This is not duplication of existing functionality. This PR allows end users to enable email alerts.
Email is the most universal notification channel in enterprise environments,
and many organizations require it.

Implementation: env vars vs spec.config.smtp

This PR uses environment variables (GF_SMTP_*) rather than adding a
smtp section to spec.config of the Grafana CR.

Reason: when SMTP credentials are placed in spec.config.smtp with ${VAR}
placeholders (the pattern used by GF_DATABASE_PASSWORD), grafana-operator
writes those placeholders into grafana.ini literally. Grafana does not
perform environment variable expansion in the [smtp] section of grafana.ini,
so it attempts authentication with the literal string ${GF_SMTP_PASSWORD}
and fails with 535 incorrect password.

Environment variables of the form GF_<SECTION>_<KEY> are read directly by
Grafana at startup and override grafana.ini values. This works reliably
with grafana-operator. Verified empirically on a live CozyStack cluster.

Security

SMTP credentials follow the existing chart pattern:

env:
- name: GF_SMTP_USER
  valueFrom:
    secretKeyRef: { name: grafana-smtp, key: user }
- name: GF_SMTP_PASSWORD
  valueFrom:
    secretKeyRef: { name: grafana-smtp, key: password }

The chart creates grafana-smtp Secret from values. Credentials never appear
in the Grafana CR spec in plain text.

Screenshots

Testing

Tested on a live CozyStack (v1.3.5) cluster (Grafana 11.4.0) by patching the Grafana CR
with GF_SMTP_* env vars including secretKeyRef for credentials, then verifying:

  • Env vars present in container (kubectl exec ... env | grep GF_SMTP)
  • SMTP visible to Grafana via API (smtp.enabled=true, password masked)
  • Test notification via Grafana UI Contact Point → email delivered
  • Existing Alerta/Telegram/Slack alerts unaffected
  • Credentials stored in Secret, not visible in Grafana CR
  • Alternative approach via spec.config.smtp with ${VAR} placeholders
    confirmed NOT working — fails with 535

Backward compatibility

Fully backward-compatible. grafana.smtp.enabled defaults to false,
so existing deployments are unaffected. The Secret and env vars are only
rendered when explicitly enabled.

Release note

feat(monitoring): Add optional SMTP configuration for Grafana to enable email notifications through Grafana Unified Alerting. Disabled by default, configure via `grafana.smtp` in monitoring values.

Summary by CodeRabbit

  • New Features

    • Added SMTP email notification settings for Grafana, enabling email-based alerts with host, credentials, sender identity, TLS policy, and certificate verification options.
  • Documentation

    • Updated configuration docs and examples to include the new Grafana SMTP parameters and defaults.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3dc466bb-5984-4faf-8240-ffcaf8279610

📥 Commits

Reviewing files that changed from the base of the PR and between 8b84a3c and b4860d9.

📒 Files selected for processing (5)
  • packages/extra/monitoring/README.md
  • packages/extra/monitoring/values.schema.json
  • packages/extra/monitoring/values.yaml
  • packages/system/monitoring-rd/cozyrds/monitoring.yaml
  • packages/system/monitoring/templates/grafana/grafana.yaml
✅ Files skipped from review due to trivial changes (1)
  • packages/extra/monitoring/README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/extra/monitoring/values.schema.json
  • packages/system/monitoring-rd/cozyrds/monitoring.yaml
  • packages/system/monitoring/templates/grafana/grafana.yaml
  • packages/extra/monitoring/values.yaml

📝 Walkthrough

Walkthrough

Adds Grafana SMTP configuration: JSON schema and type additions, default values and README docs, Monitoring ApplicationDefinition schema updates, and Helm template wiring that conditionally creates an SMTP Secret and injects GF_SMTP_* env vars into the Grafana container.

Changes

SMTP Configuration Support

Layer / File(s) Summary
SMTP Schema and Type Contracts
packages/extra/monitoring/values.schema.json, packages/extra/monitoring/values.yaml
SmtpConfig type and JSON schema define SMTP enablement, server/credentials, sender identity, and TLS/verification controls.
Default Values and Documentation
packages/extra/monitoring/values.yaml, packages/extra/monitoring/README.md
SMTP defaults (enabled, host, user, password, from address/name, StartTLS policy, skip_verify) are added and documented in the parameters reference table.
Application Definition Schema Extension
packages/system/monitoring-rd/cozyrds/monitoring.yaml
Monitoring ApplicationDefinition's openAPISchema and keysOrder are extended to declare spec.grafana.smtp and its nested fields.
Grafana Helm Template Implementation
packages/system/monitoring/templates/grafana/grafana.yaml
Conditional Kubernetes grafana-smtp Secret stores SMTP credentials; GF_SMTP_* environment variables are injected into the Grafana container from values and the secret.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A rabbit nudges config lines with care,
Secrets tucked where emails dare,
Grafana learns to send and sing,
StartTLS waltzes, certificates cling,
Hoppy alerts — inboxes everywhere! 🐇📬

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'feat(monitoring): add optional SMTP configuration for Grafana' accurately and specifically describes the main change: adding SMTP configuration capability to Grafana, with clear indication it is optional.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added area/monitoring Issues or PRs related to the monitoring stack (vlogs, vmstack, grafana, workloadmonitor) kind/feature Categorizes issue or PR as related to a new feature size/L This PR changes 100-499 lines, ignoring generated files labels May 27, 2026
@SerjioTT

Copy link
Copy Markdown
Contributor Author

The E2E test was failure and it's not related to this PR.

The Configure Tenant and wait for applications step times out waiting for
the etcd StatefulSet (3 replicas) in tenant-root.
This failed identically on all 3 retry attempts, but on a component this PR
doesn't touch. This PR only modifies the monitoring package and the Grafana
CR — and the monitoring HelmRelease reaches Ready in every attempt:

helmrelease.helm.toolkit.fluxcd.io/monitoring condition met

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces support for SMTP configuration in the Grafana monitoring stack. By allowing users to define SMTP settings, it enables email notifications directly through Grafana Unified Alerting. The implementation uses environment variables to bypass limitations in Grafana's configuration file parsing, while maintaining security by utilizing Kubernetes Secrets for sensitive credentials.

Highlights

  • SMTP Configuration: Added optional SMTP configuration for Grafana to enable email notifications via Grafana Unified Alerting.
  • Environment Variable Injection: Implemented injection of SMTP parameters as GF_SMTP_* environment variables, ensuring compatibility with Grafana's startup configuration.
  • Secret Management: Introduced a new Kubernetes Secret grafana-smtp to securely manage SMTP credentials, avoiding plain-text exposure in the Grafana CR.
New Features

🧠 You can now enable Memory (public preview) to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment Gemini (@gemini-code-assist) Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on Gemini (@gemini-code-assist) comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces SMTP configuration support for Grafana email notifications across the monitoring packages, adding schema definitions, default values, documentation, and Kubernetes manifests (Secret and environment variables). The review feedback highlights that Grafana's SMTP configuration key is start_tls_policy (with underscores) rather than startTLS_policy, and recommends renaming this parameter across the schema, values, and documentation. Additionally, the feedback suggests conditionally creating the SMTP Secret and injecting user/password environment variables only when a username is provided, which enables support for passwordless SMTP relays. Finally, it notes that the generated monitoring.yaml file should be regenerated after these schema updates.

Comment thread packages/system/monitoring/templates/grafana/grafana.yaml
Comment thread packages/system/monitoring/templates/grafana/grafana.yaml Outdated
Comment thread packages/extra/monitoring/values.yaml
Comment thread packages/extra/monitoring/values.yaml
Comment thread packages/extra/monitoring/values.schema.json Outdated
Comment thread packages/extra/monitoring/README.md Outdated
Comment thread packages/system/monitoring-rd/cozyrds/monitoring.yaml Outdated
@SerjioTT
Sergei Makarov (SerjioTT) marked this pull request as draft May 29, 2026 08:33
Add GF_SMTP_* environment variables injected into the Grafana container
to enable email notifications through Grafana Unified Alerting.
SMTP credentials are stored in a Kubernetes Secret (grafana-smtp) and
the feature is disabled by default via grafana.smtp.enabled=false.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: SerjioTT <serjiott@gmail.com>
@SerjioTT
Sergei Makarov (SerjioTT) force-pushed the feat/monitoring-grafana-smtp branch from 8b84a3c to b4860d9 Compare May 29, 2026 11:24
@SerjioTT
Sergei Makarov (SerjioTT) marked this pull request as ready for review May 29, 2026 11:46

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces SMTP configuration support for Grafana within the monitoring package. It updates the documentation, values schema, default values, and cozyrds monitoring schema to include SMTP-related parameters such as host, user, password, and TLS policies. Additionally, it updates the Grafana deployment template to conditionally create a Kubernetes Secret for SMTP credentials and injects the SMTP configuration as environment variables into the Grafana container. There are no review comments, so I have no feedback to provide.

@github-actions

Copy link
Copy Markdown

This PR has had no activity for 60 days and was marked lifecycle/stale.
It will be closed in 14 days unless commented or labelled lifecycle/frozen.

@github-actions github-actions Bot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale label Jul 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NOT LGTM. The feature is wanted and the plumbing is mostly right, but the password ends up somewhere a tenant can read it.

The PR body says credentials never appear in the Grafana CR spec in plain text, and that is true of the Grafana CR. The exposure moved up a level rather than away. packages/extra/monitoring/templates/helmrelease.yaml:60 is values: {{- .Values | toYaml | nindent 4 }}, which copies the entire values tree into the nested HelmRelease verbatim, so helm template monitoring packages/extra/monitoring --set grafana.smtp.password=SUPERSECRET renders spec.values.grafana.smtp.password: SUPERSECRET. The password therefore lands in the Monitoring CR, in the monitoring-system HelmRelease and in Flux's release-storage Secret, all in the tenant namespace and all readable by the tenant.

The shape this repo uses for secret material is secretRef.name pointing at an existing Secret in the release namespace, mutually exclusive with an inline value and guarded by a hard fail. It lives in this very chart at packages/system/monitoring/templates/_helpers.tpl:115-122, with the tenant-facing contract in packages/extra/monitoring/values.yaml. To be clear about the timing: that convention landed on 15 July, and this PR was opened on 27 May, so it is not something you ignored. It is simply what the chart now expects, and adopting it is what unblocks this.

Two more that need fixing.

The grafana.smtp block was only added to packages/extra/monitoring/values.yaml, never to packages/system/monitoring/values.yaml, so rendering the system chart on its own emits a bare value: for GF_SMTP_FROM_ADDRESS and GF_SMTP_HOST. That is YAML null rather than an empty string, because quote passes a nil through untouched instead of quoting it into "". Worth knowing generally: {{ .Values.missing | quote }} renders nothing at all, while {{ .Values.missing | default "" | quote }} renders "".

There is no validation on the block. enabled: true with an empty host renders GF_SMTP_ENABLED=true next to GF_SMTP_HOST="", and every mail send then fails silently, which is the failure mode operators find hardest to diagnose. The chart's habit elsewhere is a hard fail at render time, as in templates/vlogs/vlogs.yaml:9 and the helper above.

The branch also conflicts with main now, in packages/system/monitoring/templates/grafana/grafana.yaml and packages/system/monitoring-rd/cozyrds/monitoring.yaml.

Things I checked that are fine, so you do not spend time on them. The generated artifacts are clean: regenerating the schema, the README table and the RD on your head produces zero drift, which is unusual and appreciated. Nothing here derives a name from .Release.Name, so the release-naming confusion tracked in #3338 does not touch this diff. And the one red check is not yours: the E2E run died on the kubevirt-operator HelmRelease hanging in an upgrade for 49 minutes, cascading into the Cluster-API provider wait, with no monitoring or SMTP line anywhere in the log.

One judgement call I am recording rather than blocking on: a tenant who can edit the Monitoring CR can point Grafana at any SMTP host and set skip_verify. That is consistent with what the same CR already allows through the Slack, Telegram and remote-write settings, so I do not think it needs gating.

Given how much of the chart around this was rewritten since May, I would treat this as a redo on current main using secretRef rather than a rebase and patch. If you have moved on from it, say so and I will close it with a pointer to the helper, so whoever picks it up next starts from the right shape.

@github-actions

Copy link
Copy Markdown

Closed because no activity followed the lifecycle/stale warning.
Reopen if the change is still wanted.

@github-actions github-actions Bot added the lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed label Aug 13, 2026
@github-actions github-actions Bot closed this Aug 13, 2026
@SerjioTT

Copy link
Copy Markdown
Contributor Author

Closing this intentionally rather than letting it lapse — #3800 covers the need, at a better layer.

Our actual requirement was email delivery for alerts. This PR approached it through Grafana's own SMTP (GF_SMTP_*), but Grafana is not the alerting engine here: the path is VMRule → vmalert → VMAlertmanager → Alerta, and Grafana is the dashboard layer. #3800 adds the email receiver where the routing actually happens, keeps Alerta delivery independent of the SMTP path, and mounts the SMTP password from an existing Secret via auth_password_file instead of carrying it through values as this PR did. That is the right shape for the platform, and it makes this change redundant.

For the record, what #3800 deliberately does not cover is Grafana-native SMTP — UI-configured contact points, user invites, password reset mail, but we do not need those today.

Thanks for the reviews!

@SerjioTT
Sergei Makarov (SerjioTT) deleted the feat/monitoring-grafana-smtp branch August 18, 2026 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/monitoring Issues or PRs related to the monitoring stack (vlogs, vmstack, grafana, workloadmonitor) kind/feature Categorizes issue or PR as related to a new feature lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale size/L This PR changes 100-499 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants