echo6-docs/runbooks/mailcow-create-mailbox.md
Matt Johnson e9231ac24a Migration: consolidate Echo6 docs to cortex with full infrastructure cleanup sync
- Documents recent infrastructure cleanup (8 CTs destroyed, 35 DNS records removed, Headscale cleanup)
- Adds 24 new runbooks covering Authentik, PeerTube, Meshtastic, RECON, Proxmox, Mailcow, Internet Archive, GPU routing
- Adds project documentation for headscale, vaultwarden, peertube, matrix, mmud, advbbs, arr stack
- Updates services.md, environment.md, caddy.md, authentik.md to match live infrastructure
- Removes 4 deprecated runbook duplicates (canonical versions live in projects/)
- Adds .gitignore for binary archives and editor temp files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-13 06:02:16 +00:00

8.1 KiB

Mailcow: Create Mailbox

Create a new mailbox in Mailcow on the Contabo VPS. Covers both interactive (UI) and API-driven creation, with the critical authsource fix for service accounts.


When to Use This

Any time a new mailbox is created in Mailcow, but especially for service/system accounts that authenticate via SMTP to send mail programmatically (e.g., no-reply@echo6.co used by Authentik, recon@echo6.co used by the RECON pipeline). These accounts don't log in through the Mailcow web UI or SSO — they pass credentials directly to Postfix over SMTP, so they must use local password authentication.


Prerequisites

  • SSH access to Contabo (ssh root@100.64.0.1)
  • Mailcow API key (stored in Mailcow admin UI under System → Configuration → API)
  • Mailcow DB password: source from /opt/mailcow-dockerized/.env (DBPASS)

Inputs

LOCAL_PART=no-reply           # Left side of the @ sign
DOMAIN=echo6.co              # Must already exist in Mailcow
DISPLAY_NAME="Echo6 No Reply" # Friendly name
PASSWORD=<strong-password>   # Generate with: openssl rand -base64 24 | tr -d '/+='
QUOTA_MB=256                  # Mailbox quota in MB
IS_SERVICE_ACCOUNT=true       # true = SMTP sender, false = regular user
MAILCOW_API_KEY=<api-key>    # From Mailcow admin UI

Step 1: Create the Mailbox

Option A: Via Mailcow API

ssh root@100.64.0.1

curl -sk -X POST "https://127.0.0.1:8443/api/v1/add/mailbox" \
  -H "X-API-Key: ${MAILCOW_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "local_part": "'${LOCAL_PART}'",
    "domain": "'${DOMAIN}'",
    "name": "'${DISPLAY_NAME}'",
    "password": "'${PASSWORD}'",
    "password2": "'${PASSWORD}'",
    "quota": '${QUOTA_MB}',
    "active": 1,
    "force_pw_update": 0,
    "tls_enforce_in": 1,
    "tls_enforce_out": 1
  }'

Expected response:

[{"type":"success","msg":["mailbox_added","no-reply@echo6.co"]}]

Option B: Via Mailcow Admin UI

  1. Open https://mail.echo6.co (log in as admin)
  2. Navigate to Email → Mailboxes → Add mailbox
  3. Fill in local part, domain, display name, password, quota
  4. Click Add

Access Flags

For service accounts (send-only), disable unnecessary access after creation:

curl -sk -X POST "https://127.0.0.1:8443/api/v1/edit/mailbox" \
  -H "X-API-Key: ${MAILCOW_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "items": ["'${LOCAL_PART}@${DOMAIN}'"],
    "attr": {
      "sogo_access": "0",
      "imap_access": "0",
      "pop3_access": "0",
      "smtp_access": "1"
    }
  }'

Regular user accounts can leave all access flags at their defaults (all enabled).


Step 2: Fix authsource (CRITICAL for Service Accounts)

The Problem

Mailcow domains configured with OIDC authentication (like echo6.co with Authentik SSO) set authsource=generic-oidc on every new mailbox by default. This tells Dovecot to authenticate the account through the OIDC provider instead of the local password hash.

For service accounts that log in via SMTP with a username and password, this means:

  1. Postfix receives the SMTP AUTH credentials
  2. Postfix hands them to Dovecot for verification
  3. Dovecot's Lua passdb sees authsource=generic-oidc
  4. Dovecot tries to authenticate via Authentik SSO
  5. The service account doesn't exist in Authentik → auth fails silently
  6. SMTP returns 535 5.7.8 Error: authentication failed: (reason unavailable)

The failure message gives no indication that OIDC is the cause. The password is correct, the mailbox exists, and the Mailcow API reports success on password changes — but SMTP auth never works.

The Fix

Change the authsource from generic-oidc to mailcow in the database:

ssh root@100.64.0.1

# Source the DB password
DBPASS=$(grep ^DBPASS /opt/mailcow-dockerized/.env | cut -d= -f2)

# Check current authsource
docker exec mailcowdockerized-mysql-mailcow-1 \
  mysql -u mailcow -p${DBPASS} mailcow -N \
  -e "SELECT username, authsource FROM mailbox WHERE username='${LOCAL_PART}@${DOMAIN}'"

# Fix: set authsource to local password auth
docker exec mailcowdockerized-mysql-mailcow-1 \
  mysql -u mailcow -p${DBPASS} mailcow \
  -e "UPDATE mailbox SET authsource='mailcow' WHERE username='${LOCAL_PART}@${DOMAIN}'"

When to apply:

Account Type authsource Reason
Service account (SMTP sender) mailcow Authenticates with local password via SMTP
Regular user (SSO login) generic-oidc Authenticates through Authentik web SSO
Regular user (IMAP/SMTP client) mailcow Authenticates with local password from mail client

Rule of thumb: if the account will ever authenticate with a username + password (SMTP, IMAP, POP3), set authsource=mailcow. Only leave generic-oidc for accounts that exclusively use SSO web login.


Step 3: Verify SMTP Authentication

Wait a few seconds after the authsource fix, then test:

# From the Contabo host
python3 -c "
import smtplib
s = smtplib.SMTP('mail.echo6.co', 587, timeout=10)
s.starttls()
s.login('${LOCAL_PART}@${DOMAIN}', '${PASSWORD}')
print('SMTP auth: OK')
s.quit()
"

If SMTP auth is being used from inside a Docker container (like Authentik), also test from there:

docker exec authentik-worker python3 -c "
import smtplib
s = smtplib.SMTP('mail.echo6.co', 587, timeout=10)
s.starttls()
s.login('${LOCAL_PART}@${DOMAIN}', '${PASSWORD}')
print('Container SMTP auth: OK')
s.quit()
"

Both should print OK. If either fails with 535 5.7.8 Error: authentication failed, re-check the authsource (Step 2).


Step 4: Store Credentials

Add the new mailbox credentials to /home/zvx/projects/.ref/credentials:

# Mailcow: ${LOCAL_PART}@${DOMAIN}
MAILCOW_${LOCAL_PART^^}_USER=${LOCAL_PART}@${DOMAIN}
MAILCOW_${LOCAL_PART^^}_PASS=${PASSWORD}

Troubleshooting

SMTP auth fails immediately after mailbox creation

Cause: authsource=generic-oidc (see Step 2).

SMTP auth fails after it was previously working

Cause 1: Mailcow may have reset the authsource during a stack restart or update. Re-apply Step 2.

Cause 2: The password hash may have been corrupted by a failed API password reset. Mailcow's password edit API (/api/v1/edit/mailbox) sometimes reports success but doesn't actually update the hash. Workaround: Delete the mailbox and recreate it from scratch (Step 1), then re-apply the authsource fix (Step 2). Do not attempt to reset the password via API.

"Unknown user" in Dovecot logs

Check that the mailbox exists and is active:

curl -sk "https://127.0.0.1:8443/api/v1/get/mailbox/${LOCAL_PART}@${DOMAIN}" \
  -H "X-API-Key: ${MAILCOW_API_KEY}" | python3 -m json.tool

Checking Dovecot auth logs

docker logs mailcowdockerized-dovecot-mailcow-1 --since 5m 2>&1 | grep -i auth

Checking netfilter/fail2ban

Too many failed SMTP login attempts can trigger Mailcow's brute-force protection:

docker logs mailcowdockerized-netfilter-mailcow-1 --since 10m 2>&1 | grep -i ban

If the Contabo IP (5.189.158.149) is banned, restart the netfilter container:

cd /opt/mailcow-dockerized && docker compose restart netfilter-mailcow

Checklist

[ ] Mailbox created (API or UI)
[ ] Access flags set appropriately for account type
[ ] authsource checked — set to 'mailcow' if service account
[ ] SMTP auth verified from host
[ ] SMTP auth verified from consuming container (if applicable)
[ ] Credentials stored in /home/zvx/projects/.ref/credentials

Reference: Current Service Accounts

Mailbox Used By authsource Purpose
no-reply@echo6.co Authentik mailcow SSO invitation emails, notifications
cipher@echo6.co CIPHER generic-oidc Daily intelligence briefs
recon@echo6.co RECON generic-oidc Pipeline notifications
fulcrum@echo6.co Fulcrum generic-oidc Hub notifications

Note: cipher, recon, and fulcrum currently use generic-oidc. If any of these need to send mail via SMTP (not through the SSO web UI), their authsource must be changed to mailcow per Step 2.


Created: 2026-02-16