Back to Article List

Where to set up Grafana SMTP settings

Where to set up Grafana SMTP settings - Where to set up Grafana SMTP settings

Grafana ships with email switched off. The [smtp] section is already in the config with a default for every key, and enabled is false, so the first time you build an email contact point and press test you get an error. The fix is about five lines. Most of the work is finding which file on your particular install those five lines belong in, because the answer changes with how Grafana got onto the machine.

Where the [smtp] section lives on each install type

Grafana reads exactly one custom config file, and which one it reads is decided by the --config flag passed by whatever started the process. On a package install that flag comes from the systemd unit. In a container it comes from the entrypoint. Nothing scans a directory looking for INI files, so a correct [smtp] block in a file the process never opens does nothing at all.

Install methodFile you editNotes
deb or rpm from apt.grafana.com/etc/grafana/grafana.iniThe unit passes --config=/etc/grafana/grafana.ini. There is no separate custom.ini here.
Binary tarball or zip<install dir>/conf/custom.iniYou create it yourself. It starts empty and only needs the sections you change.
macOS Homebrew/opt/homebrew/etc/grafana/grafana.ini or /usr/local/etc/grafana/grafana.iniApple silicon uses the first path, Intel the second.
Windowsconf\custom.iniCopy conf\sample.ini to conf\custom.ini and edit the copy.
Docker/etc/grafana/grafana.ini inside the containerNormally driven by GF_SMTP_* environment variables instead.
Kubernetes via Helmthe grafana.ini key in values.yamlThe chart renders it into a ConfigMap. Editing the file in the pod is pointless.

One file gets special treatment. conf/defaults.ini is the reference copy of every setting Grafana understands, and the docs say of it, in italics, "Don't change this file." It is replaced on upgrade, so anything you put there survives until the next patch release and then quietly vanishes. Read it and copy out of it. Don't edit it.

If the server itself doesn't exist yet, the one-click Grafana VPS template gets you to the login screen with the package install already done, and you edit the same root-owned /etc/grafana/grafana.ini afterwards. The guide on how to install Grafana on an Ubuntu VPS has the APT repo steps if you'd rather run them by hand. Either route ends at the same path.

The full [smtp] block and what every key does

This is the section verbatim from conf/defaults.ini, which is the state your instance is in right now if you have never touched it.

[smtp]
enabled = false
host = localhost:25
user =
password =
cert_file =
key_file =
skip_verify = false
from_address = [email protected]
from_name = Grafana
ehlo_identity =
startTLS_policy =
enable_tracing = false

Twelve keys, and you'll set five or six of them. The Grafana reference for configuring Grafana lists them alongside every other section. What each one does:

KeyWhat it doesDefault
enabledThe master switch. While this is false every attempt to send returns "SMTP not configured, check your grafana.ini config file's [smtp] section" and no connection is ever opened.false
hostRelay address as host:port. The port is not optional.localhost:25
user / passwordSMTP AUTH credentials. Leave both empty for an unauthenticated internal relay.empty
cert_file / key_fileClient certificate and key, for a relay that wants mutual TLS. Rare outside enterprise mail.empty
skip_verifySkips verification of the relay's certificate. Useful for ten seconds while you diagnose, then set it back.false
from_addressThe From address on every mail Grafana sends. Most relays reject anything that isn't a verified sender.[email protected]
from_nameDisplay name next to that address.Grafana
ehlo_identityThe name sent in the EHLO greeting. Falls back to instance_name.empty
startTLS_policyHow hard Grafana insists on STARTTLS. Empty behaves as OpportunisticStartTLS.empty
enable_tracingAdds trace headers to outgoing mail for OpenTelemetry correlation.false

enable_tracing is the one I've never switched on. The description says it adds trace headers to outgoing mail for OpenTelemetry correlation, and I have not found anybody writing about what they then do with an alert email inside a trace. If you're already correlating notification delivery with spans, it presumably does exactly what it says.

A minimal working block is short:

[smtp]
enabled = true
host = smtp.example.com:587
user = [email protected]
password = your-password-here
from_address = [email protected]
from_name = Grafana
startTLS_policy = MandatoryStartTLS

Every line in the shipped grafana.ini is commented out

The /etc/grafana/grafana.ini the package drops on disk is a copy of conf/sample.ini, and sample.ini has every single line prefixed with a semicolon. In INI syntax a leading semicolon is a comment. So the file looks like a complete configuration, you scroll to the [smtp] block, change false to true, save, restart and nothing changes, because the line you edited reads ;enabled = true and Grafana never saw it.

sudo grep -n -A 14 '^\[smtp\]' /etc/grafana/grafana.ini

Any line in that output starting with ; is inert. Strip the semicolon from the keys you want and leave the rest alone. The section header itself, [smtp], has to be uncommented too.

Passwords containing # or ; need triple quotes

Generated relay passwords love hash and semicolon characters, and both start comments in this parser. The documented escape is triple quotes:

password = """#p4ss;word"""

Without them, password = #p4ss;word parses as an empty password followed by a comment, and you get an authentication failure that looks nothing like a quoting problem. Wrapping every SMTP password in triple quotes is a harmless habit, since the quotes cost nothing when the password is plain.

Ports, TLS and startTLS_policy

The host = localhost:25 default is a placeholder, and it goes nowhere on almost any server you'd deploy on. Two reasons. There's usually no MTA listening on localhost:25 in the first place, and even if you install Postfix, most hosting providers block outbound port 25 by default to protect the network's sending reputation. Port 25 is the path spambots reach for, so it stays shut unless you ask for it and explain why.

What you want instead is an authenticated relay on 587 or 465.

PortEncryptionUse it when
587STARTTLS: plaintext connection upgraded to TLS by the STARTTLS commandThe default choice. Every provider below supports it.
465Implicit TLS, encrypted from the first byteYour relay documents it, or a middlebox mangles STARTTLS. The Grafana docs note "Use port 465 for implicit TLS."
25STARTTLS if offeredAn internal relay on your own network that you control end to end.

Grafana does not have a separate "use SSL" toggle. It infers implicit TLS from port 465 and uses startTLS_policy for everything else. Three values are accepted:

OpportunisticStartTLS is the default when the key is empty. Grafana upgrades to TLS if the server advertises STARTTLS and sends in plaintext if it doesn't, which means a broken or stripped advertisement silently downgrades you. MandatoryStartTLS refuses to send unless the upgrade succeeds. NoStartTLS never upgrades, which is only sane for a relay on a trusted internal network or on 465 where TLS is already implicit.

I set MandatoryStartTLS on every 587 relay. Alert emails carry hostnames, metric names and thresholds, which is a tidy summary of your infrastructure for anyone sitting on the path. A bounced send is a problem you notice within the hour. A silently downgraded one arrives fine and leaks quietly, and the log line the mandatory policy produces is what separates the two.

SMTP relay settings by provider

Verify the sender identity at the provider before you touch Grafana. Most rejections on a fresh setup are the relay refusing a from_address it has never seen.

Gmail and Google Workspace with an app password

Google's own guidance for devices and applications lists smtp.gmail.com on port 25, 465 or 587, with SSL on 465 and TLS on 587, and it requires the full email address plus an app password rather than the account password. An app password is a 16-digit passcode you generate after enabling 2-Step Verification on the account. Accounts using Advanced Protection can't create one, and neither can users in an organisation where the admin has blocked them.

[smtp]
enabled = true
host = smtp.gmail.com:587
user = [email protected]
password = """abcdefghijklmnop"""
from_address = [email protected]
from_name = Grafana
startTLS_policy = MandatoryStartTLS

Paste the app password without the spaces Google displays it with. The sending limit through smtp.gmail.com is 2,000 messages per day, which is generous for alerting until a flapping rule turns into a mail loop. Workspace tenants have a second option, smtp-relay.gmail.com on 25, 465 or 587, which authenticates by source IP address instead of credentials and allows up to 10,000 recipients per user per day. Current limits and hostnames are on Google's page for sending mail from a printer, scanner or app. For a single Grafana instance the plain app password route is simpler, and the relay only pays off when you're already sending application mail through it.

Microsoft 365 and Exchange Online

The client submission endpoint is smtp.office365.com on port 587 with STARTTLS, TLS 1.2 or 1.3, authenticating as a licensed mailbox. Two things make this harder than it looks.

First, SMTP AUTH is often switched off before you start. If security defaults are enabled on the tenant, SMTP AUTH is already disabled, and many tenants also have it off at the organisation level. An Exchange Online admin turns it on per mailbox:

Set-CASMailbox -Identity [email protected] -SmtpClientAuthenticationDisabled $false

Second, and more important for planning: Microsoft is retiring basic authentication for client submission, and the timeline has already moved more than once. Grafana's mailer authenticates with a username and password. There is no OAuth field in the [smtp] section, so when basic auth for SMTP AUTH goes away on a tenant, a Grafana instance pointed at smtp.office365.com stops sending and no amount of config editing brings it back. Check the current milestones on Microsoft's page for setting up a device or application to send email using Microsoft 365 before you build on it, and treat the mailbox credential route as temporary. Microsoft points affected senders at High Volume Email and at Azure Communication Services, and if your Grafana box is going to outlive the current deadline, a dedicated transactional relay is less work than a migration under time pressure.

Amazon SES

SES endpoints follow the pattern email-smtp.<region>.amazonaws.com, so email-smtp.eu-west-1.amazonaws.com for Ireland. Ports 25, 587 and 2587 use STARTTLS. Ports 465 and 2465 use TLS wrapper, the implicit kind. The 2xxx ports exist because EC2 throttles 25 by default and some networks block the standard ones.

[smtp]
enabled = true
host = email-smtp.eu-west-1.amazonaws.com:587
user = AKIAIOSFODNN7EXAMPLE
password = """BJ2cW9ZnKm4EXAMPLEpasswordvalue"""
from_address = [email protected]
from_name = Grafana
startTLS_policy = MandatoryStartTLS

SES SMTP credentials are not your IAM access keys. The AWS docs put it plainly: "Your SMTP password is different from your AWS secret access key." You generate them in the SES console under SMTP settings, or derive them yourself with the conversion script in the SES SMTP credentials documentation. Most people click through the console.

The derivation is a nice piece of design if you ever go and look at it. The SMTP password is a chain of HMAC-SHA256 steps over your secret access key with the region and the service name mixed in along the way, which is why one IAM user yields a completely different SMTP password per region. Credentials made in us-east-1 will not authenticate against an eu-west-1 endpoint, and the error you get back says nothing whatsoever about regions. Anyway.

Two more things catch new accounts. The identity in from_address has to be verified, and a fresh SES account sits in the sandbox where you can only send to verified recipients.

SendGrid

The host is smtp.sendgrid.net on 587, with 25 and 2525 also open and 465 for SSL. The username is the literal string apikey, on every account. The API key itself goes in the password field and needs at least Mail Send permission.

[smtp]
enabled = true
host = smtp.sendgrid.net:587
user = apikey
password = """SG.xxxxxxxxxxxxxxxxxxxxxx"""
from_address = [email protected]
from_name = Grafana
startTLS_policy = MandatoryStartTLS

The SendGrid SMTP integration docs confirm that username string, which is the kind of thing you want to see in the vendor's own docs, because it reads like a placeholder. Postmark and Brevo take the same shape with their own hostnames, and both need a verified sender before the first message goes out.

Docker and Kubernetes with GF_SMTP_ environment variables

Any Grafana setting maps to an environment variable as GF_<SECTION>_<KEY>, uppercased, with dots and dashes turned into underscores. That makes the whole [smtp] section reachable without touching a config file at all, which is the point in a container.

Docker and Docker Compose

services:
  grafana:
    image: grafana/grafana:13.2.0
    ports:
      - "3000:3000"
    volumes:
      - grafana-storage:/var/lib/grafana
    environment:
      GF_SMTP_ENABLED: "true"
      GF_SMTP_HOST: "smtp.sendgrid.net:587"
      GF_SMTP_USER: "apikey"
      GF_SMTP_PASSWORD__FILE: "/run/secrets/grafana_smtp_password"
      GF_SMTP_FROM_ADDRESS: "[email protected]"
      GF_SMTP_FROM_NAME: "Grafana"
      GF_SMTP_STARTTLS_POLICY: "MandatoryStartTLS"
    secrets:
      - grafana_smtp_password

secrets:
  grafana_smtp_password:
    file: ./secrets/smtp_password.txt

volumes:
  grafana-storage:

The __FILE suffix (two underscores) works on any GF_* variable and reads the value out of the named file, which keeps a relay password out of docker inspect output and out of your compose file. The Grafana Docker Compose setup covers volume layout and image pinning for the rest of that file. Setting both GF_SMTP_PASSWORD and GF_SMTP_PASSWORD__FILE is an error, so pick one of them.

Note the trailing newline problem while you're here. If you create the secret file with echo "SG.xxx" > smtp_password.txt, the newline is part of the file. Use printf '%s' 'SG.xxx' > smtp_password.txt instead, or spend an hour convinced your API key is wrong.

Helm values.yaml

The chart moved to the grafana-community organisation, so helm repo add grafana-community https://grafana-community.github.io/helm-charts is the current source. The config goes under the grafana.ini key, which the chart renders into a ConfigMap:

grafana.ini:
  smtp:
    enabled: true
    host: smtp.sendgrid.net:587
    from_address: [email protected]
    from_name: Grafana
    startTLS_policy: MandatoryStartTLS

smtp:
  existingSecret: grafana-smtp
  userKey: user
  passwordKey: password

That top-level smtp block is separate from the grafana.ini one and points at a Kubernetes Secret holding the credentials, with userKey and passwordKey naming the two keys inside it. Leave user and password out of the grafana.ini map when you use it. The generic env and envValueFrom values are there too if you'd rather inject GF_SMTP_* directly from a secret reference.

Where to store the SMTP password

Grafana builds its configuration in layers, and each layer overrides the one before it. conf/defaults.ini first, then any cfg:default.* command line arguments, then your custom config file, then GF_* environment variables, and finally cfg:* arguments without the default. prefix. What that order gives you: environment variables beat grafana.ini, and both beat the cfg:default.* arguments the packaged systemd unit and the Docker entrypoint pass in. Mixing the two is fine as long as you know which one wins.

My preference is to put the non-secret keys in grafana.ini where they're visible to anyone reading the config, and the password in an environment file that only root can read. Config management tools copy grafana.ini around, and support tickets end up with it pasted in. A separate file with mode 0600 stays out of all of that.

sudo install -m 600 -o root -g root /dev/null /etc/grafana/smtp.env
echo 'GF_SMTP_PASSWORD=SG.xxxxxxxxxxxxxxxxxxxxxx' | sudo tee /etc/grafana/smtp.env
sudo systemctl edit grafana-server

In the editor that opens, add:

[Service]
EnvironmentFile=/etc/grafana/smtp.env

Then sudo systemctl daemon-reload. The drop-in survives package upgrades and rotating the credential is one file edit plus a restart. The Grafana security hardening guide gives the admin password and the secret key the same treatment for the same reason. Use EnvironmentFile and not a bare Environment= line, because the latter shows up in systemctl show for any user who can run it.

Static headers and plain text email

Two smaller sections you'll want eventually. [smtp.static_headers] adds a fixed header to every message Grafana sends, which is how you get alert mail to sort itself into a folder or survive a corporate mail rule. Keys have to be in canonical header form:

[smtp.static_headers]
Foo-Header = bar
X-Grafana-Environment = production

And [emails] controls the body format. The default is HTML only:

[emails]
content_types = text/html

It takes a comma separated list in descending order of preference, and the supported values are text/html and text/plain. Setting content_types = text/plain gives you plain text alerts, which is the right call when notifications get piped into a ticketing system that mangles HTML. text/html, text/plain sends multipart and lets the client choose.

Restart Grafana and send a test email

Configuration is read once at process start. Nothing here reloads.

sudo systemctl restart grafana-server
sudo systemctl status grafana-server --no-pager

The systemd unit is still named grafana-server even though the CLI binary changed to the two-word grafana server form, so that command is correct and will stay correct. Now confirm what the running process loaded rather than what you think you wrote:

curl -s -u admin:yourpassword http://localhost:3000/api/admin/settings | jq .smtp

That endpoint needs basic auth as a Grafana server admin and returns the effective configuration after every layer has been applied, with the password redacted to asterisks. If enabled reads "true" and the host is what you set, the config half is finished. The same view is in the UI under Administration, then General, then Settings.

Then send something. Go to Alerts & IRM, then Alerting, then Notification configuration, open the Contact points tab and create an email contact point with your address in the Addresses field. Save it, click Edit on it, then Test, then Send test notification. Watch the log at the same time:

sudo tail -f /var/log/grafana/grafana.log

A successful send is quiet at info level. A failure prints the dialer's error, which is the useful part. In a container that file is empty by design and the output goes to docker logs, which the guide on where Grafana logs are and how to read them explains along with how to get a real file back. Either way the error text is the thing to read next.

Once the test lands, the walkthrough on setting up email alerts in Grafana picks up with contact point routing and the alert rules themselves.

When email still doesn't send

Two different failure classes, and the error text tells you which one you're in.

Where a failed send reports "SMTP not configured, check your grafana.ini config file's [smtp] section", the config never took effect. That string comes from a single guard on the enabled flag and means nothing else, so the dedicated write-up on fixing SMTP not configured in Grafana works through the seven reasons enabled stays false when you're certain you set it true. Checking your host and port at this stage is wasted effort.

If you get anything else, a timeout, an x509 complaint, a 535 authentication failure, a refused connection, then Grafana is talking to your relay and the relay is unhappy. Test the path without Grafana in the way:

openssl s_client -starttls smtp -crlf -connect smtp.sendgrid.net:587

If that hangs, it's egress filtering or a firewall. If it completes and shows a certificate chain, the network is fine and the problem is credentials or sender identity. The roundup of common Grafana errors and their fixes collects the broader symptoms, most of which have nothing to do with mail. Neither result implicates Grafana itself.

One last practical note: set from_address to a real mailbox on a domain you control, not the [email protected] default. Modern relays check SPF and DKIM alignment against the From domain, and a localhost address fails every one of those checks before your message reaches a recipient's spam folder, never mind their inbox.

Your idea deserves better hosting

24/7 support 30-day money-back guarantee Cancel anytime
Billing Cycle

VPS.S1

£4.41 Save  17 %
£3.67 Monthly
  • 2 vCPU AMD EPYC
  • 2 GB RAMMEMORY
  • 30 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included

VPS.S3

£11.04 Save  33 %
£7.36 Monthly
  • 4 vCPU AMD EPYC
  • 6 GB RAMMEMORY
  • 70 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included

EPYC VPS.P1

£6.62 Save  22 %
£5.15 Monthly
  • 2 vCPU AMD EPYC
  • 4 GB RAMMEMORY
  • 40 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

EPYC VPS.P2

£12.51 Save  24 %
£9.56 Monthly
  • 2 vCPU AMD EPYC
  • 8 GB RAMMEMORY
  • 80 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

EPYC VPS.P4

£22.08 Save  23 %
£16.93 Monthly
  • 4 vCPU AMD EPYC
  • 16 GB RAMMEMORY
  • 160 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

EPYC VPS.P5

£29.44 Save  25 %
£22.08 Monthly
  • 8 vCPU AMD EPYC
  • 16 GB RAMMEMORY
  • 180 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

EPYC VPS.P6

£44.17 Save  25 %
£33.12 Monthly
  • 8 vCPU AMD EPYC
  • 32 GB RAMMEMORY
  • 200 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

EPYC VPS.P7

£51.53 Save  29 %
£36.81 Monthly
  • 16 vCPU AMD EPYC
  • 32 GB RAMMEMORY
  • 240 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

Genoa VPS.G2

£18.40 Save  20 %
£14.72 Monthly
  • 2 vCPUAMD EPYC Genoa 4th generation 9xx4 with 3.25 GHz or similar, on Zen 4 architecture. AMD EPYC G4
  • 4 GB DDR5MEMORY
  • 50 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

Genoa VPS.G4

£33.13 Save  22 %
£25.76 Monthly
  • 4 vCPUAMD EPYC processor with dedicated vCPU cores, on enterprise server hardware. AMD EPYC G4
  • 8 GB DDR5MEMORY
  • 100 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

Genoa VPS.G6

£66.26 Save  22 %
£51.53 Monthly
  • 8 vCPUAMD EPYC processor with dedicated vCPU cores, on enterprise server hardware. AMD EPYC G4
  • 16 GB DDR5MEMORY
  • 200 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

Genoa VPS.G7

£117.80 Save  22 %
£92.03 Monthly
  • 8 vCPUAMD EPYC processor with dedicated vCPU cores, on enterprise server hardware. AMD EPYC G4
  • 32 GB DDR5MEMORY
  • 250 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6IPv6 is currently unavailable in France, Finland or the Netherlands. included
  • Free auto backupsIncludes one backup slot you can set to run daily, weekly or monthly.

AMD Ryzen VPS.R1

£12.51 Save  18 %
£10.30 Monthly
  • 1 dedicated CPU AMD Ryzen 9 7950X with 4.5 GHz or similar, on Zen 4 architecture. vCPU
  • 4 GB DDR5MEMORY
  • 50 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • Auto backup included

AMD Ryzen VPS.R2

£22.08 Save  17 %
£18.40 Monthly
  • 2 dedicated CPUs AMD Ryzen 9 7950X with 4.5 GHz or similar, on Zen 4 architecture. vCPU
  • 8 GB DDR5MEMORY
  • 100 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • Auto backup included

AMD Ryzen VPS.R4

£80.98 Save  18 %
£66.26 Monthly
  • 8 dedicated CPUs AMD Ryzen 9 7950X with 4.5 GHz or similar, on Zen 4 architecture. vCPU
  • 32 GB DDR5MEMORY
  • 400 GB NVMeSTORAGE
  • Unmetered bandwidth
  • IPv4 & IPv6 included IPv6 support is currently unavailable in France, Finland or the Netherlands.
  • Auto backup included

Frequently asked questions

Can Grafana send email through a relay that doesn't require a password?

Yes. Leave user and password empty and set host to your internal relay, for example host = 10.0.0.25:25. Grafana skips the AUTH step when no username is configured. On a private network where the relay accepts by source IP that's a clean setup. Reach for startTLS_policy = NoStartTLS only when the relay genuinely doesn't offer STARTTLS, since the opportunistic default handles both cases on its own.