Nextcloud app MailDrop: fetches email via IMAP, extracts attachments, and stores them in a configurable folder. Configuration is done in the admin UI.
Author: David Schilling (davejs92@gmail.com)
License: MIT — see LICENSE
Latest release: GitHub Releases
- Multiple mapping configurations (IMAP → target folder), each independently enableable
- IMAP fetch (optional SSL/TLS), with certificate validation toggle
- Attachments land flat in the target folder by default (
{Ymd_His}_uid{N}_{filename}) - Optional: per-mail subfolder (
create_mail_folder) and/or save.emlnext to attachments (save_mail_file) - Subject and sender filters; max attachment size limit
- IMAP cursor (
last_uid/ UIDVALIDITY) with reset in the UI - Mark messages as seen or delete after import
- Connection test and manual fetch per mapping or for all (
occ maildrop:fetch -m <id>) - Background job every 5 minutes
- Admin UI in English and German (follows the Nextcloud user language)
- Nextcloud 28–36
- PHP ≥ 8.1 (no PHP
max-versionininfo.xml; use a PHP version supported by your Nextcloud) - Working system cron (for the background job)
- Outbound IMAP access to the mail server
- Download the release archive: Releases →
maildrop-x.y.z.tar.gz - Extract into
custom_apps/on the server (folder must be namedmaildrop):
sudo tar -xzf maildrop-1.1.1.tar.gz -C /path/to/nextcloud/custom_apps/
sudo chown -R www-data:www-data /path/to/nextcloud/custom_apps/maildrop- Enable the app:
sudo -u www-data php /path/to/nextcloud/occ app:enable maildrop
# Docker example (service name may be `app` or `nextcloud`):
docker compose exec -u www-data app php occ app:enable maildrop- In Nextcloud: Settings → Administration → MailDrop.
./scripts/build-release.sh # version from apps/maildrop/appinfo/info.xml
# or:
./scripts/build-release.sh 1.1.1 # set version in info.xml and buildOutput: dist/maildrop-<version>.tar.gz (includes vendor/) and optional .sha256.
Each mapping connects one IMAP mailbox to one Nextcloud target:
| Setting | Notes |
|---|---|
| IMAP host / port / encryption | none, tls (STARTTLS), or ssl |
| Verify TLS certificate | Recommended on; turn off only for lab/self-signed |
| IMAP user / password / folder | Password stored encrypted; leave empty on save to keep |
| Target user / folder | Files land in that user’s Nextcloud storage |
| Subject / sender filter | Optional substring match |
| Max attachment size | Bytes; 0 = unlimited (default 25 MiB) |
| Create subfolder per email | Off by default |
Save .eml next to attachments |
Off by default |
| Mark as seen / delete after import | Optional |
| Enable fetch | Per-mapping switch (fetch_enabled) |
| Reset cursor | Clears last_uid / UIDVALIDITY so mail is re-scanned |
- Docker + Docker Compose
- Python 3 (test-mail script and E2E)
- Composer (for
apps/maildropdependencies)
cd apps/maildrop && composer install --no-dev
cd ../..
docker compose up -d # core stack (db, mail, nextcloud)
# optional with cron + app init:
docker compose --profile full up -dWait until Nextcloud is ready (first start can take 1–2 minutes):
docker compose ps
# enable the app manually (without profile full):
docker compose exec -u www-data nextcloud php occ app:enable maildrop| Service | URL / port | Credentials |
|---|---|---|
| Nextcloud | http://localhost:8080 | admin / admin |
| GreenMail SMTP | localhost:3025 | – |
| GreenMail IMAP | localhost:3143 | maildrop / maildrop |
| GreenMail Web | http://localhost:8081 | – |
- Log in: http://localhost:8080
- Settings → Administration → MailDrop
- Suggested values: host
mail, port3143, encryptionNone, user/passwordmaildrop/maildrop, target useradmin, target folder/Mail-Anhänge, enable fetch - Test connection, then Save
python3 scripts/send-test-mail.py
# or with a custom file:
python3 scripts/send-test-mail.py --file ./README.mdThen Fetch this mapping in the admin UI (or wait ~5 minutes). Attachments appear under Files → Mail-Anhänge (timestamp-prefixed names unless subfolders are enabled).
php apps/maildrop/tests/Unit/AttachmentNamerTest.phpSends real SMTP mail → GreenMail → occ maildrop:fetch → WebDAV checks. Covers four storage modes: flat default, per-mail folder, .eml sidecar, and both options together.
cd apps/maildrop && composer install --no-dev && cd ../..
docker compose up -d
./tests/integration/run.sh
# or:
python3 tests/integration/test_mail_to_nextcloud.pyCI: .github/workflows/integration.yml (uses docker-compose.ci.yml so the app is copied into the container instead of bind-mounted).
apps/maildrop/ # Nextcloud MailDrop app
appinfo/ # info.xml, routes
lib/ # PHP services, settings, occ, job
js/ css/ templates/ # Admin UI
l10n/ # en + de translations
tests/Unit/ # Unit tests
CHANGELOG.md
LICENSE
docker/nextcloud/ # Init script
docker-compose.yml # Nextcloud, MariaDB, Cron, GreenMail
docker-compose.ci.yml # CI override (no apps bind-mount)
scripts/send-test-mail.py
scripts/build-release.sh # Release tarball including vendor/
tests/integration/ # E2E
dist/ # Build output (gitignored)
flowchart LR
SMTP[Test mail / real SMTP] --> GreenMail[GreenMail IMAP]
GreenMail --> Job[MailDrop background job]
Job --> NC[Nextcloud Files]
UI[Admin UI] --> Job
- Source language for UI/API strings: English
- Bundles:
apps/maildrop/l10n/en.*andapps/maildrop/l10n/de.* - UI language follows the logged-in Nextcloud user’s language
- IMAP credentials are stored encrypted via Nextcloud’s crypto API.
- Prefer SSL/TLS and strong passwords for real mailboxes.
- After installing or upgrading, use Reset cursor if a previous failed fetch left the cursor stuck.
- Set target folder and user deliberately; use subject/sender filters when one mailbox feeds multiple workflows.
| Symptom | Likely cause |
|---|---|
0 imported, 0 skipped, cursor stays at UID 0 |
UID search failed (fixed in 1.1.1 via getByUidGreater); upgrade and reset cursor |
| Fetch OK but no files | Wrong IMAP folder (e.g. Sent vs INBOX), filters, or attachment not detected |
| Files in unexpected place | Check target user and target folder; file picker shows the logged-in admin’s files |
| IMAP login fails after save | Password re-encrypted incorrectly; set the password again and save |
docker compose down
# including all data:
docker compose down -vMIT © David Schilling (davejs92@gmail.com) — see LICENSE.