- IMAP mailbox monitoring with SSL/TLS, STARTTLS and plain connection support - ICS attachment extraction from emails and import to Nextcloud via CalDAV - Create or update calendar events by UID to avoid duplicates - Event filtering by summary (contains, regex or exact match, with inversion) - Deduplication by regex pattern extracted from event summaries - Move or mark processed emails as seen after import - Continuous polling mode and one-time run mode - Fully configurable via environment variables - Docker and Docker Compose support - Test suite with pytest
IMAP to Nextcloud Calendar Importer
Automatically monitors an IMAP mailbox for emails with ICS calendar attachments and imports them to your Nextcloud calendar.
Features
- 📧 Monitors IMAP mailbox for new emails
- 📅 Extracts ICS attachments and imports to Nextcloud
- 🔍 Filter events by SUMMARY field (title)
- 🔐 Supports SSL/TLS, STARTTLS, and self-signed certificates
- 🐳 Fully Dockerized
- ⚙️ Configurable via environment variables
- 🔄 Continuous or one-time execution modes
- 📝 Comprehensive logging
Quick Start
-
Clone or download the files
-
Create a
.envfile:cp .env.example .env -
Edit
.envwith your credentials -
Build and run with Docker Compose:
docker-compose up -d -
View logs:
docker-compose logs -f
Configuration
Required Environment Variables
| Variable | Description | Example |
|---|---|---|
IMAP_SERVER |
IMAP server hostname | imap.gmail.com |
IMAP_PORT |
IMAP port (usually 993) | 993 |
IMAP_EMAIL |
Your email address | user@example.com |
IMAP_PASSWORD |
Email password or app password | your-password |
CALDAV_URL |
Nextcloud CalDAV URL | https://cloud.example.com/remote.php/dav |
CALDAV_USERNAME |
Nextcloud username | username |
CALDAV_PASSWORD |
Nextcloud password or app password | your-password |
Optional Environment Variables
| Variable | Default | Description |
|---|---|---|
IMAP_MAILBOX |
INBOX |
Mailbox to monitor |
IMAP_PROCESSED_FOLDER |
Processed |
Folder for processed emails (if MOVE_PROCESSED=true) |
IMAP_USE_SSL |
true |
Use direct SSL/TLS connection (port 993) |
IMAP_USE_STARTTLS |
false |
Use STARTTLS (port 143) - set IMAP_USE_SSL=false first |
IMAP_VERIFY_SSL |
true |
Verify SSL certificates (set to false for self-signed) |
CALENDAR_NAME |
personal |
Target calendar name in Nextcloud |
CALDAV_VERIFY_SSL |
true |
Verify SSL certificates (set to false for self-signed) |
MARK_AS_SEEN |
true |
Mark processed emails as read |
MOVE_PROCESSED |
false |
Move processed emails to separate folder |
CHECK_INTERVAL |
300 |
Seconds between checks (continuous mode) |
RUN_ONCE |
false |
Run once and exit (useful for cron) |
LOG_LEVEL |
INFO |
Logging level (DEBUG, INFO, WARNING, ERROR) |
Summary Filter Options
Filter calendar events by their SUMMARY (title) field:
| Variable | Default | Description |
|---|---|---|
SUMMARY_FILTER |
(empty) | Filter pattern (leave empty to import all) |
SUMMARY_FILTER_MODE |
contains |
Filter mode: contains, regex, or exact |
INVERT_FILTER |
false |
If true, exclude matches instead of including |
Filter Examples
Import only events with "Meeting" in the title:
SUMMARY_FILTER=Meeting
SUMMARY_FILTER_MODE=contains
INVERT_FILTER=false
Import events starting with "Team" (regex):
SUMMARY_FILTER=^Team.*
SUMMARY_FILTER_MODE=regex
INVERT_FILTER=false
Exclude dentist appointments:
SUMMARY_FILTER=Dentist
SUMMARY_FILTER_MODE=contains
INVERT_FILTER=true
Import only exact match "Weekly Standup":
SUMMARY_FILTER=Weekly Standup
SUMMARY_FILTER_MODE=exact
INVERT_FILTER=false
IMAP Connection Modes
The script supports different IMAP connection security modes:
SSL/TLS (Recommended - Port 993)
Direct encrypted connection from the start:
IMAP_USE_SSL=true
IMAP_USE_STARTTLS=false
IMAP_PORT=993
STARTTLS (Port 143)
Starts unencrypted then upgrades to TLS:
IMAP_USE_SSL=false
IMAP_USE_STARTTLS=true
IMAP_PORT=143
Self-Signed Certificates
If your mail server uses self-signed certificates:
IMAP_VERIFY_SSL=false
CALDAV_VERIFY_SSL=false
Security Warning: Only disable SSL verification for trusted internal servers. This makes connections vulnerable to man-in-the-middle attacks.
Plain Connection (NOT RECOMMENDED)
Unencrypted connection:
IMAP_USE_SSL=false
IMAP_USE_STARTTLS=false
Usage Modes
Continuous Mode (Default)
The container runs continuously and checks for new emails every CHECK_INTERVAL seconds:
environment:
- RUN_ONCE=false
- CHECK_INTERVAL=300
One-Time Mode
Run once and exit (useful with external schedulers like cron):
environment:
- RUN_ONCE=true
Or run directly:
docker-compose run --rm imap-calendar-importer
Finding Your Nextcloud Calendar Name
- Go to your Nextcloud calendar
- Click the three dots (⋮) next to your calendar
- Look at the calendar URL or settings
- The calendar name is usually visible in the settings or URL path
Alternatively, run the script with LOG_LEVEL=DEBUG to see all available calendars.
Security Best Practices
-
Use app-specific passwords instead of your main account passwords
- Gmail: Create app password
- Nextcloud: Settings → Security → App passwords
-
Protect your
.envfile:chmod 600 .env -
Never commit credentials to version control:
echo ".env" >> .gitignore
Troubleshooting
Can't connect to IMAP
- Check server address and port
- Verify credentials
- For SSL/TLS use port 993, for STARTTLS use port 143
- Enable "less secure apps" or use app-specific password
- For self-signed certificates: set
IMAP_VERIFY_SSL=false - Check firewall rules
Can't connect to CalDAV
- Verify Nextcloud URL format:
https://your-nextcloud.com/remote.php/dav - Check username and password
- For self-signed certificates: set
CALDAV_VERIFY_SSL=false - Ensure CalDAV is enabled in Nextcloud
Calendar not found
- Check calendar name matches exactly
- Run with
LOG_LEVEL=DEBUGto see available calendars - Ensure you have access to the calendar
Events not being filtered correctly
- Check your
SUMMARY_FILTERpattern - Try
SUMMARY_FILTER_MODE=containsfor simple matching - Use
LOG_LEVEL=DEBUGto see what summaries are being processed
Docker Commands
# Start the service
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the service
docker-compose down
# Rebuild after code changes
docker-compose up -d --build
# Run once manually
docker-compose run --rm imap-calendar-importer
Development
Build the image manually:
docker build -t imap-calendar-importer .
Run with custom environment:
docker run --rm --env-file .env imap-calendar-importer
License
MIT License - feel free to modify and use as needed.