Importing Emails (MBOX, IMAP, PST)¶
Messages supports importing emails from external sources: MBOX files, IMAP servers, and PST files. This page covers the configuration needed to make the import feature work in production.
Supported Formats¶
| Format | Source | Preserves |
|---|---|---|
MBOX (.mbox) |
Google Takeout, Thunderbird, Apple Mail | Labels, read/unread, starred, drafts, sent status |
| IMAP | Gmail, Proton Bridge, any IMAP server | IMAP flags (\Seen, \Draft, \Flagged) |
PST (.pst) |
Microsoft Outlook | Email content and folder structure |
Gmail Takeout Workflow¶
- Go to Google Takeout
- Deselect all, select only Mail
- Export as MBOX format
- Download the
.mboxfile - In Messages, go to a mailbox → Integrations → Import
Messages recognizes X-Gmail-Labels headers from Google's export format and preserves labels.
Proton Mail Workflow¶
Proton doesn't have a direct MBOX export. You need an intermediate step:
- Proton Mail Bridge (paid plans): installs a local IMAP server
- Connect Thunderbird to the bridge → export mailbox as MBOX → import into Messages
- Or use Proton's native Export Tool (if available on your plan) → MBOX → import
Configuration¶
The import feature requires S3-compatible storage (MinIO) with proper public URL configuration.
The Problem¶
Import files are uploaded to MinIO via presigned URLs. By default, the backend generates URLs using the internal Docker hostname (http://minio:9000). The browser, running on HTTPS, cannot load these HTTP internal URLs — browsers block this as mixed content.
The Fix¶
Three settings in .env are required:
# Internal endpoint (backend communicates with MinIO)
AWS_S3_ENDPOINT_URL=http://minio:9000
# Public URL (browser downloads presigned URLs from here)
MEDIA_BASE_URL=https://s3.messages.<domain>
# Domain replacement — rewrites internal hostname to public hostname
# in presigned URLs. Must be a full URL with scheme.
AWS_S3_DOMAIN_REPLACE=https://s3.messages.<domain>
AWS_S3_DOMAIN_REPLACE is the critical setting. It tells Messages to replace http://minio:9000 with https://s3.messages.<domain> in all presigned URLs. The value must:
- Include the scheme (
https://) - NOT include a trailing slash
- NOT include a port number (Traefik handles port 443)
Traefik Route¶
MinIO must be publicly accessible through Traefik for presigned URLs to work:
minio:
labels:
- "traefik.enable=true"
- "traefik.http.routers.messages-minio.rule=Host(`s3.messages.<domain>`)"
- "traefik.http.routers.messages-minio.entrypoints=websecure"
- "traefik.http.routers.messages-minio.tls=true"
- "traefik.http.routers.messages-minio.tls.certresolver=letsencrypt"
- "traefik.http.services.messages-minio.loadbalancer.server.port=9000"
No additional CORS configuration on MinIO is needed — the presigned URLs are direct GET/PUT requests without Origin headers.
Common Errors¶
"You have aborted the upload"¶
Cause: The browser can't reach the MinIO presigned URL.
Check: The presigned URL in the browser console. If it starts with http://minio:9000/..., AWS_S3_DOMAIN_REPLACE is not set or not working.
❌ http://minio:9000/msg-imports/... (blocked — internal hostname)
✅ https://s3.messages.<domain>/msg-imports/... (works)
"Blocked loading mixed active content"¶
Cause: The presigned URL uses HTTP but the page is HTTPS.
Fix: Set AWS_S3_DOMAIN_REPLACE=https://s3.messages.<domain> (include the scheme).
"Invalid endpoint: s3.messages.example.com"¶
Cause: AWS_S3_DOMAIN_REPLACE is set without the scheme (https://).
Fix: Use AWS_S3_DOMAIN_REPLACE=https://s3.messages.<domain> (full URL with scheme).
"Failed to upload file" / 500 Internal Server Error¶
Cause: The backend cannot parse the MinIO endpoint after domain replacement.
Check:
1. AWS_S3_DOMAIN_REPLACE includes https:// scheme
2. No trailing slash
3. No port (Traefik handles 443)
Storage Buckets¶
The import feature uses a dedicated S3 bucket. Ensure the MinIO init container creates it:
minio-init:
command:
- |
mc alias set myminio http://minio:9000 messages <minio-password>
mc mb myminio/messages-media --ignore-existing
mc mb myminio/msg-imports --ignore-existing # ← Import bucket
mc mb myminio/msg-blobs --ignore-existing
Feature Flag¶
Messages has a feature flag controlling import availability:
This is enabled by default but worth checking if imports are unavailable in the UI.
Large File Handling¶
Messages uses multipart upload for files larger than the chunk size. The chunk size is controlled by:
For very large .mbox files (multiple GB):
- The file is split into 100MB chunks
- Each chunk gets a separate presigned URL
- Chunks are uploaded in parallel
- The backend assembles them in MinIO
- The Celery worker processes the assembled file
Be patient with large imports — they are processed asynchronously by the worker.