echo6-docs/vault/runbooks/mailcow-create-mailbox.md
echo6-autocommit cce29c595d auto: docs sync 2026-07-12T00:00:23+00:00
Files changed: ", c.get(k))\nPY\n\\\"\n\"" engine/config.yaml engine/lib/lint.py engine/lib/vocab_gen.py engine/lint-report.md engine/sweep.sh vault/.obsidian/workspace.json vault/archive/projects/meshai-native-fire-severity-audit-cc-handoff.md vault/archive/projects/vaultwarden-plan.md vault/docs/matrix/matrix_host.md vault/docs/matrix/synapse.md vault/docs/services/services.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/projects/matrix-synapse-deployment.md vault/projects/meshai.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/expose-service-contabo.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md
2026-07-12 00:00:23 +00:00

8.5 KiB

title type tags aliases related updated
Mailcow: Create Mailbox runbook
auth
authentik
authentik-oidc-application
caddy
authentik-create-invitation
proxmox-onboard-node
2026-07-11

Mailcow: Create Mailbox

Migrated off Contabo → edge1 (mail-only) on 2026-06-19. Mailcow now lives in edge1 CT 101 (internal 10.10.10.2); public IP 5.189.158.149 is unchanged, but the tailscale address is now 100.64.0.40 (not 100.64.0.1).

Create a new mailbox in Mailcow on edge1 (Mailcow CT 101). 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 edge1 (ssh edge1 or ssh root@100.64.0.40)
  • 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 edge1

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 edge1

# 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 edge1
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 edge1 public 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