Architecture Overview¶
MailFallBack is a multi-container application where the MFB service acts as the control plane, managing configuration and orchestration for all sidecar services.
Component Diagram¶
graph TD
Browser["Web Browser"]
subgraph MFB ["MFB Web UI :8000"]
direction LR
D[Dashboard]
A[Accounts]
ADM[Admin]
RST[Restore]
end
subgraph RC ["Roundcube :8001"]
direction LR
RM[Read mail]
SR[Search]
DL[Download]
end
subgraph DOV ["Dovecot IMAP 2.4"]
direction LR
PASSDB[SQL passdb]
USERDB[Lua userdb]
ACL[ACL r/o]
FTS[FTS Flatcurve]
end
TIKA["Apache Tika<br/>:9998"]
UPSTREAM["Upstream IMAP<br/>(Gmail, Outlook, ...)"]
subgraph PG ["PostgreSQL :5432"]
direction LR
USERS[users]
ACCTS[accounts]
JOBS[sync_jobs]
AUDIT[audit_logs]
RCT["rc_* tables"]
end
subgraph MAIL ["Maildir Storage"]
direction LR
MDIR["/data/mailboxes/{uuid}/"]
end
Browser --> MFB
Browser --> RC
RC -- "IMAP :31143" --> DOV
MFB -- "Lua userdb HTTP callback" --> DOV
DOV -- "doveadm :8080" --> MFB
DOV -- "FTS decoder :9998" --> TIKA
MFB -- "mbsync subprocess" --> UPSTREAM
MFB -- "SQL :5432" --> PG
DOV --> PG
DOV --> MAIL
MFB --> MAIL
Data Flow¶
Backup Flow (mbsync)¶
- The APScheduler fires based on each account's cron schedule
- MFB generates an mbsync configuration file for the account
- mbsync runs as a subprocess, connecting to the upstream IMAP server
- Mail is downloaded to the account's Maildir directory on the store volume
- The sync job status and stats are recorded in PostgreSQL
IMAP Access Flow (Dovecot)¶
- A user connects to Dovecot via IMAP (directly or through Roundcube)
- Dovecot authenticates the user via SQL passdb (checks MFB's
userstable) - Dovecot calls the Lua userdb, which makes an HTTP request to MFB's internal API
- MFB returns the list of accounts the user can access (owned + group-shared)
- Dovecot creates dynamic namespaces for each account
- The user sees their backed-up mail, read-only via ACLs
Config Generation Flow¶
- MFB starts and calls
generate_all_configs()during the lifespan event - Dovecot config files are written to the shared
dovecot_confdvolume - Roundcube config (if webmail enabled) is written to the
webmail_confvolume - Dovecot reads these configs when it starts (it depends on MFB being healthy)
- Roundcube reads its config from the shared volume
This means no manual editing of Dovecot or Roundcube configuration is needed. MFB is the single source of truth.
Service Dependencies¶
graph BT
PG["db (PostgreSQL)"]
MFB["mailfallback<br/>(must be healthy)"]
DOV["dovecot<br/>(reads generated configs,<br/>calls Lua userdb)"]
WM["webmail<br/>(connects to Dovecot IMAP,<br/>shares database)"]
TIKA["tika<br/>(Dovecot FTS decoder)"]
MFB --> PG
DOV --> MFB
WM --> DOV
WM --> PG
TIKA --> DOV
MFB must be healthy before Dovecot starts, because Dovecot needs the generated config files. Roundcube must wait for Dovecot. Tika has no dependencies and can start independently.
Key Design Decisions¶
UUID-Based Maildir Paths¶
Account maildirs use UUID directories (/data/mailboxes/{uuid}/) instead of email-based paths. This provides:
- No conflicts - multiple accounts with the same email from different providers
- No special characters - UUIDs are filesystem-safe
- Decoupled from identity - changing ownership is a database-only operation, no file moves
Centralized Config Generation¶
Rather than requiring manual configuration of Dovecot and Roundcube, MFB generates all config files at boot from environment variables. This means:
- One place to configure everything (the
.envfile) - No config drift between services
- Feature flags (Tika, webmail, SSO) automatically propagate to sidecar configs
Read-Only IMAP via ACLs¶
Dovecot ACLs enforce read-only access: * owner lrs (lookup, read, write-seen). Users can read messages and mark them as read, but cannot delete, move, or modify anything. mbsync writes directly to the filesystem, bypassing Dovecot ACLs entirely, so backups are unaffected.
Multi-Owner Accounts¶
The many-to-many relationship between users and accounts (via account_owners) means:
- One account can have multiple owners
- Ownership changes are instant (no file operations)
- Group-based access adds another visibility layer without affecting ownership
Single UID Across Containers¶
All containers run as UID 1000 (user vmail). This ensures consistent file ownership on shared volumes, which is critical for Kubernetes pod-level volume sharing.