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
This commit is contained in:
121
02-selfhosting/services/ghost-smtp-mailgun-setup.md
Normal file
121
02-selfhosting/services/ghost-smtp-mailgun-setup.md
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
title: Ghost Email Configuration with Mailgun
|
||||
domain: selfhosting
|
||||
category: services
|
||||
tags:
|
||||
- ghost
|
||||
- mailgun
|
||||
- smtp
|
||||
- email
|
||||
- docker
|
||||
- newsletter
|
||||
status: published
|
||||
created: 2026-04-18
|
||||
updated: 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:
|
||||
> ```bash
|
||||
> 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.
|
||||
|
||||
```yaml
|
||||
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:
|
||||
|
||||
```bash
|
||||
cd /root/<stack-dir> && docker compose up -d
|
||||
```
|
||||
|
||||
Check logs for a clean boot with no mail-related warnings:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
- [ghost-emailanalytics-lag-warning](../../05-troubleshooting/ghost-emailanalytics-lag-warning.md)
|
||||
- [docker-healthchecks](../docker/docker-healthchecks.md)
|
||||
- [watchtower-smtp-localhost-relay](../docker/watchtower-smtp-localhost-relay.md)
|
||||
Reference in New Issue
Block a user