Skip to content

SMTP Endpoints

Everything the Processor needs to reach a mail server is a profile.

SELECT pgrelay_notifier.create_profile(
    p_profile_name => 'mailer',
    p_transport    => 'smtp',
    p_profile      => '{
        "host":     "smtp.example.com",
        "port":     587,
        "security": "starttls",
        "username": "app-mailer",
        "password": "_env:MAILER_SMTP_PASSWORD",
        "from":     "[email protected]"
    }'::jsonb,
    p_channel      => 'notifications'
);

Profile keys

Unknown keys are ignored, so profiles are forward-compatible with future Processor releases.

Key Required Meaning
host SMTP server
port Default 587
security starttls (default), tls (implicit TLS, e.g. port 465), none (internal relays only)
auth plain, login, none. Default: plain if username present, else none
username with auth SMTP username
password with auth The SMTP password — must be an "_env:VAR_NAME" reference (below), never a literal value
from Envelope sender and From header
timeout_seconds Per-send timeout; default 30, hard cap 120

The password never touches the database

"_env:VAR_NAME" is a reference, not a value — it names an environment variable on the machine running the pg_relay Processor, resolved fresh at send time. create_profile/update_profile reject a literal string in password outright; there is no way to make a profile store one. Rotating the password is then an environment change plus nothing — no restart, no SQL.

On a systemd host, put the actual value in the Processor's own environment file:

# /etc/pg_relay/pg_relay.env   (mode 600)
MAILER_SMTP_PASSWORD='the-actual-password'

then sudo systemctl restart pg_relay once — only needed when the variable name itself is new to the process; the value is re-read from the running process's environment at send time otherwise.

Common variants

-- Implicit TLS (port 465)
'{"host":"smtp.example.com","port":465,"security":"tls","username":"app","from":"[email protected]"}'

-- Internal relay, no TLS, no auth (trusted network only)
'{"host":"relay.internal","port":25,"security":"none","from":"[email protected]"}'

Validating before you commit to a profile

create_profile() validates the whole block and lists every problem at once, rather than one error per fix-and-retry cycle. To check a block before creating anything:

SELECT * FROM pgrelay_notifier.validate_profile('smtp', '{"host":"h"}'::jsonb);

Optional: a default sender and Reply-To for this profile

p_send_from and p_reply_to on create_profile()/update_profile() set what a notification uses when the sender composes without p_sender/p_reply_to — useful when every message on a profile should come from (or reply to) the same address without every caller having to say so:

SELECT pgrelay_notifier.create_profile('mailer', 'smtp', '{...}'::jsonb,
    p_channel := 'notifications',
    p_send_from := '[email protected]', p_reply_to := '[email protected]');

Resolution happens once, at compose time — an explicit sender argument beats the profile default, which beats the profile JSON's own from key — and is recorded on the notification, so a later edit to the profile's defaults never changes a message already composed. Clear either with an empty string. This applies equally to Microsoft 365 profiles, where send_from lands on the Graph sender key — see Microsoft 365 Endpoints.