Skip to content

Email delivery

Email is off by default. Without it, pfCentral shows invite and reset links once, and an admin passes them on by hand. With it on, pfCentral also emails the link to the person, retries if delivery fails, and still shows the link once as a fallback.

pfCentral only sends two kinds of mail: member invites and password resets. It sends no alerts.

  1. Pick a transport.

    • SMTP: any relay you already run. The right choice for on-premises and air-gapped installs.
    • Resend or Cloudflare Email Service: HTTPS APIs, for installs with internet access.

    Verify the sender domain with your relay or provider and publish its SPF, DKIM and DMARC records. pfCentral does not sign mail itself.

  2. Generate an encryption key. Queued links are encrypted at rest with it.

    Terminal window
    openssl rand -base64 32

    Store it with your other secrets and back it up. If it changes, queued emails cannot be decrypted and their links must be issued again.

  3. Set the public URL. PFCENTRAL_SERVER_BASE_URL must be the address people can open, such as https://pf.example.com. Emailed links use it as is.

  4. Add the settings for your transport and restart:

    PFCENTRAL_EMAIL_ENABLED=true
    PFCENTRAL_EMAIL_ENCRYPTION_KEY=<from step 2>
    PFCENTRAL_EMAIL_FROM=no-reply@example.com
    PFCENTRAL_EMAIL_FROM_NAME=pfCentral
    PFCENTRAL_EMAIL_PROVIDER=smtp
    PFCENTRAL_EMAIL_SMTP_HOST=smtp.example.com
    PFCENTRAL_EMAIL_SMTP_PORT=587
    PFCENTRAL_EMAIL_SMTP_TLS=starttls # or implicit (port 465)
    PFCENTRAL_EMAIL_SMTP_AUTH=plain # plain, login or none
    PFCENTRAL_EMAIL_SMTP_USERNAME=pf-central
    PFCENTRAL_EMAIL_SMTP_PASSWORD=<relay password>

    For an internal relay without authentication, set PFCENTRAL_EMAIL_SMTP_AUTH=none.

    The server checks the settings at start. A missing key, From address or credential stops it with an error instead of silently queueing mail.

  1. The start-up log contains notification worker started provider=smtp (or your provider).
  2. Invite a test member. The Members page still shows the link, and the email arrives.
  3. If it does not arrive, look for the notification.failed audit event and the error in the server log.
invite / reset ──▶ notifications table (link encrypted) ──▶ worker ──▶ SMTP / API
pending → sending → accepted │
▲ └── failed ◀───────┘ after 5 tries
└──────── retry 1m, 5m, 15m, 1h, 4h
  • PostgreSQL is the queue. With several replicas, each email is still sent once.
  • Network errors, 4xx and 429 answers are retried. Permanent errors fail at once.
  • Accepted means the provider took the message, not that it reached the inbox.
  • Finished rows are deleted after PFCENTRAL_EMAIL_RETENTION (default 30 days).
TransportAllow outbound
SMTP with STARTTLSTCP 587 to the relay
SMTP with implicit TLSTCP 465 to the relay
ResendHTTPS to api.resend.com
CloudflareHTTPS to api.cloudflare.com