Files
MajorWiki/02-selfhosting/services/ghost-smtp-mailgun-setup.md
majorlinux b40e484aae Add 5 wiki articles from 2026-04-17/18 work
- ghost-smtp-mailgun-setup: two-system email config (newsletter API + transactional SMTP)
- firewalld-fleet-hardening: Fedora fleet firewall audit-and-harden pattern with Ansible
- clamav-fleet-deployment: fleet deployment with nice/ionice throttling + quarantine
- ansible-check-mode-false-positives: when: not ansible_check_mode guard for verify/assert tasks
- ghost-emailanalytics-lag-warning: submitted status, lag counter, fetchMissing skip explained
2026-04-18 11:13:39 -04:00

4.9 KiB

title, domain, category, tags, status, created, updated
title domain category tags status created updated
Ghost Email Configuration with Mailgun selfhosting services
ghost
mailgun
smtp
email
docker
newsletter
published 2026-04-18 2026-04-18T11:13

Ghost Email Configuration with Mailgun

Overview

Ghost uses two separate mail systems that must be configured independently. This is the most common source of confusion in Ghost email setup — configuring one does not configure the other.

System Purpose Where configured
Newsletter / Member email Sending posts to subscribers Ghost Admin UI → Settings → Email (stored in DB)
Transactional / Staff email Magic links, password resets, admin notifications docker-compose.yml environment variables

Both should route through Mailgun for consistent deliverability and tracking.

Prerequisites

  • A Mailgun account with a verified sending domain
  • DNS access for your sending domain
  • Ghost running in Docker (this guide assumes Docker Compose)

Step 1 — DNS Records

Add these records to your sending domain before configuring Ghost. Mailgun will verify them before allowing sends.

Type Name Value
TXT @ v=spf1 include:mailgun.org ~all
TXT pdk1._domainkey (provided by Mailgun — long DKIM key)
CNAME email mailgun.org

The tracking CNAME (email.yourdomain.com) enables Mailgun's open/click tracking. Ghost's EmailAnalytics feature requires it.

After adding records, verify in Mailgun → Sending → Domains → your domain → DNS Records. All records should show green.

Step 2 — Newsletter Email (Mailgun API)

Configure in Ghost Admin → Settings → Email newsletter. Ghost stores these settings in its database settings table — not in the compose file.

Setting Value
Mailgun region US (api.mailgun.net) or EU (api.eu.mailgun.net)
Mailgun domain yourdomain.com
Mailgun API key Private API key from Mailgun dashboard

Ghost uses the Mailgun API (not SMTP) for newsletter delivery. This enables open tracking, click tracking, and the EmailAnalytics dashboard.

Verify via DB: If Ghost is MySQL-backed, you can confirm the settings landed:

docker exec <db-container> mysql -u root -p<password> ghost \
  -e "SELECT key_name, value FROM settings WHERE key_name LIKE 'mailgun%';"

Step 3 — Transactional Email (SMTP via Mailgun)

Configure in docker-compose.yml as environment variables. Ghost's default transport (Direct) attempts raw SMTP delivery, which is blocked by most hosting providers and treated as spam. Mailgun SMTP is the reliable path.

services:
  ghost:
    image: ghost:6-alpine
    environment:
      # ... other Ghost config ...
      mail__transport: SMTP
      mail__from: noreply@yourdomain.com
      mail__options__host: smtp.mailgun.org
      mail__options__port: 587
      mail__options__auth__user: postmaster@yourdomain.com
      mail__options__auth__pass: <mailgun-smtp-password>

The SMTP password is separate from the API key. Find it in Mailgun → Sending → Domains → your domain → SMTP credentials → postmaster@yourdomain.com.

After updating the compose file, restart Ghost:

cd /root/<stack-dir> && docker compose up -d

Check logs for a clean boot with no mail-related warnings:

docker logs <ghost-container> 2>&1 | grep -i mail

Verifying the Full Stack

Newsletter: Send a test post to members (even with 1 subscriber). Check Ghost Admin → Posts → sent post → Email analytics. Delivered count should increment within minutes.

Transactional: Trigger a staff magic link (Ghost Admin → sign out → request magic link). The email should arrive within seconds.

Mailgun logs: Mailgun → Logs → Events shows all API and SMTP activity. Filter by domain to isolate Ghost sends.

Common Issues

Newsletter sends but staff emails don't arrive (or vice versa): The two systems are independent. Check both configurations separately.

transport: Direct in config: Ghost writes a config.production.json inside the container. If mail.transport shows Direct, the environment variables didn't apply — verify the compose key names (double underscores for nested config).

Mailgun API key vs SMTP password: These are different credentials. The API key (starts with key-) is for the newsletter system. The SMTP password is for the transactional system. Don't mix them.

Domain state: unverified in Mailgun: DNS records haven't propagated or are wrong. Use dig TXT yourdomain.com and dig TXT pdk1._domainkey.yourdomain.com to verify from outside your network.

See Also