exim2sieve is a small Go utility that helps you migrate cPanel Exim filters and mailboxes to modern, Sieve‑capable mail platforms such as Mailcow, DirectAdmin, or any other Dovecot/Sieve setup.
It focuses on:
- Exporting cPanel filter definitions (
filter.yaml/filter) per mailbox. - Converting those filters to Sieve scripts with sane names and comments.
- Importing Sieve scripts into the target server via
doveadm. - Optionally exporting & importing full Maildir contents.
- Creating Mailcow mailboxes via API based on the exported backup tree.
Typical migration flow from cPanel → Mailcow / other Dovecot:
- Export filters (and optionally maildirs) from the cPanel server:
./exim2sieve -cpanel-user myipgr -dest ./backup -maildir
- (Optional) Create mailboxes in Mailcow based on the export tree:
./exim2sieve -config exim2sieve.conf -create-mailcow-mailboxes -backup ./backup/myipgr -domain myip.gr
- Import Sieve into the target Dovecot:
./exim2sieve -config exim2sieve.conf -import-sieve -backup ./backup/myipgr -domain myip.gr
- Import Maildir contents (message migration):
./exim2sieve -config exim2sieve.conf -import-maildir -backup ./backup/myipgr -domain myip.gr
You can also work per single mailbox using -mailbox chris or -mailbox [email protected].
- Export all filters for a single cPanel account:
- Per‑domain
_domain.filter+_domain.sieve. - Per‑mailbox
filter.yaml/filter+ converted*.sieve.
- Per‑domain
- Optional Maildir export for each mailbox.
Command:
./exim2sieve -cpanel-user myipgr -dest ./backup
./exim2sieve -cpanel-user myipgr -dest ./backup -maildir # include Maildir dataExport layout:
backup/
myipgr/ ← cPanel user
mailcow_mailboxes.log ← optional log from Mailcow creation step
myip.gr/ ← domain
_domain.filter ← raw /etc/vfilters/myip.gr (optional)
_domain.sieve ← converted domain-wide Sieve (if present)
chris/
filter.yaml ← original cPanel YAML filter
chris.sieve ← combined Sieve for this mailbox
maildir/ ← optional Maildir copy (if -maildir used)
admin/
filter.yaml
admin.sieve
maildir/
...
Supported inputs:
filter.yaml(cPanel YAML filter format).- Text
filterfiles (cPanel Exim filter syntax).
The internal sieve package:
- Parses cPanel rules into a neutral
Filterformat. - Converts each enabled filter entry into a
SieveScript. - Combines multiple rules into a single, clean Sieve script per mailbox using
CombineScripts.
- Each Exim filter rule has a
filtername(from YAML) or#Name(from text file). - The combined Sieve script:
- Deduplicates all
require [...]lines and puts them at the top. - For each rule, adds:
# Filter: Nixpal if address :is "from" "[email protected]" { fileinto "Nixpal"; stop; }
- Deduplicates all
- Roundcube / Sieve UIs often encode rule names as:
The converter already preserves human‑readable block labels via
# rule:[Nixpal]# Filter: ....
You can later extend it to emit# rule:[name]if you want Roundcube‑style naming.
Example YAML → Sieve:
filter:
-
enabled: 1
filtername: Nixpal
rules:
- part: "$header_from:"
match: is
opt: or
val: [email protected]
actions:
- action: save
dest: Nixpal
- action: finish
dest: ~Becomes:
require ["fileinto"];
# Filter: Nixpal
if address :is "from" "[email protected]"
{
fileinto "Nixpal";
stop;
}You can convert a single filter.yaml or filter file to Sieve as a quick test or standalone tool:
./exim2sieve -path /home/myipgr/etc/myip.gr/chris/filter.yaml -dest ./outThe tool auto‑detects YAML vs text:
- If it looks like
filter.yaml→ parse YAML. - Otherwise → parse text Exim filter.
Output:
out/
filters.sieve ← combined Sieve script for that YAML/filter file
After you have a backup tree (e.g. ./backup/myipgr), you can import Sieve into the target Dovecot using doveadm.
Basic example:
[doveadm]
# Bare metal dovecot:
command = doveadm
# Mailcow (dovecot in Docker), e.g.:
# command = docker exec -i mailcowdockerized-dovecot-mailcow-1 doveadm
[mailcow]
# Optional, used only by -create-mailcow-mailboxes mode
api_url = https://mail.example.com
api_key = your-mailcow-api-key
default_quota = 5GB
[paths]
# Optional, for Maildir import mapping (see below)
# maildir_host_base = /root/chris/backup
# maildir_container_base = /backup./exim2sieve -config exim2sieve.conf -import-sieve -backup ./backup/myipgr -domain myip.grWhat it does:
- Walks
./backup/myipgr/myip.gr/<localpart>/. - For each mailbox (e.g.
chris):- Finds
chris.sieve(or first*.sieve). - Checks if user exists on the target:
doveadm user -u [email protected]
- If the user exists:
- Runs:
doveadm sieve put -u [email protected] cpanel-migrated < chris.sieve doveadm sieve activate -u [email protected] cpanel-migrated
- Logs:
Imported sieve for [email protected] from .../chris.sieve as cpanel-migrated
- Runs:
- Finds
Use the -mailbox filter:
# by localpart
./exim2sieve -config exim2sieve.conf -import-sieve -backup ./backup/myipgr -domain myip.gr -mailbox chris
# or full address
./exim2sieve -config exim2sieve.conf -import-sieve -backup ./backup/myipgr -domain myip.gr -mailbox [email protected]The importer will match either uname == "chris" or address [email protected].
When you export with -maildir, ExportUser additionally copies each mailbox’s Maildir:
./exim2sieve -cpanel-user myipgr -dest ./backup -maildirLayout example:
backup/myipgr/myip.gr/chris/
filter.yaml
chris.sieve
maildir/
cur/
new/
tmp/
backup/myipgr/myip.gr/admin/
filter.yaml
admin.sieve
maildir/
...
The Maildir copy is a straightforward directory copy (retaining cur/, new/, tmp/).
Use -import-maildir:
./exim2sieve -config exim2sieve.conf -import-maildir -backup ./backup/myipgr -domain myip.grFor each mailbox:
-
Verifies that
maildir/exists and looks like Maildir (at leastcur/ornew/). -
Checks if the user exists via
doveadm user -u addr. -
If yes, runs:
doveadm import -u addr maildir:/path/to/maildir "" ALL -
Logs:
Imported maildir for [email protected] from backup/myipgr/myip.gr/chris/maildir
When Dovecot runs inside Docker (e.g. Mailcow), the host path where backups live may not be visible directly.
To handle this, ImportMaildir supports host→container mapping via [paths] config:
[paths]
maildir_host_base = /root/chris/backup
maildir_container_base = /backupAssuming you mount your backup into the Dovecot container:
# docker-compose override example
dovecot-mailcow:
volumes:
- /root/chris/backup:/backup:roThen host path:
/root/chris/backup/myipgr/myip.gr/chris/maildir
is automatically mapped to:
/backup/myipgr/myip.gr/chris/maildir
So the actual doveadm command becomes:
doveadm import -u [email protected] maildir:/backup/myipgr/myip.gr/chris/maildir "" ALLIf maildir_host_base / maildir_container_base are empty, the tool simply uses the host path as‑is (good for DirectAdmin / bare metal Dovecot).
You can also limit to a single mailbox:
./exim2sieve -config exim2sieve.conf -import-maildir -backup ./backup/myipgr -domain myip.gr -mailbox chrisexim2sieve can read the backup tree and create Mailcow mailboxes via API before you import filters/Maildir.
In exim2sieve.conf:
[mailcow]
api_url = https://mail.example.com
api_key = your-mailcow-api-key
default_quota = 10GB ; mailbox quota (MB/GB syntax supported)./exim2sieve -config exim2sieve.conf -create-mailcow-mailboxes -backup ./backup/myipgr -domain myip.grBehavior:
- Walks
./backup/myipgr/myip.gr/<localpart>/and builds a mailbox list. - Uses the Mailcow API client to:
- Ensure the domain exists (or create it if needed, depending on your Mailcow config/permissions).
- Create each mailbox with the configured default quota.
- Logs a summary to
mailcow_mailboxes.logunderbackup/myipgr.
You can then run -import-sieve and -import-maildir to attach filters and messages to those mailboxes.
exim2sieve is mode‑based: exactly one of the main modes should be active per run.
-
-cpanel-user <user>/-account <user>
Export filters (and optionally Maildir) for a cPanel account:./exim2sieve -cpanel-user myipgr -dest ./backup ./exim2sieve -cpanel-user myipgr -dest ./backup -maildir
-
-path <file>
Convert a singlefilter.yamlorfiltertofilters.sievein-dest. -
-import-sieve
Import Sieve scripts from a backup usingdoveadm. -
-import-maildir
Import Maildir contents from a backup usingdoveadm import. -
-create-mailcow-mailboxes
Create Mailcow domains/mailboxes from a backup tree via the Mailcow API.
-
-dest <dir>
Destination folder for exports (default:./backup). -
-maildir
When exporting from cPanel, also copy each mailbox's Maildir undermaildir/. -
-backup <path>
Root of existing backup tree for import modes (e.g../backup/myipgr). -
-domain <domain>
Limit import/export to a specific domain (myip.gr).
(Useful when an account has many domains.) -
-mailbox <name or full address>
Limit Sieve/Maildir import to a single mailbox:-mailbox chris-mailbox [email protected]
-
-config <file>
Path toexim2sieve.conf.
If omitted, the loader will try./exim2sieve.confand/etc/exim2sieve.conf.
Fallback defaults:doveadmin PATH, Mailcow disabled.
On the cPanel server:
./exim2sieve -cpanel-user myipgr -dest /root/chris/backup -maildir
tar czf myipgr-backup.tgz -C /root/chris/backup myipgrMove myipgr-backup.tgz to the Mailcow host, extract to /root/chris/backup.
On the Mailcow host:
-
Ensure backup is mounted inside Dovecot container:
dovecot-mailcow: volumes: - /root/chris/backup:/backup:ro
-
Configure
exim2sieve.conf:[doveadm] command = docker exec -i mailcowdockerized-dovecot-mailcow-1 doveadm [mailcow] api_url = https://mail.example.com api_key = your-mailcow-api-key default_quota = 10GB [paths] maildir_host_base = /root/chris/backup maildir_container_base = /backup
-
Create Mailcow mailboxes:
./exim2sieve -config exim2sieve.conf -create-mailcow-mailboxes -backup /root/chris/backup/myipgr -domain myip.gr
-
Import Sieve:
./exim2sieve -config exim2sieve.conf -import-sieve -backup /root/chris/backup/myipgr -domain myip.gr
-
Import Maildir:
./exim2sieve -config exim2sieve.conf -import-maildir -backup /root/chris/backup/myipgr -domain myip.gr
./exim2sieve -cpanel-user myipgr -dest ./backup # on cPanel side
rsync -av ./backup/myipgr mycow:/srv/exim2sieve/ # copy tree
# On target server (with dovecot):
./exim2sieve -config exim2sieve.conf -import-sieve -backup /srv/exim2sieve/myipgr -domain myip.gr -mailbox chrisContributions, bug reports and feature ideas are welcome.
- Add support for more complex Exim constructs.
- Improve Sieve rule naming (e.g. Roundcube‑style
# rule:[name]). - Extend import/export helpers for other control panels.
Please open an issue or PR on GitHub with a clear description and, if possible, sample filter.yaml / filter snippets.
Usage flow
- Export from cPanel (creates backup + shadow under per-domain dirs): ./exim2sieve -cpanel-user mycpuser -dest ./backup -maildir
-
Create mailboxes in Mailcow via API: ./exim2sieve -config exim2sieve.conf
-create-mailcow-mailboxes
-backup ./backup/mycpuser
-domain myip.gr -
Patch passwords from shadow into Mailcow MySQL: ./exim2sieve -config exim2sieve.conf
-mailcow-passwords-from-shadow
-backup ./backup/mycpuser
-domain myip.gr