--- URL: "https://doc.dovecot.org/main/llms-full.txt" LLMS_URL: "https://doc.dovecot.org/main/llms-full.txt" --- # Dovecot Community Edition (CE) * * * Dovecot is an open source email server for Linux/UNIX-like systems, written with security primarily in mind. Dovecot is an excellent choice for both small and large installations. It's fast, simple to set up, requires no special administration and it uses very little memory. Dovecot supports the IMAP ([RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)), POP3 ([RFC 1939](https://datatracker.ietf.org/doc/html/rfc1939)), LMTP ([RFC 2033](https://datatracker.ietf.org/doc/html/rfc2033)), and ManageSieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) mail protocols. ## Dovecot Pro [Dovecot Pro](https://www.open-xchange.com/portfolio/ox-dovecot-pro/) is a commercial software product of [Open-Xchange](https://www.open-xchange.com/). Dovecot Pro implements Palomar, a cloud-native architecture that allows for Dovecot to be highly available, scalable, and stateless. Dovecot Pro uses Dovecot Core as part of the Palomar architecture. For full details and documentation on Dovecot Pro, visit [Dovecot Pro](https://www.dovecotpro.com/). ## Dovecot Community Edition [Dovecot Community Edition (CE)](https://www.dovecot.org/) is the open source version of Dovecot. Dovecot CE is designed for use on a single server. There is no support or maintenance for multiple Dovecot servers to interact with each other. This site fully documents Dovecot CE and its behavior. Dovecot CE is also referred to as "Dovecot Core" in this documentation. To get started, see [https://repo.dovecot.org/](https://repo.dovecot.org/) and [Quick configuration](/main/core/config/quick.html). Please use the [Dovecot mailing list](https://www.dovecot.org/mailinglists.html) for questions about Dovecot CE. # Contributing to the Documentation There are several ways you can contribute to improving the Dovecot documentation. ## Submitting Fixes and Improvements If you are familiar with Git and [GitHub](https://github.com/), you can submit your changes directly to the documentation repository. 1. **Fork the repository:** Start by creating a fork of the official [Dovecot documentation repository](https://github.com/dovecot/documentation/) on GitHub. 2. **Clone your fork:** Clone your newly created fork to your local machine: shell ``` git clone https://github.com/YOUR_USERNAME/documentation.git cd documentation ``` 3. **Create a new branch:** Create a new branch for your changes. Choose a descriptive name for your branch. shell ``` git checkout -b my-awesome-fix ``` 4. **Make your changes:** Edit the documentation files as needed. 5. **Commit your changes:** Once you are happy with your changes, commit them with a descriptive commit message. The commit message should be in the format `docs: A brief summary of the change`. The commit message should use imperative language ("Fix", "Change", "Add"), not past-tense ("Fixed", "Changed", "Added"). Use a single prefix term that describes the area of the documentation the commit affects (e.g., "settings", "fts"). shell ``` git commit -am "installation: Fix a typo in the installation guide" ``` 6. **Push your changes:** Push your changes to your fork on GitHub. shell ``` git push origin my-awesome-fix ``` 7. **Create a Merge Request:** After pushing your commit, you should see a GitHub link to create a merge request in the Git output contents. Click on this link and complete the form to submit the merge request to the official Dovecot repository. (If you don't see the link, go to your personal fork of the documentation project on GitHub to find the submission link.) ## I Don't Know How to Code! Even if you're not comfortable with Git and GitHub, you can still contribute to the documentation. If you find a typo, a factual error, or have a suggestion for improvement, you can submit an issue to the [GitHub issue tracker](https://github.com/dovecot/documentation/issues). When submitting an issue, please include the following information: - **The URL of the page** where you found the issue. - **A description of the issue.** Be as specific as possible. For example, instead of saying "the documentation is confusing", say "in the section on XYZ, the first paragraph is unclear because...". - **A suggestion for how to fix the issue.** If you have an idea for how to improve the documentation, please share it! The core developers will review your suggestion and incorporate it into the documentation if they agree that it's a good idea. # Doveadm "Doveadm" is Dovecot's administrative tools. They can be run either from the command line, via the doveadm protocol, or via the doveadm HTTP API. ## Commands See [All Dovecot Doveadm Commands](/main/core/summaries/doveadm.html#all-dovecot-doveadm-commands). ## Error/Exit Codes The `doveadm` and `dovecot-lda` tools use mostly `sysexits.h` compatible error codes. | Code | Label | Status | Description | | --- | --- | --- | --- | | 0 | | Success | | | 2 | | Success, but mailbox changed during the sync | This code can be safely ignored during intermediate migrations (any changes will be captured during a subsequent sync). For a final migration/cut-over, this error code indicates that the dsync command should be re-run to ensure that all changes made to the original mailbox are reflected in the new mailbox (i.e., a final migration should not be considered successful unless/until dsync returns 0). | | 64 | EX\_USAGE | Incorrect parameters | Dsync was called with wrong parameters. This should never be seen in production migration usage (absent a bug). | | 65 | EX\_DATAERR | Data error | Theoretically can happen, but should never happen in real-life usage. If seen, it should be handled as a failed migration, and details should be reported to Dovecot for further investigation. | | 66 | EX\_NOINPUT | Cannot open input | | | 67 | EX\_NOUSER | User no longer exists in user DB | Either this user should not be migrated (since they no longer exist) and this user should be removed from the migration list, or there is some issue interacting with the local identity backend, in which case this migration should be treated as a temporary failure (i.e. retry and/or requeue). | | 68 | EX\_NOHOST | Hostname Unknown | Source or destination hostname is not known/resolvable. | | 69 | EX\_UNAVAILABLE | Service Unavailable | | | 70 | EX\_SOFTWARE | Internal Software Error | | | 71 | EX\_OSERROR | System Error (e.g., system cannot fork) | Issue with operating system of host running dsync. | | 72 | EX\_OSFILE | Critical OS File Missing | Issue with operating system of host running dsync. | | 73 | EX\_CANTCREAT | Cannot create mailbox/message as user is out of quota | For migrations, this should not occur as new storage quotas should be equal/greater to the existing quota. If this error occurs, would possibly indicate an issue with the sync (i.e. duplicate message). In this case, the migration should be marked as failed and the user flagged for further investigation why the sync was unsuccessful. | | 74 | EX\_IOERR | Input/Output Error | | | 75 | EX\_TEMPFAIL | Temporary failure | A temporary error that may be resolved by running the migration again. For migration purposes, if this code is returned dsync should be re-run. There should be some sort of maximum retry value defined; if exceeded, the account should either be marked as "error" or should be placed back in the queue to be attempted to be migrated at a later time. | | 76 | EX\_PROTOCOL | Remote error in protocol | | | 77 | EX\_NOPERM | Authentication failure | If authentication to the existing mail backend is via master user authentication, this error should not occur. If it does occur, there is a problem with the configuration (or, less likely, a bug in dsync) and all migrations should be suspended until the problem can be resolved. If authentication to the existing mail backend is via the user's current authentication credentials, this indicates that the credentials are no longer valid. This migration should be marked as either a temporary failure (if the authentication credentials are automatically updated when running the migration) or a permanent failure if there is no ability to obtain the new authentication credentials. Migrations into Dovecot (the new system) should be done via a master user, so this error should not be returned once the system is correctly configured. If this error still occurs and is triggered by a failure to connect to the new platform, all migrations should be suspended until the problem can be resolved. Note: EX\_NOPERM error might also happen for other reasons, such as not having write permissions to a folder, but this shouldn't happen with dsync use in migration. | | 78 | EX\_CONFIG | Invalid Settings/Configuration | This error should not be obtained once the migration system is correctly configured, e.g. after testing the migration system in a staging environment. If this error occurs, all migrations should be suspended until the problem can be resolved. | | 1003 | DOVEADM\_EX\_EXPIRED | Outdated/Expired | The command could not complete successfully because the requested contents are no longer valid or no longer up to date. Added: 2.4.0 | ### Other Issues - Folder renames if the names are invalid or too long. dsync attempts to fix invalid folder names automatically. If the folder name is too long, a new generated GUID is given it as the name. A related issue is that if any renaming happens, the folder won't be synced incrementally because dsync doesn't realize that the folder was renamed (dsync is stateless). ## HTTP API WARNING Do not expose Doveadm HTTP API to untrusted networks. It is an unsafe API with dangerous features. It should always be exposed only to your integration API with TLS enabled, secure and random password or API key configured, and relevant network level restrictions applied. ### Configuration To be able to use doveadm HTTP API it's mandatory to configure either `[doveadm_password](/main/core/summaries/settings.html#doveadm_password)` for doveadm or an `[doveadm_api_key](/main/core/summaries/settings.html#doveadm_api_key)`. To configure password for doveadm service in `dovecot.conf`: doveconf ``` doveadm_password = secretpassword ``` Or, if preferred to use separate key for doveadm HTTP API, then it can be enabled by defining key in config: doveconf ``` doveadm_api_key = key ``` Enable the doveadm http listener: doveconf ``` service doveadm { unix_listener doveadm-server { user = vmail } inet_listener doveadm { port = 2425 } inet_listener http { port = 8080 #ssl = yes # uncomment to enable https } } ``` ### Usage Connecting to the endpoint can be done by using standard HTTP protocol and authentication headers. INFO There is also [https://github.com/dovecot/doveadm-http-cli](https://github.com/dovecot/doveadm-http-cli) that can be used for accessing the API. All doveadm commands are accessed under the `/doveadm/v1` path. #### Command List Sending an authenticated GET request to the root of the endpoint returns all routes exposed by the endpoint, while a GET request to the `/doveadm/v1` path returns the list of doveadm API commands supported by the endpoint. Both responses are JSON arrays. ##### Root endpoint Example, using `[doveadm_password](/main/core/summaries/settings.html#doveadm_password)` authentication: console ``` curl -X GET -u doveadm:password http://host:port/ ``` json ``` [ {"method": "OPTIONS", "path": "*"}, {"method": "GET", "path": "/"}, {"method": "GET", "path": "/doveadm/v1"}, {"method": "POST", "path": "/doveadm/v1"} ] ``` ##### /doveadm/v1 endpoint console ``` curl -X GET -u doveadm:password http://host:port/doveadm/v1 ``` Each entry in the array describes one command: the command name and the list of parameters it accepts. Each parameter has a name and a JSON type - `string`, `boolean`, `integer`, or `array` (a list of values). The returned command set depends on the release and configuration of the endpoint. Example output: json ``` [ { "command": "reload", "parameters": [] }, { "command": "statsDump", "parameters": [ {"name": "socketPath", "type": "string"}, {"name": "reset", "type": "boolean"}, {"name": "fields", "type": "string"} ] }, { "command": "fsDelete", "parameters": [ {"name": "recursive", "type": "boolean"}, {"name": "maxParallel", "type": "integer"}, {"name": "filterName", "type": "string"}, {"name": "path", "type": "array"} ] } ] ``` #### Authentication ##### Basic Auth Use password as configured in `[doveadm_password](/main/core/summaries/settings.html#doveadm_password)`. User name is `doveadm`. ``` curl -H "Authorization: Basic " http://host:port/doveadm/v1 ``` or console ``` curl -u doveadm:password http://host:port/doveadm/v1 ``` ##### `X-Dovecot-API` Auth Use API Key as configured in `[doveadm_api_key](/main/core/summaries/settings.html#doveadm_api_key)`. sh ``` curl -H "Authorization: X-Dovecot-API " \ http://host:port/doveadm/v1 ``` ### API Overview #### Request sh ``` curl -H "Authorization: Basic " http://host:port/doveadm/v1 curl -u doveadm:password http://host:port/doveadm/v1 ``` There is also [https://github.com/dovecot/doveadm-http-cli](https://github.com/dovecot/doveadm-http-cli) that can be used for accessing the API. ### API overview All commands sent to the API needs to be posted in json format using `Content-Type: application/json` in headers for the request type and the JSON content as payload in format: json ``` [ [ "command1", { "parameter1": "value", "parameter2": "value", "parameter3": "value" }, "tag1" ] ] ``` Multiple commands can be submitted in one json payload: json ``` [ [ "command1", { "parameter1": "value", "parameter2": "value" }, "tag1" ], [ "command2", { "parameter1": "value", "parameter2": "value" }, "tag2" ] ] ``` WARNING For now it is safest not to send multiple commands in one payload, as some commands may kill the server in certain error conditions leaving you without any response. Also it is not guaranteed that the commands will be processed in order. INFO All commands are case sensitive. #### Response Requests that fail _before_ the doveadm command is run returns 400/500 HTTP response codes: | Code | Reason | | --- | --- | | 400 | Invalid request. Response body contains error message in text/plain. | | 401 | Unauthorized (missing authentication). | | 403 | Forbidden (authentication failed). | | 404 | Unknown doveadm command. | | 500 | Internal server error (see Dovecot logs for more information). | Otherwise, the response will be a 200 code, Content-Type `application/json`, and the body will be a JSON object. ##### Success Note All data returned in the JSON object will be strings (e.g., counting fields will be strings, not numbers/integers). json ``` [ [ "doveadmResponse", [], "tag1" ] ] ``` ##### Failure `exitCode` are the [doveadm error codes](/main/core/admin/doveadm.html#error-exit-codes). json ``` [ [ "error", { "exitCode": 68, "type": "exitCode" }, "tag1" ] ] ``` ### Example Session Reload Dovecot configuration: json ``` [ [ "reload", {}, "tag1" ] ] ``` Then we execute it with curl: sh ``` curl -v -u doveadm:secretpassword -X POST http://localhost:8080/doveadm/v1 \ -H "Content-Type: application/json" -d '[["reload",{},"tag1"]]' ``` This is equivalent to the command `[doveadm reload](/main/core/summaries/doveadm.html#reload)`. ## Mailbox Commands These commands should be run on one of the Dovecot proxies. The proxy is then responsible for forwarding the command to be run in the correct backend. This guarantees that two backend servers don't attempt to modify the same user's mailbox at the same time (which might cause problems). Changed: 2.4.0 All mail commands require providing `-u`, `-F` or `-A` parameter. `USER` environment variable is no longer supported. This will always be subject to user database lookup and requires access to auth userdb socket. ### doveadm fetch See `[doveadm fetch](/main/core/summaries/doveadm.html#fetch)`. Fetch mail contents or metadata. #### doveadm search `[doveadm search](/main/core/summaries/doveadm.html#search)` does the same as `[doveadm fetch 'mailbox-guid uid'](/main/core/summaries/doveadm.html#fetch)`. It's useful for quick checks where you don't want to write the full fetch command. ### doveadm copy See `[doveadm copy](/main/core/summaries/doveadm.html#copy)`. Copy message to another folder, potentially to another user. ### doveadm deduplicate See `[doveadm deduplicate](/main/core/summaries/doveadm.html#deduplicate)`. Deduplicate mails either by their GUID or by `Message-Id:` header. ### doveadm expunge See `[doveadm expunge](/main/core/summaries/doveadm.html#expunge)`. Expunge mails (without moving to Trash). ### doveadm flags add/remove/replace See `[doveadm flags add]()`, `[doveadm flags remove]()`, `[doveadm flags replace]()`. Update IMAP flags for a mail ### doveadm force-resync See `[doveadm force-resync](/main/core/summaries/doveadm.html#force-resync)`. Try to fix a broken mailbox (or verify that all is ok). ### doveadm index See `[doveadm index](/main/core/summaries/doveadm.html#index)`. Index any mails that aren't indexed yet. Mainly useful if [fts plugin](/main/core/plugins/fts.html) is enabled. ### doveadm mailbox create/delete/rename See `[doveadm mailbox create]()`, `[doveadm mailbox delete]()`, `[doveadm mailbox rename]()`. Modify folders. ### doveadm mailbox list See `[doveadm mailbox list]()` List user's folders. ### doveadm mailbox subscribe/unsubscribe See `[doveadm mailbox subscribe]()`, `[doveadm mailbox unsubscribe]()`. Modify IMAP folder subscriptions. ### doveadm mailbox status See `[doveadm mailbox status]()` Quickly lookup folder metadata (# of mails, # of unseen mails, etc.). ### doveadm move See `[doveadm move](/main/core/summaries/doveadm.html#move)`. Move message to another folder, potentially to another user. # Dovecot Errors and Troubleshooting ## Client Issues WARNING This section may contain old information. If you do find client(s) that need special configuration, or no longer need it, please let us know. It seems to be quite difficult to implement a working IMAP client. [Best Practices for Implementing an IMAP Client](http://www.imapwiki.org/ClientImplementation) tries to help with it. ### Negative UIDs `Invalid messageset: 1181461470:-1181461446.` IMAP uses unsigned 32bit integers for unique message identifiers. Unfortunately a lot of IMAP clients use 32bit signed integers, which means that if the UIDs go higher than 2147483647, they'll wrap to negative integers. This causes errors such as above. However normally the UIDs should never go that high, so it's possible to avoid this problem. Earlier Dovecot versions had bugs which could cause `X-UID:` headers in incoming messages to grow the UIDs too high. Some spam messages especially contained these intentionally broken `X-UID:` headers. With newer Dovecot versions these broken `X-UID:` headers aren't practically ever used. It happens only if the mail has a valid `X-IMAPbase`: header, `X-UID:` header, and the mail is written to an empty mbox file. Note that this can happen only to new mboxes, because expunging all messages in a mailbox causes Dovecot to create a metadata message at the beginning of the mbox file. In any case it's still a good idea to filter out `X-UID:` and other metadata headers in your MDA. [LDA](/main/core/config/delivery/lda.html) does this internally. See [mbox header filtering](/main/core/config/mailbox_formats/mbox.html#avoiding-crashes-and-errors). #### Fixing Fixing is done by letting Dovecot update UIDVALIDITY value and recreate the UIDs beginning from one. This means that client's local cache will be invalidated and the client will be required to download all the messages again. ##### mbox Delete Dovecot's index files (eg. `.imap/INBOX/`) and `X-IMAP:` and `X-IMAPbase:` headers from the mbox file. ##### Maildir This should really never be a problem with Maildir. If however you have managed to cause it somehow (by receiving 2 billion mails?), you can recreate the UIDs by deleting `dovecot-uidlist` file. ### Specific Clients If not listed in this section, there are no known issues. #### Apple Mail.app On Mac OS X Leopard 10.5 Mail.app appears to support subscribe/unsubscribe by right clicking on a mailbox, selecting 'Get Account Info' and selecting 'Subscription List' from tabs. This however doesn't really work with any IMAP server. Apple Mail 3.6 (that comes with OS X 10.5 Leopard) supports subscribing/unsubscribing to folders in the public namespace. #### Outlook - You should enable `[pop3_client_workarounds = outlook-no-nuls](/main/core/summaries/settings.html#pop3_client_workarounds)` workaround with POP3. - If some Outlook users don't see new or sent mails in the appropriate folders after a migration from UW IMAPd even if they are visible in other clients (e.g. Roundcube, Thunderbird, or on the disk itself), and you get the error message "BAD Error in IMAP command UID: Invalid UID messageset" in the log or rawlog: It helps to remove the problematic IMAP account completely from Outlook and recreating it again there. It speaks a different IMAP afterwards, so there are reasons to believe it caches the details of some server on the first connect and doesn't refresh them even if you change the server's hostname in the account settings. #### Thunderbird - If you're using [mbox](/main/core/config/mailbox_formats/mbox.html), [dbox](/main/core/config/mailbox_formats/dbox.html), or [Maildir](/main/core/config/mailbox_formats/maildir.html) with `:LAYOUT=fs` you should enable `[imap_client_workarounds = tb-extra-mailbox-sep](/main/core/summaries/settings.html#imap_client_workarounds)` workaround for IMAP. ([Bug report](https://bugzilla.mozilla.org/show_bug.cgi?id=29926)) - If you're using [mbox](/main/core/config/mailbox_formats/mbox.html), and if you are not using a technique to allow folders that contain both sub-folders and messages (see [mbox child folders](/main/core/config/mailbox_formats/mbox.html#child-folders)) then you will have to disable "Server supports folders that contain sub-folders and messages" setting from Thunderbird. ([Enhancement request](https://bugzilla.mozilla.org/show_bug.cgi?id=284933)) - Thunderbird may display incorrect new mail counts in the New Mail notification box. This is due to a bug in Thunderbird's handling of the CONDSTORE extension. See [Bug Report](https://bugzilla.mozilla.org/show_bug.cgi?id=885220) for details and a client-side workaround. ## Time Moved Backwards Error Dovecot isn't very forgiving if your system's time moves backwards. There are usually three possibilities why it's moving backwards: 1. You're running `ntpdate` periodically. This isn't a good idea. 2. You're using some kind of a virtual server and you haven't configured it right (or it's buggy). 3. Dovecot is started before time is synchronized at server startup. See [Time Synchronization](/main/core/config/time_sync.html). ## CentOS/RHEL8 Mail Location When installing dovecot on CentOS8 or RHEL8, you might experience problems with writing into mail location. This is due to several restrictions that need to be disabled. ### SystemD restrictions The packages distributed with CentOS8 come with restrictive systemd unit files. These restrictions are good from security perspective, yet the errors do not guide into the correct changes. If you see log messages such as: `dovecot: imap(test): Namespace '': mkdir(/home/mail/domain/test/Maildir) failed: Permission denied (euid=1005(vmail) egid=1005(vmail) missing +w perm: /home/mail/domain, UNIX perms appear ok (ACL/MAC wrong?))` You need to use `systemctl edit dovecot` to add following stanza ``` [Service] ReadWritePaths=/home/mail ``` And run `systemctl daemon-reload`. ### SELinux You can check `/var/log/audit/audit.log` for message such as: `type=AVC msg=audit(1586604621.637:6736): avc: denied { write } for pid=12750 comm="imap" name="Maildir" dev="dm-3" ino=438370738 scontext=system_u:system_r:dovecot_t:s0 tcontext=unconfined_u:object_r:etc_runtime_t:s0 tclass=dir permissive=0 type=SYSCALL msg=audit(1586604621.637:6736): arch=c000003e syscall=83 success=no exit=-13 a0=55b493a7f338 a1=1ed a2=ffffffff a3=fffffffffffffcd8 items=0 ppid=12735 pid=12750 auid=4294967295 uid=1005 gid=1005 euid=1005 suid=1005 fsuid=1005 egid=1005 sgid=1005 fsgid=1005 tty=(none) ses=4294967295 comm="imap" exe="/usr/libexec/dovecot/imap"subj=system_u:system_r:dovecot_t:s0 key=(null)` If you see this, you need to relabel your mail location to `mail_home_rw_t`. sh ``` chcon -R -t mail_home_rw_t /home/mail ``` To make this change permanent, you need to add new fcontext rule: sh ``` semanage fcontext --add --type mail_home_rw_t --range s0 '/home/mail(/.*)?' ``` After this, Dovecot should be able to write into your mail location again. ## dovecot.index.cache is Too Large ### Problem 1 `Error: Corrupted index cache file .../dovecot.index.cache: Cache file too large` The problem in these cases is that the user has a folder with a large number of messages. The only solution for now is to delete the `dovecot.index.cache` for that folder. Since v2.3.11 this is done automatically. The cache files generally are useful to reduce disk IO by being able to read commonly accessed data from the cache instead of opening the individual emails. However, usually these kind of huge folders are some kind of system accounts which just gather a lot of mails which are periodically deleted. In these cases the cache file usually isn't very useful. ### Problem 2 Alternatively you can encounter: `Fatal: master: service(imap): child ... returned error 83 (Out of memory (service imap { vsz_limit=1024 MB }, you may need to increase it))` and: `Error: mmap(size=...) failed with file .../dovecot.index.cache: Cannot allocate memory` These usually happen because the `dovecot.index.cache` file is so large that it can't fit into the memory. The solution is usually to either raise the imap service's `[service_vsz_limit](/main/core/summaries/settings.html#service_vsz_limit)` or to somewhat higher than the maximum cache file size. For example to `1500M`. An alternative solution to this is to reduce the maximum cache file size to be somewhat lower than the imap service's vsz\_limit. See `[mail_cache_max_size](/main/core/summaries/settings.html#mail_cache_max_size)`. ## UNIX Socket Resource Temporarily Unavailable Commonly visible as: `imap-login: Error: net_connect_unix(imap) failed: Resource temporarily unavailable` This means that there are more imap-login processes trying to connect to the "imap" UNIX socket than there are imap processes accepting the connections. The kernel's connection listener queue got full and it started rejecting further connections. So what can be done about it? ### Wrong Service Settings This can happen if `service imap { client_limit }` is set to anything else than 1. IMAP (and POP3 and other mail) processes do disk IO, lock waiting and such, so if all the available imap processes are stuck waiting on something, they can't accept new connections and they queue up in the kernel. For mail processes only `client_limit=1` is recommended. It can also happen if `service imap { process_limit }` is reached. Dovecot logs a warning if process\_limit or client\_limit is reached. ### Out of File Descriptors If the "ulimit -n" is too low, kernel stops notifying the process about incoming connections. Make sure that the limit is at least as high as the client\_limit. Dovecot also internally checks this, and if it's too low it writes a warning to stderr at startup and to log. Note that Dovecot is not using "dovecot" user's or PAM's limits in general. Make sure the limits are correct with: `cat /proc/`pidof dovecot`/limits`. ### Master Process Busy Dovecot master process forks all of the new processes. If it's using 100% CPU, it doesn't have time to fork enough new processes. Even if it's not constantly using 100% CPU there may be fork bursts where it temporarily gets too busy. The solution is to make it do less work by forking less processes: - Most importantly switch to [high performance login](/main/core/config/login_processes.html#high-performance-mode). This alone might be enough. - You can also switch (most of the) other commonly forked processes to be reused. For example `[service_restart_request_count = 100](/main/core/summaries/settings.html#service_restart_request_count)` reuses the process for 100 different connections before it dies. This is useful especially for imap, pop3 and managesieve services. It's better to avoid using `[service_restart_request_count = unlimited](/main/core/summaries/settings.html#service_restart_request_count)` in case there are memory leaks. - You can pre-fork some idling processes to handle bursts with `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)`. See [services configuration](/main/core/config/service.html) before changing any service settings. Some services require specific values to work correctly. ### Listener Queue Size Dovecot uses `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` \* `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` as the listener queue size. There is no upper limit in Dovecot. Most OSes use an even lower limit, typically `128`. In Linux you can increase this through: `/proc/sys/net/core/somaxconn`. ## Change Group Operation Not Permitted `imap(user): Error: chown(/home/user/mail/.imap/INBOX, group=12(mail)) failed: Operation not permitted (egid=1000(user), group based on /var/mail/user - see https://doc.dovecot.org/admin_manual/errors/chgrp_no_perm/)` This means that Dovecot tried to copy `/var/mail/user` file's group (mail) to the index file directory it was creating (`/home/user/mail/.imap/INBOX`), but the process didn't belong to the mail group, so it failed. This is important for preserving access permissions with [shared mailboxes](/main/core/config/shared_mailboxes.html). Group copying is done only when it actually changes the access permissions; for example with 0600 or 0666 mode the group doesn't matter at all, but with 0660 or 0640 it does. To solve this problem you can do only one of two things: 1. If the group doesn't actually matter, change the permissions so that the group isn't copied (e.g. `chmod 0600 /var/mail/\*`, see [mbox](/main/core/config/mailbox_formats/mbox.html)). 2. Give the mail process access to the group (e.g., `[mail_access_groups = mail](/main/core/summaries/settings.html#mail_access_groups)`). However, this is dangerous. [It allows users with shell access to read other users' INBOXes](https://dovecot.org/list/dovecot-news/2008-March/000060.html). # Events Design Dovecot supports events, which improves both logging and statistics. TIP See Also: - [All Dovecot Events](/main/core/summaries/events.html#all-dovecot-events), - [Statistics](/main/core/config/statistics.html), - [Event Export](/main/core/config/events/export.html), and - [Event Filtering](/main/core/config/events/filter.html). Each logging call can be attached to a specific event, which can provide more metadata and context than just the log message string. This will eventually allow implementing things like machine-parsable (e.g. `JSON`) log lines containing key=value pairs, while still keeping the human readable text available. Each logging event can also be captured and sent to stats, even if it's not actually logged. Commonly statistics-related events are logged with debug level. Events have: - Categories, such as `storage`, `mailbox` or `auth`. - Fields, such as `user=foo@example.com` or `service=imap`. - Creation timestamp with microsecond precision. - Source code file and line number location when sending the event. - It may have an easy human-readable name. This is important for events that are expected to be used for statistics, so they can be easily referred to. - Forced debug-flag. Debug logging is enabled for this event regardless of the global debug log filters. A child event will inherit this flag. Events are hierarchical, so they can have parent events. The events always inherit all of their parents' categories and fields. A child event can replace a parent's field, and it can also remove a parent's field. Ideally most events would have a parent hierarchy that reaches the top event that was created for the current user/session. This allows statistics to track which events happened due to which users. In some cases this may not really be possible, such as an HTTP connection that is shared across multiple users in the same process. An event's lifetime is usually the same as the "object" it attaches to. For example an IMAP client connection should have a single event created at the beginning of the connection and destroyed at disconnection. The IMAP client connection event could be used for logging things like "Client connected" and "Client disconnected" and perhaps some other connection-specific events. However, most of the logging should be done by new events that have the IMAP client connection event as their parent. TIP There's an automatic "duration" statistics field that is calculated from the creation of the event to the (last) sending of the event, so for it to make sense the event lifetime and its logging also needs to make sense. So, for example, if the IMAP client connection event was used for logging many things throughout the session, the "duration" field would make little sense for most of those events. ## Categories The event categories are hierarchical. Example: `mail` category has parent `mailbox`, which has parent `storage`. If an event filter contains `category=storage`, it will match the `mail` and `mailbox` child categories as well. TIP A category isn't the same as a service/process name, but there is a `service:` category. So, for example, IMAP process has an `imap` category for its IMAP-related events, such as IMAP client connection and IMAP command related events. Because most events would be child events under these IMAP events, they would all inherit the `imap` category. So it would appear that using `category=imap` filter would match most of the logging from imap process. However, there would likely be some events that wouldn't have the IMAP client as their parent event, so these wouldn't match the imap category. ## Fields Each event can have any number of `key=value` fields. Parent event's fields are inherited by the child event. There are 4 types of fields: - strings - numbers (`intmax_t` = signed 64bit usually) - timestamp (`struct timeval`) - a list of strings The fields can be used for various purposes: - Filtering events with `field_name=value` matching - Counting fields in statistics (most commonly number fields) - They can include metadata that are internally used by the code. For example passing data from one plugin to others. - Later on these fields can be used by the logging system. - Field names should be consistent across the code. Besides making it easier for admins to configure the events, this allows statistics code to sum up fields from different unrelated events. - Example: if all the networking events include `ip`, `net_in_bytes`, and `net_out_bytes` fields, statistics can globally track how much network traffic Dovecot is doing from its own point of view, regardless of whether it's HTTP traffic or IMAP traffic or something else. # Dovecot HTTP Client HTTP requests within Dovecot are performed using its internal HTTP client (internally referred to as "lib-http"). ## Internal HTTP Response Codes Dovecot's lib-http uses custom HTTP response codes for some error conditions. In certain locations, e.g. [lib-lua: HTTP Functions](/main/core/admin/lua/lib_lua.html#http-functions), these codes may be visible to an admin or in logging, so these response codes must be handled in the same way that standard RFC codes are. | Number | Code | Description | | --- | --- | --- | | 9000 | ABORTED | The request was aborted. | | 9001 | INVALID\_URL | Failed to parse HTTP target url. | | 9002 | HOST\_LOOKUP\_FAILED | Failed to perform DNS lookup for the host. | | 9003 | CONNECT\_FAILED | Failed to setup any connection for the host and client settings allowed no more attempts. | | 9004 | INVALID\_REDIRECT | Service returned an invalid redirect response for this request. | | 9005 | CONNECTION\_LOST | The connection was lost unexpectedly while handling the request and client settings allowed no more attempts. | | 9006 | BROKEN\_PAYLOAD | The input stream passed to the request using http\_client\_request\_set\_payload() returned an error while sending the request. | | 9007 | BAD\_RESPONSE | The service returned a bad response. | | 9008 | TIMED\_OUT | The request timed out (either this was the last attempt or the absolute timeout was hit). | # Importing Mailboxes For importing mails, dovecot has the `[doveadm import](/main/core/summaries/doveadm.html#import)` command. WARNING Do not use this tool for migrating mails to another system. See [migrating mailboxes](/main/core/admin/migration.html) instead. The import command imports mails as is, and most importantly does not preserve UIDs or check if the mail is already there. Message flags are preserved. ## Importing Mails from Other Users To import mail from another user in the system, you can do sh ``` doveadm import -U sourceuser -u destuser 'maildir:~/Maildir' Imported ALL ``` This will import all mails and folder structure from sourceuser to destuser, under folder `Imported`. This will require that both sourceuser and destuser have same system UID. You can also use imap client to do the import, which lets you import mail from users with different system UID, or users that reside on a remote system. sh ``` doveadm import -U sourceuser -u destuser imapc: Imported ALL ``` This assumes you have configured imap client. See [migrating mailboxes over IMAP](/main/core/admin/migration.html#migrating-mailboxes-over-imap) for details. ## Importing Mails from Filesystem You can also import mails from a filesystem location: sh ``` doveadm import -u destuser maildir:/opt/backup/destuser/Maildir "" ALL ``` This will restore all mails from backup into mailbox root, with folder structure. The main difference to the previous example is that the `-U` parameter is not given. This causes the source location to be opened as `destuser`. Note that `destuser` must have read and privileges to the source location. If you have only read privileges, you can try using in-memory indexes: sh ``` doveadm import -u destuser -p mail_index_path=MEMORY maildir:/opt/backup/destuser/Maildir "" ALL ``` ## Merging Storages In some disaster recovery cases you may end up having mails for the same user in two different locations, and need to merge them. For example the storage goes down and fixing it takes a long time, so during the fixing you can let the users access their emails as an empty account, which can receive new mails. Later on you can use `[doveadm import](/main/core/summaries/doveadm.html#import)` to merge the mailboxes. Note that there is no way to make this solution perfect: - IMAP clients that have cached mails locally will delete their local caches and have to re-download mails later on. - POP3 clients that leave mails on server will notice all the mails are gone, and delete their local UIDL caches. When old mails come back, they're re-downloaded as new emails (duplicates). Also, there are 3 alternative ways of how mails can be imported into mailboxes: 1. Old recovered mails are imported on top of the newly received mails. The downside here is that mails may now be sorted in a weird order. If the IMAP client shows the mails in the saved order, the new received emails show up as oldest emails. Although this may not be an issue, since many IMAP clients sort the mails by either Date: header or the received timestamp (IMAP INTERNALDATE). 2. New mails are imported on top of the old recovered mails. This avoids the sorting problems, so it's likely the preferred method. The downside here is that the IMAP/POP3 clients will have to re-download also the newly delivered emails, as well as the old ones. Another issue with this is that some IMAP clients might not show the old recovered mails without manually rebuilding local caches, because the mails become inserted to the beginning of the folders, which isn't allowed by the IMAP protocol. - Another thing to keep mind here is that IMAP clients shouldn't see IMAP UIDs pointing to different emails before/after the merge. Otherwise their local cache could point to a different email, which could even cause the user to delete wrong messages. This shouldn't be an issue as long as new mail deliveries and all user access is disabled during the merging. The old mails have the old UIDs, and newly delivered mails would all have higher UIDs (because the UIDNEXT value is not shrunk during index rebuild that clears out the mailbox). 3. New recovered mails are imported under a separate `Recovered/` folder, i.e. there will be `Recovered/INBOX`, `Recovered/Sent`, etc. The user will need to manually merge the folders. The upside here is that POP3 clients won't re-download any mails as duplicates, but otherwise it's not much different from the 1st case. ### Example For the 2nd case ("New mails are imported on top of the old recovered mails") where mail storage broke down, but a separate index storage is ok, and index storage supports snapshots: - Snapshot the current index volume at the time of breakage. - Make sure `[mailbox_list_iter_from_index_dir](/main/core/summaries/settings.html#mailbox_list_iter_from_index_dir)` setting is enabled, so folder listing is done using the index volume rather than the mail volume. - Mount a new empty mail volume. - The first time IMAP/POP3 client attempts to access an existing mail, Dovecot rebuilds the indexes for the folder. This makes the folder look empty. The folder structure is preserved, as long as the `[mailbox_list_iter_from_index_dir](/main/core/summaries/settings.html#mailbox_list_iter_from_index_dir)` setting is used. - Once the original mail volume is recovered, first disable all user access and all new mail deliveries. - Create another snapshot of the index volume. - Mount the old mail volume to the original mountpoint. - Replace the index volume with the first created snapshot. Now the storage looks exactly like it was at the time of breakage. - Mount the new mail volume to some temporary mountpoint. - Mount the second index snapshot to some temporary mountpoint. - Use `[doveadm import](/main/core/summaries/doveadm.html#import)` to recover new mails: sh ``` doveadm import -u user@example.com \ -p mail_index_path=/mnt/temp-index-storage/user \ -p mail_control_path=/mnt/temp-index-storage/user \ -p mailbox_list_iter_from_index_dir \ sdbox:/mnt/temp-mail-storage/user "" all ``` If you have other [mail location settings](/main/core/config/mail_location.html#mail-location-setting), you may also want to specify them using some temporary locations. For example using `[mail_volatile_path](/main/core/summaries/settings.html#mail_volatile_path)`, `[mailbox_list_index_prefix](/main/core/summaries/settings.html#mailbox_list_index_prefix)`: sh ``` doveadm import -u user@example.com \ -p mail_volatile_path=/tmp/doveadm-import/user \ -p mailbox_list_index_prefix=/tmp/doveadm-import/user/dovecot.list.index # other settings ``` Delete the directories after finishing the import. # Dovecot Limits Dovecot contains various configurable and built-in resource limits designed to prevent denial of service situations. This page lists those limits. ⚠️ TODO This list is currently incomplete. ## Storage Size Limits ###### `quota_mail_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Quota: Maximum Saved Mail Size](/main/core/plugins/quota.html#maximum-saved-mail-size) The maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or `[doveadm save](/main/core/summaries/doveadm.html#save)`). If configured, will also be advertized with `APPENDLIMIT` extensions for IMAP clients, and `SIZE` extension for LMTP clients. ###### `quota_mailbox_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Quota: Maximum Mailbox Count](/main/core/plugins/quota.html#maximum-mailbox-count) **Changes:** - Added: 2.4.0 Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit. ###### `quota_mailbox_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of messages that can be created in a single mailbox. ###### `quota_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Maximum number of messages for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` to get the final value (in this mailbox or namespace). This is reported as the MESSAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ###### `quota_storage_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` - `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` - `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Quota storage size limit for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` and then increased by `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` to get the final value (in this mailbox or namespace). This is reported as the STORAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ### See Also - [Quota Root](/main/core/plugins/quota.html#quota-root) ## User Concurrency Limits ###### `lmtp_user_concurrency_limit` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Changed: 2.4.1 Changed from `unlimited` to `10`. Limit the number of concurrent deliveries to a single user to this maximum value. It is useful if one user is receiving numerous mail messages and thereby causing delays to other deliveries. To prevent limiting concurrent user deliveries set this to `unlimited`. ###### `mail_max_userip_connections` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of IMAP connections allowed for a user from each IP address. This setting is checked only by backends, not proxies. Note that for this to work, any username changes must be done already by passdb lookup (not by userdb lookup). Unique users are identified via case-sensitive comparison. ## Memory Limits - [service configuration (vsz\_limit)](/main/core/config/service.html#vsz-limit) - [service configuration (process\_limit)](/main/core/config/service.html#process-limit) - [service configuration (client\_limit)](/main/core/config/service.html#client-limit) ## Message Headers There is a `10 MB` limit for a single message header block, and a `50 MB` limit for all header blocks in a message. ## MIME Parts Maximum number of MIME parts per message is 10000. A maximum of 100 MIME parts can be nested in the same hierarchy path. # Dovecot Logging **Dovecot always logs a detailed error message** if something goes wrong. If it doesn't, it's considered a bug and will be fixed. However, almost always the problem is that **you're looking at the wrong log file**; error messages may be logged to a different file than informational messages. By default Dovecot uses the syslog service with mail facility. Dovecot can also be configured to log to files directly. ## Log File Location You can find the log file locations by running `[doveadm log find]()`. ## Last Errors You can easily print the last 1000 error messages of a running Dovecot: `[doveadm log errors]()`. ## Changing Log File Paths If you don't want to use syslog, you can make Dovecot log to files directly: doveconf ``` log_path = /var/log/dovecot.log # If not set, use the value from log_path info_log_path = /var/log/dovecot-info.log # If not set, use the value from info_log_path debug_log_path = /var/log/dovecot-debug.log ``` The warning and error messages go to the file specified by `[log_path](/main/core/summaries/settings.html#log_path)` while informative messages goes to `[info_log_path](/main/core/summaries/settings.html#info_log_path)` and debug messages goes to `[debug_log_path](/main/core/summaries/settings.html#debug_log_path)`. ## Syslog You can change Dovecot's syslog facility using `[syslog_facility](/main/core/summaries/settings.html#syslog_facility)`. The syslog configuration is often in `/etc/syslog.conf` or `/etc/rsyslog*` files. When using syslog, Dovecot uses 5 different logging levels: | Level | Description | | --- | --- | | `debug` | Debug-level message. | | `info` | Informational messages. | | `warning` | Warnings that don't cause an actual error, but are useful to know about. | | `err` | Non-fatal errors. | | `crit` | Fatal errors that cause the process to die. | Where exactly these messages are logged depends entirely on your syslog configuration. Often everything is logged to `/var/log/mail.log` or `/var/log/maillog`, and err and crit are logged to `/var/log/mail.err`. This is not necessarily true for your configuration though. In an ideal configuration the errors would be logged to a separate file than non-errors. For example you could set `[syslog_facility = local5](/main/core/summaries/settings.html#syslog_facility)` and set: ``` local5.* -/var/log/dovecot.log local5.warning;local5.error;local5.crit -/var/log/dovecot-errors.log ``` Here all the Dovecot messages get logged into `dovecot.log`, while all the important error/warning messages get logged into `dovecot-errors.log`. Sometimes syslog is configured to log all info level logging to `/var/log/messages`. You can disable such duplicates for mail by adding ";local5.none". For example: ``` *.info;local2.none;authpriv.none;cron.none;local5.none /var/log/messages ``` ### Syslog Rate Limiting rsyslog is configured with flood control enabled by default. Since Dovecot can log a lot in some situations, especially with debug logging enabled, this causes log messages to be lost. The rate limiting should be disabled in `/etc/rsyslog.conf`: ``` $SystemLogRateLimitInterval 0 ``` ## Rotating Logs You can use logrotate to maintain the Dovecot log files so they don't grow beyond a manageable size. Save the below scriptlet as `/etc/logrotate.d/dovecot`: ``` /var/log/dovecot.log { weekly rotate 4 missingok notifempty compress delaycompress sharedscripts postrotate doveadm log reopen endscript } ``` INFO doveadm is not working properly with SELinux (e.g. doveadm cannot read config file when called from logrotate context). SELinux safe postrotate alternative scriptlet: ``` postrotate kill -s 0 `cat /var/run/dovecot/master.pid` || kill -s USR1 `cat /var/run/dovecot/master.pid` endscript ``` ## Internal Errors If IMAP or POP3 processes encounter some error, they don't show the exact reason for clients. Instead they show: ``` Internal error occurred. Refer to server log for more information. [2006-01-07 22:35:11] ``` The point is that whenever anything unexpected happens, Dovecot doesn't leak any extra information about it to clients. They don't need it and they might try to exploit it in some ways, so the less they know the better. The real error message is written to the error log file. The timestamp is meant for you to help you find it. ## Logging Verbosity There are several settings that control logging verbosity. By default they're all disabled, but they may be useful for debugging. - `[auth_verbose = yes](/main/core/summaries/settings.html#auth_verbose)` enables logging all failed authentication attempts. - `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)` enables all authentication debug logging (also enables `[auth_verbose](/main/core/summaries/settings.html#auth_verbose)`). Passwords are logged as ``. - `[auth_debug_passwords = yes](/main/core/summaries/settings.html#auth_debug_passwords)` removes password hiding (but only if you are not using PAM, since PAM errors aren't written to Dovecot's own logs). - `[log_debug = category=mail](/main/core/summaries/settings.html#log_debug)` enables all kinds of mail related debug logging, such as showing where Dovecot is looking for mails. - `[verbose_ssl = category=ssl](/main/core/summaries/settings.html#verbose_ssl)` enables logging SSL errors and warnings. Even without this setting if connection is closed because of an SSL error, the error is logged as the disconnection reason. - `[auth_verbose_passwords = no|plain|sha1](/main/core/summaries/settings.html#auth_verbose_passwords)`: If authentication fails, this setting logs the used password. If you don't really need to know what the password itself was, but are more interested in knowing if the user is simply trying to use the wrong password every single time or if it's a brute force attack, you can set this to `sha1` and only the SHA1 of the password is logged. That's enough to know if the password is same or different between login attempts. - `[log_debug](/main/core/summaries/settings.html#log_debug)`:Flexible debug logging configuration. # Login Process Proxying Proxying using login processes is done for IMAP, POP3, Submission, and ManageSieve protocols. LMTP and doveadm protocols implement their own proxying, but they try to be mostly compatible. ## Proxying States If login fails, the error message shows the connection state when the disconnection or timeout happened. For example `timed out in state=login/banner` with IMAP means that connection timed out while waiting for LOGIN command reply. ### IMAP States IMAP states are a bit more complicated. They describe both what commands the proxy has sent towards the backend and also what was the last received reply from the backend. The proxy can also send multiple commands pipelined, which are listed in the state. For example the state could be `capability+login/banner` to mean that CAPABILITY and LOGIN commands have been sent, but only the IMAP banner reply has been received so far. The list of sending states (left side of `/`): | States | Description | | --- | --- | | `id` | ID command used to send session ID and original IMAP client IP/port. | | `starttls` | STARTTLS command. | | `capability` | CAPABILITY command. Only used if backend doesn't send it automatically. | | `login` | LOGIN command. | | `authenticate` | AUTHENTICATE command. Sent if LOGIN command couldn't be used, e.g., because using master user login or non-cleartext authentication mechanism. | | `auth-continue` | SASL continuation sent for AUTHENTICATE command. | The list of received states (right side of `/`): | States | Description | | --- | --- | | `none` | Nothing has been received so far. | | `banner` | IMAP banner received ("\* OK Dovecot ready"). | | `id` | ID command reply received. | | `starttls` | STARTTLS command reply received. | | `capability` | CAPABILITY command reply received. | | `auth-continue` | AUTHENTICATE command asked for continuation (`+` reply). | | `login` | LOGIN or AUTHENTICATE command reply received. | ### POP3 States POP3 states work a bit differently than IMAP states: | States | Description | | --- | --- | | `banner` | Nothing has been received so far (banner has NOT been received). | | `starttls` | STLS command has been sent. | | `xclient` | XCLIENT command has been sent. | | `login1` | USER command has been sent. | | `login2` | PASS or AUTH command has been sent. | ### Submission States Submission states are similar to POP3: | States | Description | | --- | --- | | `banner` | Nothing has been received so far (banner has NOT been received). | | `ehlo` | EHLO command has been sent. | | `starttls` | STLS command has been sent. | | `tls-ehlo` | EHLO command after STLS has been sent. | | `xclient` | XCLIENT command has been sent. | | `authenticate` | AUTH command has been sent. | ### ManageSieve States | States | Description | | --- | --- | | `none` | Nothing has been received so far. | | `tls-start` | STARTTLS command has been sent. | | `tls-ready` | STARTTLS command was accepted. TLS handshake was started. | | `xclient` | XCLIENT command has been sent. | | `auth` | AUTHENTICATE command has been sent. | # Dovecot Lua Support Dovecot supports Lua scripting in several configuration areas. See: - [Lua authentication database](/main/core/config/auth/databases/lua.html) - [push-notification plugin](/main/core/plugins/push_notification.html) Info Dovecot supports [Lua 5.3](https://www.lua.org/manual/5.3/) and [Lua 5.4](https://www.lua.org/manual/5.4/). ## Modules Dovecot contains two modules that support Lua scripting: - [lib-lua](/main/core/admin/lua/lib_lua.html) - [mail-lua](/main/core/admin/lua/mail_lua.html) # Lua: lib-lua Dovecot provides a lib-lua internal helper as part of libdovecot.so. It has facilities for loading scripts from various sources, and also helps with reusing scripts by keeping track of which scripts are loaded. Each script has it's own memory pool, which is guaranteed to be released when script is unloaded. DANGER Never use `os.exit()` from a Lua script. This will cause the whole process to exit instead of just the script. ## Initialization When script is loaded, `script_init()` function is called, if found. Changed: 2.4.0 `script_init()` return value is no longer checked. Use error() instead if necessary. ## De-initialization When script is being unloaded, `script_deinit()` function is called, if found. ## C API ### `struct event *dlua_check_event(lua_State *L, int arg)` Check the argument with index `arg` from stack and try to convert it to an Dovecot Event. Throws an error if the argument is not a valid Dovecot Event. ### `void dlua_dovecot_register(struct dlua_script *script)` Register dovecot variable. This item can also be extended by context specific tables, like authentication database adds `dovecot.auth`. ### `void dlua_push_event(lua_State *L, struct event *event)` Pushes an Dovecot Event to stack. ### `void dlua_push_timeval(lua_State *L, const struct timeval *tv)` Pushes an timeval struct to stack as integer. ## Lua API ### Base Functions #### `dovecot.i_debug()` **Signature:** `dovecot.i_debug(text)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `text` | _string_ | Message to log. | Log debug level message. #### `dovecot.i_info()` **Signature:** `dovecot.i_info(text)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `text` | _string_ | Message to log. | Log info level message. #### `dovecot.i_warning()` **Signature:** `dovecot.i_warning(text)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `text` | _string_ | Message to log. | Log warning level message. #### `dovecot.i_error()` **Signature:** `dovecot.i_error(text)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `text` | _string_ | Message to log. | Log error level message. #### `dovecot.event()` **Signature:** `dovecot.event(parent)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `parent` | _parent_ | Use as parent event. | Generate new event. If `parent` is not specified, the lua script is used as the parent. #### `dovecot.var_expand()` **Signature:** `dovecot.var_expand(input)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `input` | _string_ | String containing variables to expand. | Expand variables in the input string, [Settings Variables](/main/core/settings/variables.html). #### `dovecot.base64.encode()` **Signature:** `dovecot.base64.encode(input)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `input` | _string_ | Data to encode | Base64 encodes the provided input. #### `dovecot.base64.decode()` **Signature:** `dovecot.base64.decode(input)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `input` | _string_ | Data to decode | Base64 decodes the provided input. #### `dovecot.restrict_global_variables()` **Signature:** `dovecot.restrict_global_variables(toggle)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `toggle` | _boolean_ | Enable or disable defining new global variables. | Enable or disable restricting new global variables. If enabled, the rest of the script won't be allowed to declare global non-function variables but they can declare local variables and use already defined global variables. If a script needs to define a variable, they must declare them as local i.e. instead of `my_var = "some value"`, do `local my_var = "some value"`. Restrictions will remain in place until the end of the execution of the script or until they are lifted by calling `dovecot.restrict_global_variables(false)`. Default is permissive mode, i.e., same as lua's default, global variables are not restricted. ### HTTP Functions #### `dovecot.http.client()` **Signature:** `dovecot.http.client({ auto_redirect=Boolean, auto_retry=Boolean, connect_backoff_time=Time (milliseconds), connect_backoff_max_time=Time (milliseconds), connect_timeout=Time (milliseconds), event_parent=event, request_max_attempts=Unsigned Integer, max_auto_retry_delay=Time, max_connect_attempts=Unsigned Integer, max_idle_time=Time (milliseconds), request_max_redirects=Unsigned Integer, proxy_url=String, request_absolute_timeout=Time (milliseconds), request_timeout=Time (milliseconds), soft_connect_timeout=Time (milliseconds), ssl_cipher_list=String, ssl_cipher_suites=String, ssl_client_ca_dir=String, ssl_client_ca_file=File, ssl_client_cert_file=File, ssl_client_key_file=File, ssl_client_key_password=String, ssl_crypto_device=String, ssl_curve_list=String, ssl_client_require_valid_cert=Boolean, ssl_min_protocol=String, ssl_options=String, rawlog_dir=String, user_agent=String })` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `auto_redirect` | _Boolean_ | If this setting is `yes` redirects are handled as long as `[http_client_request_max_redirects](/main/core/summaries/settings.html#http_client_request_max_redirects)` isn't reached. If `no` the redirect responses are handled as regular failure responses. WARNING This setting should likely be changed only in the code, never in configuration. (Default: `yes`) | | `auto_retry` | _Boolean_ | If this setting is `no` requests are not automatically retried by the generic HTTP client code. It's still possible to retry the requests with explicit `http_client_request_try_retry()` calls as long as `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` isn't reached. WARNING This setting should likely be changed only in the code, never in configuration. (Default: `yes`) | | `connect_backoff_time` | _Time (milliseconds)_ | Initial backoff time for retries. It's doubled at each connection failure. (Default: `100 ms`) | | `connect_backoff_max_time` | _Time (milliseconds)_ | Maximum backoff time for retries. (Default: `1 min`) | | `connect_timeout` | _Time (milliseconds)_ | Max time to wait for TCP connect and SSL handshake to finish before retrying. `0` = use `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)`. | | `event_parent` | _event_ | Parent event to use. | | `request_max_attempts` | _Unsigned Integer_ | Maximum number of attempts for a request. (Default: `1`) | | `max_auto_retry_delay` | _Time_ | Maximum acceptable delay for automatically retrying/redirecting requests. If a server sends a response with a `Retry-After` header that causes a delay longer than this, the request is not automatically retried and the response is returned. | | `max_connect_attempts` | _Unsigned Integer_ | Maximum number of connection attempts to a host before all associated requests fail. If non-zero, the maximum will be enforced across all IPs for that host, meaning that IPs may be tried more than once eventually if the number of IPs is smaller than the specified maximum attempts. If the number of IPs is higher than the maximum attempts not all IPs are tried. If `0`, all IPs are tried at most once. | | `max_idle_time` | _Time (milliseconds)_ | Maximum time a connection will idle. If parallel connections are idle, the duplicates will end earlier based on how many idle connections exist to that same service. | | `request_max_redirects` | _Unsigned Integer_ | Maximum number of redirects for a request. `0` = redirects refused. | | `proxy_url` | _String_ | URL for HTTP proxy. Ignored if `[http_client_proxy_socket_path](/main/core/summaries/settings.html#http_client_proxy_socket_path)` is set. | | `request_absolute_timeout` | _Time (milliseconds)_ | Max total time to wait for HTTP request to finish, including all retries. `0` means no limit. | | `request_timeout` | _Time (milliseconds)_ | Max time to wait for HTTP requests to finish before retrying. (Default: `1 min`) | | `soft_connect_timeout` | _Time (milliseconds)_ | Time to wait for TCP connect and SSL handshake to finish for the first connection before trying the next IP in parallel. `0` = wait until current connection attempt finishes. | | `ssl_cipher_list` | _String_ | The list of SSL ciphers to use for TLSv1.2 and below connections, in order of preference. Use `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` for TLSv1.3 connections. You do not need to edit this setting in order to disable specific SSL protocols; that is best done with `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` instead. This setting is used for both incoming and outgoing SSL connections. (Default: `ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH (for ssl_server, empty for ssl_client)`) | | `ssl_cipher_suites` | _String_ | The list of SSL cipher suites to use for TLSv1.3 connections, in order of preference. Use `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` for TLSv1.2 and below connections. This setting is used for both incoming and outgoing SSL connections. See: [https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites](https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites) (Default: `\`) | | `ssl_client_ca_dir` | _String_ | The directory where trusted SSL CA certificates can be found. For example `/etc/ssl/certs`. These certificates are used only for outgoing SSL connections (e.g. with the imapc driver). For extra security you might want to point to a directory containing certificates only for the CAs that are actually needed for the server operation instead of all the root CAs. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. | | `ssl_client_ca_file` | _File_ | File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. | | `ssl_client_cert_file` | _File_ | Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` | | `ssl_client_key_file` | _File_ | Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` | | `ssl_client_key_password` | _String_ | Password for the `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)`. | | `ssl_crypto_device` | _String_ | Available Values: Which SSL crypto device to use. | | `ssl_curve_list` | _String_ | Colon separated list of elliptic curves to use, in order of preference. An empty value uses the defaults from the SSL library. This setting is used for both incoming and outgoing SSL connections. Example: ``` ssl_curve_list = P-521:P-384:P-256 ``` (Default: `\`) | | `ssl_client_require_valid_cert` | _Boolean_ | Require a valid certificate when connecting to external SSL services? (Default: `yes`) | | `ssl_min_protocol` | _String_ | The minimum SSL protocol version Dovecot accepts. It cannot be empty. This setting is used for both incoming and outgoing SSL connections. Supported values are: `ANY` WARNING This value is meant for tests only. It should not be used in any deployment of any value/relevance. `TLSv1` Support TLSv1+. (TLSv1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.1` Support TLSv1.1+. (TLSv1.1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.2` Support TLSv1.2+. `TLSv1.3` Support TLSv1.3+. `LATEST` Support only the latest version available. (Default: `TLSv1.2`) | | `ssl_options` | _String_ | Additional options for SSL. This setting is used for both incoming and outgoing SSL connections. Currently supported options are: `compression` Enable compression. `no_ticket` Disable SSL session tickets. | | `rawlog_dir` | _String_ | Directory for writing raw log data for debugging purposes. WARNING Must be writable by the process creating this log. | | `user_agent` | _String_ | `User-Agent:` header to send. | **Returns:** An http\_client object. Create a new http client object that can be used to submit requests to remote servers. #### Object `http_client` ##### `http_client.request()` **Signature:** `http_client.request({ url=string, method=string })` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `url` | _string_ | Full url address. Parameters will be parsed from the string. TLS encryption is implied with use of `https`. | | `method` | _string_ | HTTP method to use. | **Returns:** An http\_request object. Create a new request object. By default, the request has `Host`, and `Date` headers with relevant values, as well as `Connection: Keep-Alive`. #### Object `http_request` ##### `http_request.add_header()` **Signature:** `http_request.add_header(name, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Name of the HTTP header. | | `value` | _string_ | Value of the header. | Add a header to the request. ##### `http_request.remove_header()` **Signature:** `http_request.remove_header(name)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Name of the HTTP header. | Do a lookup of the header in the request and remove it if found. ##### `http_request.set_payload()` **Signature:** `http_request.set_payload(value, synchronous)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `value` | _string_ | Payload of the request as string data. | | `synchronous` | _boolean_ | Expect 100 Continue header before sending data. Defaults to `false`. | Set payload data to the request. Optionally you can set `synchronous`, which will cause "100 Continue" header to be sent. ##### `http_request.set_event()` **Signature:** `http_request.set_event(value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `value` | _event_ | Event to be used as parent event. | Pass the parent event to be used for this request. ##### `http_request.get_event()` **Signature:** `http_request.get_event()` **Returns:** An event object Returns the event used by the http\_request. ##### `http_request.submit()` **Signature:** `http_request.submit()` **Returns:** An http\_response object. Connect to the remote server and submit the request. This function blocks until the HTTP response is fully received. #### Object `http_response` ##### `http_response.status()` **Signature:** `http_response.status()` **Returns:** Status code of the http response. Get the status code of the HTTP response. The codes contain error codes as well as HTTP codes, e.g., '200 HTTP\_OK' and error code that denote connection to remote server failed. A human-readable string of the error can then be read using `reason()` function. Besides the status codes sent by remote HTTP servers, the status code can also contain Dovecot's internal 9xxx errors. See [Dovecot HTTP Internal Response Codes](/main/core/admin/http.html#internal-http-response-codes). ##### `http_response.reason()` **Signature:** `http_response.reason()` **Returns:** String representation of the status. Returns a human-readable string of HTTP status codes, e.g. "OK", "Bad Request", "Service Unavailable", as well as connection errors, e.g., "connect(...) failed: Connection refused". ##### `http_response.header()` **Signature:** `http_response.header(name)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Header to retrieve. | **Returns:** Value of the HTTP response header. Get value of a header in the HTTP request. If header is not found from the response, an empty string is returned. ##### `http_response.payload()` **Signature:** `http_response.payload()` **Returns:** Payload of the HTTP response as a string. Get the payload of the HTTP response. #### Example HTTP Client Code lua ``` local json = require "json" local http_client = dovecot.http.client { timeout = 10000; max_attempts = 3; debug = true; } function auth_password_verify(request, password) local auth_request = http_client:request { url = "https://endpoint/"; method = "POST"; } local req = {user=request.user, password=password} auth_request:set_payload(json.encode(req)) local auth_response = auth_request:submit() local resp_status = auth_response:status() if resp_status == 200 then return dovecot.auth.PASSDB_RESULT_OK, "" else return dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH, "" end end ``` ### Object `event` #### `event.append_log_prefix()` **Signature:** `event.append_log_prefix(prefix)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `prefix` | _string_ | Prefix to append. | Set prefix to append into log messages. #### `event.replace_log_prefix()` **Signature:** `event.replace_log_prefix(prefix)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `prefix` | _string_ | Prefix to append. | Append prefix for log messages. #### `event.set_always_log_source()` Add source path:line to the log messages. #### `event.set_forced_debug()` Added: 2.4.1 Enable forced debugging for the event and its child events. #### `event.unset_forced_debug()` Added: 2.4.1 Disable previously enabled forced debugging. #### `event.set_name()` **Signature:** `event.set_name(name)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Event name. | Set name for event. #### `event.add_str()` **Signature:** `event.add_str(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _string_ | A value. | Add a key-value pair to event. #### `event.add_int()` **Signature:** `event.add_int(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _int_ | Integer value. | Add a key-value pair to event. #### `event.add_timeval()` **Signature:** `event.add_timeval(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _int_ | Unix timestamp. | Add a key-value pair to event. #### `event.inc_int()` **Signature:** `event.inc_int(key, diff)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `diff` | _int_ | Difference to add. Can be negative. | Increment key-value pair. #### `event.log_debug()` **Signature:** `event.log_debug(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit debug message. #### `event.log_info()` **Signature:** `event.log_info(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit info message. #### `event.log_warning()` **Signature:** `event.log_warning(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit warning message. #### `event.log_error()` **Signature:** `event.log_error(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit error message. #### `event.passthrough_event()` Returns an passthrough event. A log message _must be_ logged or else a panic will occur. ### Object `event_passthrough` #### `event_passthrough.append_log_prefix()` **Signature:** `event_passthrough.append_log_prefix(prefix)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `prefix` | _string_ | Prefix to append. | Set prefix to append into log messages. #### `event_passthrough.replace_log_prefix()` **Signature:** `event_passthrough.replace_log_prefix(prefix)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `prefix` | _string_ | Prefix to append. | Append prefix for log messages. #### `event_passthrough.set_always_log_source()` Add source path:line to the log messages. #### `event_passthrough.set_name()` **Signature:** `event_passthrough.set_name(name)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Event name. | Set name for event. #### `event_passthrough.add_str()` **Signature:** `event_passthrough.add_str(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _string_ | A value. | Add a key-value pair to event. #### `event_passthrough.add_int()` **Signature:** `event_passthrough.add_int(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _int_ | Integer value. | Add a key-value pair to event. #### `event_passthrough.add_timeval()` **Signature:** `event_passthrough.add_timeval(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `value` | _int_ | Unix timestamp. | Add a key-value pair to event. #### `event_passthrough.inc_int()` **Signature:** `event_passthrough.inc_int(key, diff)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key name. | | `diff` | _int_ | Difference to add. Can be negative. | Increment key-value pair. #### `event_passthrough.log_debug()` **Signature:** `event_passthrough.log_debug(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit debug message. #### `event_passthrough.log_info()` **Signature:** `event_passthrough.log_info(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit info message. #### `event_passthrough.log_warning()` **Signature:** `event_passthrough.log_warning(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit warning message. #### `event_passthrough.log_error()` **Signature:** `event_passthrough.log_error(message)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `message` | _string_ | Message to log. | Emit error message. ### Object `dict` TIP Currently, this object cannot be created within the Lua code itself. #### `dict.lookup()` **Signature:** `dict.lookup(key[, username])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key to lookup. | | `username` | _string_ | Username for private dict keys. | Lookup key from dict. If key is found, returns a table with values. If key is not found, returns `nil`. #### `dict.iterate()` **Signature:** `dict.iterate(path, flags[, username])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `path` | _string_ | Path prefix to iterate. | | `flags` | _int_ | Iteration flags. Currently raw numbers must be used for these. See `enum dict_iterate_flags` in the C code. | | `username` | _string_ | Username for private dict paths. | Returns an iteration step function and dict iter userdata. For example: lua ``` for key, values in dict:iterate(key_prefix, 0) do dovecot.i_debug('key='..key..', first value='..values[1]) end ``` #### `dict.transaction_begin()` **Signature:** `dict.transaction_begin([username])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `username` | _string_ | Username for private dict keys. | Returns a new transaction object. #### Object `dict.transaction` ##### `dict.transaction.set()` **Signature:** `dict.transaction.set(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key to set. | | `value` | _string_ | Value to set. | Set key=value in the dict transaction. ##### `dict.transaction.unset()` **Signature:** `dict.transaction.unset(key)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Key to unset. | Unset key in the dict transaction. ##### `dict.transaction.set_timestamp()` **Signature:** `dict.transaction.set_timestamp({ seconds=int, nanoseconds=int })` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `seconds` | _int_ | UNIX timestamp. | | `nanoseconds` | _int_ | Nanoseconds part of the timestamp. | Set timestamp to the dict transaction. This is currently used only with Cassandra. ##### `dict.transaction.commit()` Commit the transaction. ##### `dict.transaction.rollback()` Rollback the transaction. ### Object `dns_client` Added: 2.4.0 TIP Currently, this object cannot be created within the Lua code itself. #### `dns_client.lookup()` **Signature:** `dns_client.lookup(hostname[, event])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `hostname` | _string_ | Hostname to lookup. | | `event` | _event_ | Event to use for logging. | **Returns:** On successful DNS lookup, returns a table with IP addresses (which has at least one IP). On failure, returns nil, error string, net\_gethosterror() compatible error code (similar to e.g. Lua io.\* calls). Lookup hostname asynchronously via dns-client process. # Lua: mail-lua mail-lua is a plugin that can be loaded to provide API for mail storage Lua plugins. mail-lua provides a common script to be used in mail storage instead of per-plugin scripts. See [mail-lua plugin](/main/core/plugins/mail_lua.html). ## Initialization When mail user is created, a script is loaded if present as `mail_lua_script()` and `mail_user_created()` is called if present in script. ## De-initialization On deinitialization, `mail_user_deinit_pre()` is called first, if present, followed by `mail_user_deinit()`. ## C API ### `void dlua_register_mail_storage(struct dlua_script *script)` Parameters: - `script`: `dlua_script` to add mail storage. Register storage Lua interface to script context. ### `bool mail_lua_plugin_get_script(struct mail_user *user, struct dlua_script **script_r)` Parameters: - `user`: `mail_user` - `script`: `dlua_script` Returns script context if available. If FALSE is returned, no Lua script has been loaded, and you should optionally deal this yourself. ### `void dlua_push_mail_user(struct dlua_script *script, struct mail_user *user)` Parameters: - `script`: `dlua_script` - `user`: `mail_user` Pushes a mail user on top of stack. ### `void dlua_push_mailbox(struct dlua_script *script, struct mailbox *box)` Parameters: - `script`: `dlua_script` - `mailbox`: `mailbox` Pushes a mailbox on top of stack. ### `void dlua_push_mail(struct dlua_script *script, struct mail* mail)` Parameters: - `script`: `dlua_script` - `mail`: `mail` Pushes a mail on top of stack. ## Lua API ### Object `dovecot.storage` #### enum `dovecot.storage.STATUS_MESSAGES` #### enum `dovecot.storage.STATUS_RECENT` #### enum `dovecot.storage.STATUS_UIDNEXT` #### enum `dovecot.storage.STATUS_UIDVALIDITY` #### enum `dovecot.storage.STATUS_UNSEEN` #### enum `dovecot.storage.STATUS_FIRST_UNSEEN_SEQ` #### enum `dovecot.storage.STATUS_KEYWORDS` #### enum `dovecot.storage.STATUS_HIGHESTMODSEQ` #### enum `dovecot.storage.STATUS_PERMANENT_FLAGS` #### enum `dovecot.storage.FIRST_RECENT_UID` #### enum `dovecot.storage.STATUS_HIGHESTPVTMODSEQ` #### enum `dovecot.storage.MAILBOX_FLAG_READONLY` #### enum `dovecot.storage.MAILBOX_FLAG_SAVEONLY` #### enum `dovecot.storage.MAILBOX_FLAG_DROP_RECENT` #### enum `dovecot.storage.MAILBOX_FLAG_NO_INDEX_FILES` #### enum `dovecot.storage.MAILBOX_FLAG_KEEP_LOCKED` #### enum `dovecot.storage.MAILBOX_FLAG_IGNORE_ACLS` #### enum `dovecot.storage.MAILBOX_FLAG_AUTO_CREATE` #### enum `dovecot.storage.MAILBOX_FLAG_AUTO_SUBSCRIBE` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_FULL_READ` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_FULL_WRITE` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_FAST` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_NO_EXPUNGES` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_FIX_INCONSISTENT` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_EXPUNGE` #### enum `dovecot.storage.MAILBOX_SYNC_FLAG_FORGE_RESYNC` #### enum `dovecot.storage.MAILBOX_ATTRIBUTE_PREFIX_DOVECOT` String constant `vendor/vendor.dovecot/`. #### enum `dovecot.storage.MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT` String constant `vendor/vendor.dovecot/pvt/`. #### enum `dovecot.storage.MAILBOX_ATTRIBUTE_PREFIX_DOVECOT_PVT_SERVER` String constant `vendor/vendor.dovecot/pvt/server/`. ### Object `mail_user` - Has `tostring()` - Is comparable (by username) #### Functions ##### `mail_user.plugin_getenv()` **Signature:** `mail_user.plugin_getenv(key)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Setting name. | Returns key from user plugin settings or userdb environment. ##### `mail_user.var_expand()` **Signature:** `mail_user.var_expand(template)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `template` | _string_ | Variable template string. | Expands mail user variables (see `[Settings variables](/main/core/settings/variables.html)`). ##### `mail_user.mailbox()` **Signature:** `mail_user.mailbox(name[, flags])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `name` | _string_ | Mailbox name. | | `flags` | _int_ | Flags, see [`dovecot.storage`](#dovecot-storage). | Allocates a mailbox. ##### `mail_user.metadata_get()` **Signature:** `mail_user.metadata_get(key)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata key, must begin with `/private/` or `/shared/`. | Returns given metadata key for the user. ##### `mail_user.metadata_set()` **Signature:** `mail_user.metadata_set(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata key, must begin with `/private/` or `/shared/`. | | `value` | _string_ | Value to set, `nil` unsets value. | Sets user metadata key to value. ##### `mail_user.metadata_unset()` **Signature:** `mail_user.metadata_unset(key)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata key, must begin with `/private/` or `/shared/`. | Unsets value, same as calling `mailbox.metadata_set()` with value = `nil`. ##### `mail_user.metadata_list()` **Signature:** `mail_user.metadata_list(key[, key, ...])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata prefix, must begin with `/private/` or `/shared/`. | Lists all keys for the mailbox metadata under prefix. #### Variables ##### `mail_user.home` Home directory (if available). ##### `mail_user.username` User's name. ##### `mail_user.uid` System UID. ##### `mail_user.gid` System GID. ##### `mail_user.service` Service type (IMAP/POP3/LMTP/LDA/...). ##### `mail_user.session_id` Current session ID. ##### `mail_user.session_create_time` When session was created. ##### `mail_user.nonexistent` Does user exist? ##### `mail_user.anonymous` Is user anonymous? ##### `mail_user.autocreated` Was user automatically created internally for some operation? ##### `mail_user.mail_debug` Is debugging turned on? ##### `mail_user.fuzzy_search` Does fuzzy search work for this user? ##### `mail_user.dsyncing` Is user being dsynced? ### Object `mailbox` - Has `tostring()` - Is comparable (by full mailbox name) #### Functions ##### `mailbox.open()` Opens the mailbox. ##### `mailbox.close()` Closes the mailbox. ##### `mailbox.free()` Releases mailbox (must be done). ##### `mailbox.sync()` **Signature:** `mailbox.sync([flags])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `flags` | _int_ | Flags, see [`dovecot.storage`](#dovecot-storage). | Synchronizes the mailbox (should usually be done). ##### `mailbox.status()` **Signature:** `mailbox.status(item[, item, ...])` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `item` | _string_ | Item name. | **Returns:** mailbox\_status table Returns requested mailbox status items as table. ##### `mailbox.metadata_get()` **Signature:** `mailbox.metadata_get(key)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata key, must begin with `/private/` or `/shared/`. | Returns given metadata key for the mailbox. ##### `mailbox.metadata_set()` **Signature:** `mailbox.metadata_set(key, value)` **Arguments:** | Name | Type | Description | | --- | --- | --- | | `key` | _string_ | Metadata key, must begin with `/private/` or `/shared/`. | | `value` | _string_ | Value to set, `nil` unsets value. | Sets mailbox metadata key to value. #### Variables ##### `mailbox.attribute` Full mailbox name. ##### `mailbox.name` Mailbox name. ##### `mailbox.attribute` Full mailbox name. ##### `mailbox.name` Mailbox name. ### Table `mailbox_status` #### Variables ##### `mailbox_status.mailbox` Full name of mailbox. ##### `mailbox_status.messages` Number of messages. ##### `mailbox_status.recent` Number of \\Recent messages. ##### `mailbox_status.unseen` Number of \\Unseen messages. ##### `mailbox_status.uidvalidity` Current UID validity. ##### `mailbox_status.uidnext` Next UID. ##### `mailbox_status.first_unseen_seq` First sequence number of unseen mail. ##### `mailbox_status.first_recent_uid` First UID of unseen mail. ##### `mailbox_status.highest_modseq` Highest modification sequence. ##### `mailbox_status.highest_pvt_modseq` Highest private modification sequence. ##### `mailbox_status.permanent_flags` Supported permanent flags as a bitmask. ##### `mailbox_status.flags` Supported flags as a bitmask. ##### `mailbox_status.permanent_keywords` Are permanent keywords supported? ##### `mailbox_status.allow_new_keywords` Can new keywords be added? ##### `mailbox_status.nonpermanent_modseqs` Are modification sequences temporary? ##### `mailbox_status.no_modseq_tracking` Are modification sequences being tracked? ##### `mailbox_status.have_guids` Do GUIDs exist? ##### `mailbox_status.have_save_guids` Can GUIDs be saved? ##### `mailbox_status.have_only_guid128` Are GUIDs only 128 bits? ##### `mailbox_status.keywords` Table of current keywords. ### Object `mail` - Has `tostring()` - Is comparable (within same mailbox, by UID) #### Functions _None yet._ #### Variables ##### `mail.mailbox` Mailbox object. ##### `mail.seq` Sequence number (can change). ##### `mail.uid` UID (immutable). # Migrating Mailboxes WARNING Badly done migration will cause your IMAP and/or POP3 clients to re-download all mails! Read this page carefully! This guide assumes that the target host has a v2.3 or newer version of Dovecot. You should use migration when you are changing Dovecot storage configuration, such as compression, encryption or mail location driver; or want to restructure your mails in some way. Things that you should consider in your config: - [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) - IMAP client settings (when needed) - [Namespace definitions including public and shared namespaces](/main/core/config/namespaces.html) - [ACL settings](/main/core/plugins/acl.html) - [quota plugin](/main/core/plugins/quota.html): Quota without rules (to make sure quota gets calculated, but not enforced) - [mail cache configuration](/main/core/config/mail_cache.html) - `[mail_attachment_detection_options](/main/core/summaries/settings.html#mail_attachment_detection_options)` - `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` - [mail-compress plugin](/main/core/plugins/mail_compress.html) and [mail-crypt plugin](/main/core/plugins/mail_crypt.html) settings - [NFS](/main/core/config/nfs.html) related settings Sometimes, if your production configuration file has complex authentication flows, push notifications, or other settings that might cause unwanted effects during migration, you can make a separate migration configuration file. To use this configuration file, you can store it as, for example, `/etc/dovecot/dovecot-migration.conf`, and use it with `doveadm -c /etc/dovecot/dovecot-migration.conf sync`. Other settings to use: - `[dsync_features = empty-header-workaround](/main/core/summaries/settings.html#dsync_features)` - This is sometimes needed with some broken servers. - `[mail_prefetch_count = 20](/main/core/summaries/settings.html#mail_prefetch_count)` - Read multiple mails in parallel; improves performance. - `[dsync_commit_msgs_interval = 100](/main/core/summaries/settings.html#dsync_commit_msgs_interval)` - If you have very large mailboxes, you might want to tune this - `[dsync_hashed_headers = Date Message-ID](/main/core/summaries/settings.html#dsync_hashed_headers)` - If you need to hash other headers for whatever reason, you can change this - `[mail_fsync = never](/main/core/summaries/settings.html#mail_fsync)` - Avoid useless I/O. ## Testing It is a very good idea to do some test migrations first using test accounts to ensure everything works. When migrating mails from another server, you should make sure that these are preserved: 1. Message flags - Lost flags can be really annoying, you most likely want to avoid it. 2. Message UIDs and UIDVALIDITY value - If UIDs are lost, at a minimum clients' message cache gets cleaned and messages are re-downloaded as new. - Some IMAP clients store metadata by assigning it to specific UID, if UIDs are changed these will be lost. 3. Mailbox subscription list ## Migrating Mailboxes from Another Dovecot Server ### Preparations If the old system is running v2.1.14+ , you can use doveadm protocol to migrate your mails. If the old system is older, see [migrating messages over IMAP](#migrating-messages-over-imap). First, setup doveadm service on old server: doveconf ``` service doveadm { inet_listener doveadm { port = 12354 } } doveadm_password = supersecret ``` Then set doveadm password on new server: doveconf ``` doveadm_password = supersecret ``` Now you are ready to migrate. ### Executing Migration If you want to avoid any changes to your source system, unidirectional synchronization is recommended. To migrate users, use: `[doveadm backup -Ru username tcp:host:port](/main/core/summaries/doveadm.html#backup)`. If you are experiencing problems, run: sh ``` doveadm -D backup -Ru username tcp:host:port ``` This will enable debug logging. The doveadm backup command forces the destination to look exactly like the source, deleting mails and mailboxes if necessary. If it's possible that the destination already has new mails (or other changes), use `[doveadm sync -1](/main/core/summaries/doveadm.html#sync)` instead: sh ``` doveadm -o imapc_password=bar sync -1Ru user imapc: ``` You can run the command again to perform incremental updates. Note that Public and Shared namespaces are synchronized automatically (see caveats). #### Caveats - Migrating shared namespaces is difficult prior to v2.3.15+. To migrate Shared namespaces, you need to use `-n Shared -n Shared/Mailbox`. - If you are using ACLs, some ACL types can cause problems. Especially if you have ACLs that only permit writing to folder, but not reading it. This applies to shared folders only. - Migration can cause unexpectedly high loads on the source system. Beware! There is no way to throttle the synchronization. - Avoid accessing the target mailboxes before the first sync. At best this causes warnings about GUID or UIDVALIDITY changes, and at worst it fails the sync entirely. ## Migrating mailboxes over IMAP When migrating mails over IMAP, you need to have valid credentials to the source system. You can either use master password, master user, or individual user authentication. This depends entirely on the source system. See [Master Users](/main/core/config/auth/master_users.html) for more details. ### Preparations Configure authentication on the source system to match your preference. Ensure both source and target system agree on usernames. Configure IMAP client on the target system. Changed: 2.4.0 All IMAPC features are auto-enabled by default. Please refer to `[imapc_features](/main/core/summaries/settings.html#imapc_features)` for description on individual flags on how to turn these off when necessary. - `[imapc_list_prefix = INBOX](/main/core/summaries/settings.html#imapc_list_prefix)` - If the old IMAP server uses `INBOX.` namespace prefix, set this. - `[imapc_host = remotehost](/main/core/summaries/settings.html#imapc_host)` - Remote hostname. - `[imapc_ssl = imaps](/main/core/summaries/settings.html#imapc_ssl)` and `[imapc_port = 993](/main/core/summaries/settings.html#imapc_port)` - Set, if using TLS. - `[imapc_ssl = starttls](/main/core/summaries/settings.html#imapc_ssl)` and `[imapc_port = 143](/main/core/summaries/settings.html#imapc_port)` - Set, if using STARTTLS. - `[ssl_client_ca_dir = /etc/ssl/certs](/main/core/summaries/settings.html#ssl_client_ca_dir)` or `[ssl_client_ca_file = /etc/ssl/ca-certificates.pem](/main/core/summaries/settings.html#ssl_client_ca_file)` - These default to system. Master password auth: doveconf ``` imapc_user = %{user} imapc_password = supersecret ``` Master user auth: doveconf ``` imapc_user = %{user} imapc_master_user = master-user imapc_password = master-password ``` Individual password auth: doveconf ``` imapc_user = %{user} # doveadm -o imapc_password=password backup -Ru user imapc: ``` You can verify that the settings are done correctly with: sh ``` doveadm -o imapc_password=password -o mail_driver=imapc -o mail_path= mailbox list -u user ``` ### POP3 notes If you need to retain POP3 support on your new system, you should use [pop3-migration plugin](/main/core/plugins/pop3_migration.html). doveconf ``` pop3c_host = hostname # Authenticate as masteruser / masteruser-secret, but use a separate login # user. # If you don't have a master user, remove the pop3c_master_user setting. pop3c_user = %{user} pop3c_master_user = masteruser pop3c_password = masteruser-secret # if you are using TLS pop3c_ssl = pop3s pop3c_port = 995 # if you are using StartTLS pop3c_ssl = starttls pop3c_port = 110 namespace pop3-migration { prefix = POP3-MIGRATION-NS/ mail_driver = pop3c mail_path = list = no hidden = yes } protocol doveadm { mail_plugins { pop3_migration = yes } } pop3_migration_mailbox = POP3-MIGRATION-NS/INBOX ``` You can alternatively configure compatible UIDL format in Dovecot. See `[pop3_uidl_format](/main/core/summaries/settings.html#pop3_uidl_format)`. ### Executing Migration To migrate users, use: sh ``` doveadm -o imapc_password=bar -o pop3c_password=bar backup -Ru user imapc: ``` If you are experiencing problems, enable debugging with the `-D` parameter: sh ``` doveadm -D -o imapc_password=bar -o pop3c_password=bar backup -Ru username imapc: ``` The doveadm backup command forces the destination to look exactly like the source, deleting mails and mailboxes if necessary. If it's possible that the destination already has new mails (or other changes), use `[doveadm sync -1](/main/core/summaries/doveadm.html#sync)` instead: sh ``` doveadm -o imapc_password=bar -o pop3c_password=bar sync -1Ru user imapc: ``` Note that Public and Shared namespaces are synchronized automatically (see caveats). See [doveadm error codes](/main/core/admin/doveadm.html#error-exit-codes) for details on how to handle errors. #### Caveats - You cannot migrate **to** imap, only from. #### POP3 Caveats - POP3 message order (when it's different from IMAP message order) is not preserved with [mbox](/main/core/config/mailbox_formats/mbox.html). - If source POP3 server merges multiple IMAP mailboxes into one POP3 INBOX, the migration won't be transparent. - If source IMAP and POP3 servers return messages somehow differently, [pop3-migration plugin](/main/core/plugins/pop3_migration.html) might not be able to match the messages - Don't trust the migration tools blindly. Verify manually that the UIDLs are correct before exposing real clients to Dovecot. You can do this by logging in using your old POP3 server, issuing UIDL command and saving the output. Then log in using Dovecot and save its UIDL output as well. Use e.g. `diff` command to verify that the lists are identical. Note that: - If a client already saw changed UIDLs and decided to start re-downloading mails, it's unlikely there is anything you can do to stop it. Even going back to your old server is unlikely to help at that point. - Some (many?) POP3 clients also require that the message ordering is preserved. - Some clients re-download all mails if you change the hostname in the client configuration. Be aware of this when testing. ## Migration from Gmail to Dovecot You can use dsync migration via IMAP protocol, but there are a few things different with Gmail compared to other IMAP servers. With Gmail, when you delete a mail from POP3, the mail is only hidden from future POP3 sessions, but it's still available via IMAP. If you wish to preserve this functionality, there's a `[pop3_deleted_flag](/main/core/summaries/settings.html#pop3_deleted_flag)` setting. Gmail has labels. If a message has multiple labels, it shows up in multiple IMAP folders, but it's still the same message and uses quota only once for that message. Dovecot currently doesn't have label support, so the migration will copy the message to multiple folders and each instance will use up quota. There's currently no easy fix for this. Even though the quota is duplicated, it doesn't mean that the storage usage has to be duplicated. Use the doveadm sync's `-a` parameter to attempt to copy mails with the same GUIDs. A virtual `All Mails` mailbox needs to be configured using the virtual plugin. Then you need to give this mailbox as `-a` parameter, e.g.: ``` doveadm sync -a "Virtual/All Mails" ... ``` Currently this is implemented by reading through all the GUIDs in the virtual mailbox. This of course isn't very efficient for things like incremental replication. Gmail has virtual folders: `All Mail`, `Starred` and `Important`. From migration point of view, this means that the migration should skip most of these folders, since their mails are in other folders anyway. You can tell dsync to skip these folders: ``` doveadm sync -x '\Flagged' -x '\Important' ``` By using the `\flag` parameters, dsync finds the folders by their `SPECIAL-USE` flag rather than their name (which may be different for users depending on their language). The `All Mail` folder contains also "archived mails" that don't exist in any other folder. These mails need to be migrated. See below. Google requires that SSL/TLS be enabled to connect through IMAP. See [migrating messages over IMAP](#migrating-messages-over-imap) for information on the various SSL/TLS related flags. Google has very limited support for username/password authentication, so you might have to use OAUTH2 or some other mechanism for logging in. ### GMail Migration Feature There is a `[imapc_features = gmail-migration](/main/core/summaries/settings.html#imapc_features)` setting that helps with GMail migration. It will: - Set the `[pop3_deleted_flag](/main/core/summaries/settings.html#pop3_deleted_flag)` to mails that no longer exist in POP3. - Return POP3 UIDL in GMail format so dsync can preserve it. - Add a new `$GmailHaveLabels` keyword to archived mails in the `\All` mailbox, which means those mails are not archived. You probably don't want to migrate these mails. - Note that mails in the `\Important` and `\Flagged` mailboxes are marked with `\Important` and `\Starred` labels. If you don't migrate mails that have `$GmailHaveLabels` then you must not exclude the `\Flagged` and `\Important` mailboxes or some of the mails won't be migrated. For example use a command line: sh ``` doveadm backup -a 'virtual/All' -O '-$GmailHaveLabels' -R -u user@domain imapc: ``` ## Optimizing Synchronization The `-s` (state) parameter can be used to significantly improve performance for incremental migrations. By using a "sync state string", dsync can avoid a full mailbox scan and only synchronize the changes that have occurred since the last synchronization. ### How it Works When you run dsync with the `-s` parameter, it will perform the synchronization as usual. At the end of the operation, doveadm will output a state string. This string is a snapshot of the mailbox's state. By providing this state string in the subsequent dsync call using the `-s` parameter, doveadm can avoid a full mailbox scan and instead only synchronize the changes that have occurred since the last synchronization. This can significantly reduce the time and resources required for the sync operation. ### Example Usage 1. **Initial sync:** sh ``` doveadm sync -s "" ``` After the initial sync, doveadm will return a state string. 2. **Subsequent syncs:** Save the state string from the previous sync and use it in the next one: sh ``` doveadm sync -s "" ``` This will perform an incremental sync based on the provided state. # Process Titles When `[verbose_proctitle = yes](/main/core/summaries/settings.html#verbose_proctitle)`, Dovecot adds various extra information to its process titles. Besides the various self-descriptive command states, there are the following: ## Generic ### `[initializing]` Added: 2.4.0 The process is still starting up and isn't yet ready to accept connections. This can especially happen if the process is still attempting to connect to the stats socket. ### `[idling]` The process is not doing anything except waiting for a client to be served. ### `[blocking on log write]` The log process is busy and not reading this process's logs. Try to debug (strace) the log process to see why. ## Log Process ### `[service too fast: FD/LISTEN_FD/LOG_PREFIX]` One specific service is sending logs faster than we can write them. The LOG\_PREFIX is usually enough to identify the service. If not, the FD and LISTEN\_FD can in theory be used to calculate which service it is. ### `[N services too fast, last: FD/LISTEN_FD/LOG_PREFIX]` Multiple services are sending logs faster than we can write them. The last service's information is shown. ### `[N services too fast]` Multiple services are sending logs faster than we can write them, but there is no additional information about which ones specifically. ## Mail Processes Added: 2.4.0 This means imap, pop3, submission and managesieve processes. ### `[waiting on client]` Login process connected to the mail process, but it hasn't finished sending the request. ### `[auth lookup]` Mail process is waiting on auth process to finish userdb lookups. ### `[post-login script]` Mail process is waiting on post-login scripts to finish. ## IMAP Process Added: 2.4.0 ### `[waiting on unhibernate client]` imap-hibernate process connected to the imap process, but it hasn't finished sending the request. ### `[unhibernating]` The imap connection is still being unhibernated. # Rawlog Dovecot supports logging IMAP/POP3/LMTP/SMTP(submission) traffic (also TLS/SSL encrypted). There are several possibilities for this: 1. `[rawlog_dir](/main/core/summaries/settings.html#rawlog_dir)` 2. Pre-login \*-login process via `-R` parameter. See below. 3. For proxying (in \*-login processes), use `[login_proxy_rawlog_dir](/main/core/summaries/settings.html#login_proxy_rawlog_dir)`. 4. For lmtp, you need to use `[lmtp_rawlog_dir](/main/core/summaries/settings.html#lmtp_rawlog_dir)` and `[lmtp_proxy_rawlog_dir](/main/core/summaries/settings.html#lmtp_proxy_rawlog_dir)` settings. 5. For submission, you need to use `[rawlog_dir](/main/core/summaries/settings.html#rawlog_dir)` and `[submission_relay_rawlog_dir](/main/core/summaries/settings.html#submission_relay_rawlog_dir)` settings. 6. Using rawlog binary, which is executed as post-login script. This is the legacy method, which shouldn't be necessary anymore. See below. ## Pre-login Rawlog The pre-login rawlog is used before IMAP, POP3, Submission or ManageSieve client logs into the post-login process. Note that LMTP and doveadm protocols don't have a pre-login process. INFO SSL/TLS sessions are currently not decrypted to rawlogs. You can enable pre-login rawlog for all users by telling the login processes to log to a rawlog directory: doveconf ``` service imap-login { executable = imap-login -R rawlogs } ``` This attempts to write the rawlogs under `$base_dir/login/rawlogs` directory. You need to create it first with enough write permissions: sh ``` mkdir /var/run/dovecot/login/rawlogs chown dovenull /var/run/dovecot/login/rawlogs chmod 0700 /var/run/dovecot/login/rawlogs ``` ## rawlog Binary This is the legacy method. `[rawlog_dir](/main/core/summaries/settings.html#rawlog_dir)` is preferred nowadays. This works by checking if `dovecot.rawlog/` directory exists in the logged in user's home directory, and writing the traffic to `yyyymmdd-HHMMSS-pid.in` and `.out` files. Each connection gets their own in/out files. Rawlog will simply skip users who don't have the `dovecot.rawlog/` directory and the performance impact for those users is minimal. ### Home Directory INFO For rawlog binary to work, your userdb must have returned a home directory for the user. WARNING The home directory must be returned by userdb, `[mail_home](/main/core/summaries/settings.html#mail_home)` won't work. Verify that `[doveadm user -u user@example.com](/main/core/summaries/doveadm.html#user)` returns the home directory, for example: sh ``` doveadm user -u user@example.com ``` ``` userdb: user@example.com user : user@example.com uid : 1000 gid : 1000 home : /home/user@example.com ``` In the above configuration, rawlog would expect to find `/home/user@example.com/dovecot.rawlog/` directory writable by uid `1000`. If your userdb can't return a home directory directly, you can add: doveconf ``` userdb db1 { # ... fields { home = /home/%{user} # or temporarily even e.g. home = /tmp/temp-home } } ``` You can also set `DEBUG` environment to have rawlog log an info message why it's not doing anything: doveconf ``` import_environment { DEBUG = 1 } ``` See `[import_environment](/main/core/summaries/settings.html#import_environment)`. ### Configuration To enable rawlog binary, use post-login scripting: doveconf ``` service imap { executable = imap postlogin } service pop3 { executable = pop3 postlogin } service postlogin { executable = script-login -d rawlog unix_listener postlogin { } } ``` You can also give parameters to rawlog: - `-b`: Write IP packet boundaries (or whatever read() sees anyway) to the log files. The packet is written between <<< and >>>. - `-t`: Log a microsecond resolution timestamp at the beginning of each line. - `-I`: Include IP address in the filename. - `-f in`: Log only to `*.in` files. - `-f out`: Log only to `*.out` files. # Running Dovecot ## Starting Dovecot can simply be started by running dovecot as root. If there are any problems, they're usually written to terminal, but they may also be written to error log at page [logging](/main/core/admin/logging.html) as well. - See [startup scripts](/main/installation/startup_scripts.html) ## Stopping Killing the Dovecot master process with a normal TERM signal does a clean shutdown. This can be done easily with `[doveadm stop](/main/core/summaries/doveadm.html#stop)`. `[shutdown_clients](/main/core/summaries/settings.html#shutdown_clients)` controls whether existing IMAP and POP3 sessions are killed. If you are using systemd, you need to set: ``` [Service] KillMode=none ExecStop=/usr/bin/doveadm stop ``` to avoid systemd from killing processes on restart. ## Processes When Dovecot is running, it uses several processes: sh ``` ps auxw|grep "dovecot" ``` ``` root 7245 0.1 0.1 2308 1096 pts/0 S+ 19:53 0:00 dovecot dovecot 7246 0.0 0.0 2084 824 pts/0 S+ 19:53 0:00 dovecot/anvil root 7247 0.0 0.0 2044 908 pts/0 S+ 19:53 0:00 dovecot/log root 7250 0.0 0.3 4988 3740 pts/0 S+ 19:53 0:00 dovecot/config root 7251 0.0 0.2 10024 2672 pts/0 S+ 19:53 0:00 dovecot/auth root 7303 0.6 0.3 10180 3116 pts/0 S+ 19:57 0:00 dovecot/auth -w vmail 7252 0.0 0.1 3180 1264 pts/0 S+ 19:53 0:00 dovecot/imap vmail 7255 0.0 0.1 3228 1596 pts/0 S+ 19:54 0:00 dovecot/pop3 dovenull 7260 0.0 0.1 4028 1940 pts/0 S+ 19:54 0:00 dovecot/imap-login dovenull 7262 0.0 0.1 4016 1916 pts/0 S+ 19:54 0:00 dovecot/pop3-login ``` - `dovecot` process is the Dovecot master process which keeps everything running. - `anvil` keeps track of user connections. - `log` writes to log files. All logging, except from master process, goes through it. - `config` parses the configuration file and sends the configuration to other processes. - `auth` handles all authentication. - `auth -w` process is an authentication worker process. It's used only with some "blocking" authentication databases, such as [SQL authentication database](/main/core/config/auth/databases/sql.html). - `imap-login` and `pop3-login` processes handle new IMAP and POP3 connections until user has logged in. They also handle proxying SSL connections even after login. - `imap` and `pop3` processes handle the IMAP and POP3 connections after user has logged in. ## Reloading Configuration Sending HUP signal to Dovecot reloads configuration. This can be done easily with: `[doveadm reload](/main/core/summaries/doveadm.html#reload)`. An acknowledgement is written to log file. ## Running Multiple Invocations of Dovecot You may wish to invoke a second session (or even multiple sessions) of Dovecot for testing different functionality, configurations, etc. In order to run multiple instances of Dovecot, you must: 1. Create a differently named copy of the `dovecot.conf` configuration file with these changes: 1. Change `[base_dir](/main/core/summaries/settings.html#base_dir)` to the new run directory. 2. Change services' `inet_listener` port numbers to new, unused values. 3. Optionally, change `instance_name` to show a different "dovecot/" prefix in ps output. 4. If you're using authentication sockets (for SMTP AUTH or deliver), you'll need to change them as well. `[auth_socket_path](/main/core/summaries/settings.html#auth_socket_path)` specifies the socket path for deliver. - Alternatively, if all the instances have identical authentication configuration, you can have only a single Dovecot instance serve the auth sockets and have the other instances use them. 2. Invoke dovecot (and dovecot-lda) with the `-c` parameter and the modified configuration file, e.g.: `dovecot -c /usr/local/etc/dovecot2.conf` 3. In order to tell the logs apart, you can set different log facilities for the instances, e.g., `syslog_facility=local6`, then configure syslogd to write local6 into "dovecot-otherinstance.log". Alternatively specify the log paths directly in `[log_path](/main/core/summaries/settings.html#log_path)` and related settings. ## Rotating Log Files If you specified log file paths manually in `dovecot.conf` instead of using syslog, you can send USR1 signal to Dovecot to make it close and reopen the log files. This can be done with: `[doveadm log reopen]()`. ## Troubleshooting If you can't see the Dovecot processes running after starting dovecot, something is most likely wrong in your dovecot.conf. Look at the error from Dovecot's log file. See [logging](/main/core/admin/logging.html) for how to find the log. If you really can't find any error messages from any logs, try starting Dovecot with `dovecot -F`. If you see it crash like: `sh: segmentation fault (core dumped) dovecot -F` Then it's a bug in Dovecot. Please report it with your configuration file. If it simply quits without giving any error, then it wrote the error to a log file and you just didn't find it. Try specifying the log file manually and make sure you're really looking at the correct file. See also [Dovecot troubleshooting](/main/core/admin/errors.html). # SASL SASL stands for "Simple Authentication and Security Layer". SASL itself is nothing more than a list of requirements for [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) and protocols to be SASL-compatible as described in [RFC 4422](https://datatracker.ietf.org/doc/html/rfc4422). IMAP, POP3, SMTP, and ManageSieve protocols all have support for SASL. Many people confuse SASL with one specific SASL implementation: the Cyrus SASL library. Dovecot has its own SASL implementation which could (one day) be separated from Dovecot itself to "compete" against Cyrus SASL library as an alternative implementation. Dovecot can be used as the SASL server for several external SMTP/Submission servers. See [SASL auth for external SMTP servers](/main/howto/virtual/simple_install.html#smtp-auth). # Testing ## Check Local Installation ### Check Dovecot is Running First check with `ps` that the `dovecot` process is actually running. If it's not, you had an error in `dovecot.conf` and the error message was written to log. Go back to [running Dovecot](/main/core/admin/running.html) and [logging](/main/core/admin/logging.html) if you can't find it. ### Check Dovecot is Listening Next check that Dovecot is listening for connections: sh ``` nc localhost 143 ``` ``` Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE STARTTLS AUTH=PLAIN] Dovecot ready. ``` If you got "connection refused", make sure that Dovecot is configured to serve the imap protocol and listening on the expected interfaces/addresses. The simplest way to do that would be using `[doveconf(1)](/main/core/man/doveconf.1.html)`: sh ``` doveconf protocols listen ``` doveconf ``` protocols = imap pop3 lmtp sieve listen = *, :: ``` If the protocols setting doesn't contain `imap` then add it. Also make sure, that relevant `!include` or `!include_try` configuration lines are not commented. If the connection fails and dovecot emits a log "_auth: Fatal: Support not compiled in for passdb driver 'pam'_", then rebuild dovecot with the pam development headers package installed. In that case you have to re-run the configure script, possibly including option **\--with-pam** to the configure command line. Next check that it also works from remote host: sh ``` nc imap.example.com 143 ``` ``` Trying 1.2.3.4... Connected to imap.example.com. Escape character is '^]'. * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE STARTTLS AUTH=PLAIN] Dovecot ready. ``` If that didn't work, check all possible firewalls in between, and check that `listen` setting is `*` in `dovecot.conf`. If you have only imaps enabled, see [remote login](#check-dovecot-is-allowing-remote-logins) for how to test using `openssl s_client`. ### Check Dovecot is Allowing Logins sh ``` nc localhost 143 ``` ``` a login "username" "password" ``` Replace the username and password with your local authentication credentials. Note that all IMAP commands begin with a tag, which is basically any string you want, but it must be there. So don't leave out the "a" in the above example. If the password contains `"` character, escape it with `\\` (e.g. `"foo\"bar"`). You should get an "a OK Logged in." reply. If you get "Authentication failed" error, set `[auth_verbose = yes](/main/core/summaries/settings.html#auth_verbose)` and `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)` in `dovecot.conf`, restart Dovecot and try again. The log file should now show enough information to help you fix the problem. ### Check Dovecot is Allowing Remote Logins You'll need to try this from another computer, since all local IPs are treated as secure: sh ``` nc imap.example.com 143 ``` ``` a login "username" "password" ``` If the connection is hanging instead of giving "\* Dovecot ready", you have a firewall that's preventing the connections. Otherwise, the only difference here compared to step above is that you might get: ``` * BAD [ALERT] Plaintext authentication is disabled, but your client sent password in plaintext anyway. If anyone was listening, the password was exposed. a NO Plaintext authentication disabled. ``` If this is the case, you didn't set `[auth_allow_cleartext = yes](/main/core/summaries/settings.html#auth_allow_cleartext)`. You could alternatively use OpenSSL to test that the server works with SSL: - Test using imaps port (assuming you haven't disabled imaps port): sh ``` openssl s_client -connect imap.example.com:993 ``` ``` * OK Dovecot ready. ``` - Test using imap port and STARTTLS command (works also with imap port): sh ``` openssl s_client -connect imap.example.com:143 -starttls imap ``` ``` * OK Dovecot ready. ``` Check Dovecot Finds INBOX After logging in, check that the INBOX is found: ``` b select inbox * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) * OK [PERMANENTFLAGS (\Answered \Flagged \Deleted \Seen \Draft \*)] Flags permitted. * 1 EXISTS * 1 RECENT * OK [UIDVALIDITY 1106186941] UIDs valid * OK [UIDNEXT 2] Predicted next UID b OK [READ-WRITE] Select completed. ``` If anything goes wrong, set `[log_debug = category=mail](/main/core/summaries/settings.html#log_debug)` and try again. The log file should now contain debugging information of where Dovecot is trying to find the mails. Fix the [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) and try again. ### Check Dovecot Finds Other Mailboxes If you already have other mailboxes created, you can check that Dovecot finds them: ``` c list "" * * LIST (\NoInferiors) "/" "test" * LIST (\NoInferiors) "/" "INBOX" c OK List completed. ``` If they weren't found, set `[log_debug = category=mail](/main/core/summaries/settings.html#log_debug)` and look at the debugging information. Fix the [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) and try again. ### Check Other IMAP Commands If you already have some emails, you can try reading them: ``` 1 SELECT INBOX 2 FETCH 1:* (FLAGS INTERNALDATE BODY.PEEK[HEADER.FIELDS (SUBJECT)]) 3 FETCH 1 BODY[TEXT] ``` `1:*` means all messages You can also try moving a mail to Trash: ``` 4 CREATE Trash 5 COPY 1 Trash 6 STORE 1 +FLAGS \Deleted 7 EXPUNGE ``` ### Check Mail Clients Work Since mail clients can be configured in various ways, please check first if the problem is with Dovecot configuration or with the client's configuration. You can rule out it being Dovecot's problem with the "telnet" methods described above. If you can't log in, - Make sure SSL/TLS settings are correct. - Make sure the client uses plaintext authentication method, unless you've specifically configured Dovecot to accept others. If you can see only INBOX, - Clear out any "IMAP namespace prefix" or similar settings from clients. - Check if client is configured to show only "subscribed mailboxes". If so, you'll have to subscribe to the mailboxes you wish to see. You can see a list of subscribed mailboxes with: ``` d lsub "" * * LSUB () "/" "INBOX" d OK Lsub completed. ``` Most IMAP clients have been tested with Dovecot and they work. ### Gracefully Exit Session To close the connection to Dovecot issue a logout: ``` e logout * BYE Logging out e OK Logout completed. ``` ## Functional & Performance Testing Easiest way to test Dovecot is to use [ImapTest](https://dovecot.github.io/imaptest/). It can be used to flood a server with random commands and it can also attempt to mimic a large number of real-world clients. ### Test Preparation System configuration - Make sure your firewall is configured to allow incoming connections for the following tcp ports: 24, 110, 143, 993, 995, 4190. - Ensure ulimit is high enough to accept all the connections and open files. ### Dovecot Configuration Enable LMTP delivery times in the configuration: doveconf ``` deliver_log_format = msgid=%{msgid} from=<%{from}> size=%{size} vsize=%{vsize} session=%{session_time}ms delivery=%{delivery_time}ms: %{message} ``` You can then see log entries like: ``` Oct 06 12:40:13 lmtp(testuser_717@example.com): Info: iQBSCwulE1ZXMwAA0J78UA: msgid=unspecified from= size=155980 vsize=157963 session=161ms delivery=134ms: saved mail to INBOX ``` Increase the maximum user connections per IP `[mail_max_userip_connections = 1000](/main/core/summaries/settings.html#mail_max_userip_connections)`. ### Troubleshooting You might run into problems where you have too few services running and you need to increase the number of services and/or modify client limit for the following: - auth - imap - pop3 - lmtp ### Sample Tests #### Functional Testing Simple imaptest to cover the basics: sh ``` timeout 10s imaptest pass=supersecret host=127.0.0.1 mbox=testmbox.sm40k \ user=testuser1 Fetch2=100 store=100 delete=100 expunge=100 clients=1 ``` Check the output for errors. Verify that messages exist in INBOX: `[doveadm mailbox status -u testuser1 all INBOX]()`. Copy a message with doveadm: `[doveadm copy -u testuser1 Trash mailbox INBOX 1](/main/core/summaries/doveadm.html#copy)`. Copy messages with imaptest: sh ``` imaptest pass=supersecret host=127.0.0.1 mbox=testmbox.sm40k user=testuser1 \ copybox=Trash ``` Move a message: `[doveadm move -u testuser1 Trash mailbox INBOX 1](/main/core/summaries/doveadm.html#move)`. #### Performance Testing: Test rapid delivery of lots of messages via IMAP APPEND (100k test users) sh ``` imaptest - user=testuser%d pass=testpass mbox=testmbox append=100,0 logout=0 \ users=100000 clients=500 msgs=100000 no_pipelining secs=10 ``` Test rapid delivery of lots of messages via LMTP: Commandimaptest.profile sh ``` imaptest profile=imaptest.profile mbox=testmbox secs=10 ``` ``` lmtp_port = 24 lmtp_max_parallel_count = 500 # Set to ~50-60% of total_user_count total_user_count = 800 rampup_time = 0s user lmtptest { username_format = testuser%{num} count = 100% mail_inbox_delivery_interval = 1s mail_spam_delivery_interval = 0 mail_action_delay = 0 mail_action_repeat_delay = 0 mail_session_length = 0 mail_send_interval = 0 mail_write_duration = 0 mail_inbox_reply_percentage = 0 mail_inbox_delete_percentage = 0 mail_inbox_move_percentage = 0 mail_inbox_move_filter_percentage = 0 } client lmtponly { count = 100% } ``` #### Load Testing: 1h mixed test against proxy (10.41.1.135) with 2m users and 200 clients: sh ``` timeout 1h imaptest pass=testpassword host=10.41.1.135 mbox=testmbox \ user=testuser%d users=1-2000000 Fetch2=100 store=100 delete=90 expunge=100 \ clients=200 ``` 8hr mixed test with 2m users; generally this would be run against multiple proxies (host=proxy ip) from multiple imaptest nodes. sh ``` timeout 8h imaptest pass=testpassword host=127.0.0.1 mbox=testmbox \ user=testuser%d users=1-2000000 Fetch2=100 store=100 delete=90 expunge=100 \ clients=100 ``` ##### POP3 + LMTP Testing Commandpop3\_2m\_profile.conf sh ``` imaptest pass=testpassword mbox=testmbox.sm40k profile=pop3_2m_profile.conf \ no_tracking clients=10000 ``` ``` lmtp_port = 24 lmtp_max_parallel_count = 1800 total_user_count = 2000000 rampup_time = 600s user pop3 { username_format = testuser%{num | fill('0', 7)} username_start_index = 1 count = 100% mail_inbox_delivery_interval = 1h mail_spam_delivery_interval = 0 mail_action_delay = 30s mail_action_repeat_delay = 1s } client pop3 { count = 70% connection_max_count = 1 protocol = pop3 pop3_keep_mails = no login_interval = 1m } client pop3 { count = 30% connection_max_count = 1 protocol = pop3 pop3_keep_mails = yes login_interval = 5min } ``` ##### IMAP + LMTP Testing Commandimap\_4m\_profile.conf sh ``` imaptest pass=testpassword mbox=testmbox profile=imap_4m_profile.conf \ clients=10000 ``` ``` lmtp_port = 24 lmtp_max_parallel_count = 15000 total_user_count = 4000000 rampup_time = 60s user imap_poweruser { username_format = testuser%{num | fill('0', 7)} username_start_index = 2000000 count = 50% mail_inbox_delivery_interval = 10m mail_spam_delivery_interval = 0s mail_action_delay = 1s mail_action_repeat_delay = 0 mail_session_length = 5s mail_send_interval = 2h mail_write_duration = 2m mail_inbox_reply_percentage = 50 mail_inbox_delete_percentage = 50 mail_inbox_move_percentage = 35 mail_inbox_move_filter_percentage = 10 } user imap_normal { username_format = testuser%7n username_start_index = 1 count = 50% mail_inbox_delivery_interval = 1h mail_spam_delivery_interval = 0 mail_action_delay = 3 min mail_action_repeat_delay = 10s mail_session_length = 30s mail_send_interval = 3h mail_write_duration = 2 min mail_inbox_reply_percentage = 5 mail_inbox_delete_percentage = 80 mail_inbox_move_percentage = 5 mail_inbox_move_filter_percentage = 10 } client Thunderbird { count = 60% connection_max_count = 1 imap_idle = yes imap_fetch_immediate = UID RFC822.SIZE FLAGS BODY.PEEK[HEADER.FIELDS (From To Cc Bcc Subject Date Message-ID Priority X-Priority References Newsgroups In-Reply-To Content-Type)] imap_fetch_manual = RFC822.SIZE BODY[] } client AppleMail { count = 40% connection_max_count = 1 imap_idle = yes imap_fetch_immediate = INTERNALDATE UID RFC822.SIZE FLAGS BODY.PEEK[HEADER.FIELDS (date subject from to cc message-id in-reply-to references x-priority x-uniform-type-identifier x-universally-unique-identifier)] MODSEQ imap_fetch_manual = BODYSTRUCTURE BODY.PEEK[] } ``` ##### Generate Read Load (BODY FETCHs): sh ``` imaptest - user=testuser%d select=100 fetch2=100,0 logout=0 clients=10 \ msgs=100000 no_pipelining users=400 no_tracking ``` # Timeouts Dovecot has a lot of timeouts in various components. Most of them have hardcoded values, because there's normally no need to change them. ## Protocol Proxies - Dovecot proxy handles IMAP, POP3 and Submission pre-login timeouts and invalid error command handling the same as Dovecot backend. After login the proxy will continue proxying until the client or the backend disconnects. - Connect timeout to backend is governed by `[login_proxy_timeout](/main/core/summaries/settings.html#login_proxy_timeout)`, which is 30 seconds by default. Can be overridden by `proxy_timeout` passdb extra field. - After connection has been established, there's still a login timeout `CLIENT_LOGIN_TIMEOUT_MSECS = MASTER_LOGIN_TIMEOUT_SECS*1000` = 3 minutes ([`login-common/client-common.h`](https://github.com/dovecot/core/blob/main/src/login-common/client-common.h#L33) and [`lib-master/master-interface.h`](https://github.com/dovecot/core/blob/main/src/lib-master/master-interface.h#L120)). ## IMAP - Before login: `CLIENT_LOGIN_TIMEOUT_MSECS = MASTER_LOGIN_TIMEOUT_SECS*1000` = 3 minutes (the same as proxies). - This may be shorter if all the available connections are in use `[[link,service_configuration,service imap-login { client_limit * process_limit }]]`. In that case the oldest non-logged in connection is disconnected. - After login: `CLIENT_IDLE_TIMEOUT_MSECS` = 30 minutes (minimum required by [RFC 2060 (section 5.4)](https://datatracker.ietf.org/doc/html/rfc2060#section-5.4)) - If IDLE command is started, Dovecot never disconnects. Only if the connection is lost there will be a disconnection. A dead connection is detected by Dovecot periodically sending "I'm still here" notifications to client (`[imap_idle_notify_interval](/main/core/summaries/settings.html#imap_idle_notify_interval)`, default every 2 minutes). - IMAP clients are supposed to send something before 30 minutes are up, but several clients don't do this. Some Outlook versions even stop receiving new mails entirely until manual intervention if IMAP server disconnects the client. - Dovecot also disconnects an IMAP client that sends too many invalid commands: - Before login: Disconnect on 3rd invalid command (`CLIENT_MAX_BAD_COMMANDS` in [`imap-login/imap-login-client.c`](https://github.com/dovecot/core/blob/main/src/imap-login/imap-login-client.c#L29). - After login: Disconnect on 20th invalid command (`CLIENT_MAX_BAD_COMMANDS` in [`imap/imap-common.h`](https://github.com/dovecot/core/blob/main/src/imap/imap-common.h#L14). ## POP3 - Before login: `CLIENT_LOGIN_TIMEOUT_MSECS = MASTER_LOGIN_TIMEOUT_SECS*1000` = 3 minutes (same as proxies). - This may be shorter if all the available connections are in use (`service pop3-login { client_limit * process_limit }`). In that case the oldest non-logged in connection is disconnected. - After login: `CLIENT_IDLE_TIMEOUT_MSECS` = 10 minutes ([`pop3/pop3-client.c`](https://github.com/dovecot/core/blob/main/src/pop3/pop3-client.c#L34). - Dovecot also disconnects an POP3 client that sends too many invalid commands: - Before login: Disconnect on 3rd invalid command (`CLIENT_MAX_BAD_COMMANDS` in [`pop3-login/client.c`](https://github.com/dovecot/core/blob/main/src/pop3-login/client.c#L25). - After login: Disconnect on 20th invalid command (`CLIENT_MAX_BAD_COMMANDS` in [`pop3/pop3-client.c`](https://github.com/dovecot/core/blob/main/src/pop3/pop3-client.c#L33). ## Submission and LMTP - Before login: `CLIENT_LOGIN_TIMEOUT_MSECS = MASTER_LOGIN_TIMEOUT_SECS*1000` = 3 minutes (submission, same as proxies). - This may be shorter if all the available connections are in use (`service submission-login { client_limit * process_limit }`). In that case the oldest non-logged in connection is disconnected. - After login: `CLIENT_IDLE_TIMEOUT_MSECS` = 5 minutes for LMTP and 10 minutes for Submission. [`lmtp/lmtp-client.c`](https://github.com/dovecot/core/blob/main/src/lmtp/lmtp-client.c#L28) and [`submission/submission-client.c`](https://github.com/dovecot/core/blob/main/src/submission/submission-client.c#L43). - Dovecot also disconnects an SMTP client that sends too many invalid commands: - Before login: Disconnect on 10th invalid command. (`CLIENT_MAX_BAD_COMMANDS` in [`submission-login/client.c`](https://github.com/dovecot/core/blob/main/src/submission-login/client.c#L23). - After login: Disconnect on 20th invalid command (`CLIENT_MAX_BAD_COMMANDS` in [`submission/submission-client.c`](https://github.com/dovecot/core/blob/main/src/submission/submission-client.c#L40). # Basic Configuration This page highlights some common authentication-related settings that may need to be set on your system. ## General Settings ### `auth_mechanisms` `[auth_mechanisms = plain login](/main/core/summaries/settings.html#auth_mechanisms)` Enables the `PLAIN` and `LOGIN` authentication mechanisms. The `LOGIN` mechanism is obsolete, but still used by old clients. ### `auth_verbose` `[auth_verbose = yes](/main/core/summaries/settings.html#auth_verbose)` Log a line for each authentication attempt failure. ### `auth_verbose_passwords` `[auth_verbose_passwords = sha1:6](/main/core/summaries/settings.html#auth_verbose_passwords)` Log the password hashed and truncated for failed authentication attempts. For example the SHA1 hash for "pass" is `9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684` but because of the setting of `:6` we only log `9d4e1e`. This can be useful for detecting brute force authentication attempts without logging the users' actual passwords. ### Authentication Penalty See [Authentication Penalty](/main/core/config/auth/penalty.html). ### `auth_cache_size` `[auth_cache_size = 100M](/main/core/summaries/settings.html#auth_cache_size)` Specifies the amount of memory used for authentication caching (passdb and userdb lookups). ### `imap_id_retain` `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)` If `imap_id_retain=yes`, imap-login will send the IMAP ID string to auth process. The variable `%{client_id}` will expand to the IMAP ID in the auth process. The ID string is also sent to the next hop when proxying. This allows passing the ID string to [Authentication Policy](/main/core/config/auth/policy.html) requests ## Authentication After Proxies This section describes authentication tactics that can be used if an architecture is used where an edge Proxy authenticates a user and then redirects to an internal Backend. TIP Proxy already verifies the authentication (in the reference Dovecot architecture; password has been switched to a master password at this point), so we don't really need to do it again. We could, in fact, even avoid the password checking entirely, but for extra security it's still done in this document. ### `auth_mechanisms` `[auth_mechanisms = plain login](/main/core/summaries/settings.html#auth_mechanisms)` Enables the `PLAIN` and `LOGIN` authentication mechanisms. The `LOGIN` mechanism is obsolete, but still used by some older clients. ### Authentication Penalty See [Authentication Penalty](/main/core/config/auth/penalty.html) for how to disable authentication penalty. The proxy already handled this. ### `auth_cache_size` `[auth_cache_size = 100M](/main/core/summaries/settings.html#auth_cache_size)` Specifies the amount of memory used for authentication caching (passdb and userdb lookups). ### `login_trusted_networks` `[login_trusted_networks = 10.0.0.0/24](/main/core/summaries/settings.html#login_trusted_networks)` Space-separated list of IP/network ranges that contain the Dovecot Proxies. This setting allows Proxies to forward the client's original IP address and session ID to the Backends. ### `mail_max_userip_connections` `[mail_max_userip_connections = 10](/main/core/summaries/settings.html#mail_max_userip_connections)` Maximum number of simultaneous IMAP4 or POP3 connections allowed for the same user from the same IP address (`10` = 10 IMAP + 10 POP3). ### SSL `[ssl = no](/main/core/summaries/settings.html#ssl)`, `[auth_allow_cleartext = yes](/main/core/summaries/settings.html#auth_allow_cleartext)` `Proxy` already decrypted the SSL connections. The Backends will always see only unencrypted connections (unless internal connections are also configured to use SSL). # Authentication Caching Dovecot supports caching the results of [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) lookups. The following rules apply to using the authentication cache: - Data is used from the cache if it's not expired (`[auth_cache_ttl](/main/core/summaries/settings.html#auth_cache_ttl)` setting) - If authentication fails this time, but it didn't fail last time, it's assumed that the password has changed and a database lookup is done. - If a database lookup fails because of some internal error, but data still exists in the cache (even if expired), the cached data is used. This allows Dovecot to log in some users even if the database is temporarily down. The authentication cache can be flushed with the `[doveadm auth cache flush]()` command. The `[doveadm auth cache status]()` command shows the number of cache hits and misses, hit ratio, and cache size. You can use that information for tuning the cache size and TTL. Pass `--reset` to clear the hit/miss counters after reading them. ## Settings TIP It should be pretty safe to set very high TTLs, because the only field that usually can change is the user's password, and Dovecot attempts to catch those cases (see the rules above). ### `auth_cache_negative_ttl` **Default:** `1hour` **Value:** [time](/main/core/settings/types.html#time) This sets the time to live for negative hits to passdb or userdb (i.e., when the user is not found or there is a password mismatch). The value `0` completely disables caching of these hits. ### `auth_cache_size` **Default:** _\[None\]_ **Value:** [size](/main/core/settings/types.html#size) The authentication cache size (e.g., `10M`). `[auth_cache_size = 0](/main/core/summaries/settings.html#auth_cache_size)` disables use of the authentication cache. A typical passdb cache entry is around 50 bytes and a typical userdb cache entry is around 100-200 bytes, depending on the amount of information your user and password database lookups return. ### `auth_cache_ttl` **Default:** `1hour` **Value:** [time](/main/core/settings/types.html#time) Time to live for cache entries. After the TTL expires, the cached record is no longer used, unless the main database look-up returns internal failure. Entries are removed from the cache only when the cache is full and a new entry is to be added. ### `auth_cache_verify_password_with_worker` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) The auth master process by default is responsible for the hash verifications. Setting this to `yes` moves the verification to auth-worker processes. This allows distributing the hash calculations to multiple CPU cores, which could make sense if strong hashes are used. ## Password Changing Scenarios ## Normal 1. User logs in with password X. The password X is added to cache and login succeeds. 2. Password is changed to Y. 3. User logs in with password Y. The cached password X doesn't match Y, but since the previous authentication was successful Dovecot does another backend passdb lookup to see if the password changed. It did, so the password Y is cached and login succeeds. ## Old Cached Password 1. User logs in with password X. The password X is added to cache and login succeeds. 2. Password is changed to Y. 3. User logs in with password X. The cached password X matches X, so login succeeds. ## Early Change 1. User logs in with password X. The password X is added to cache and login succeeds. 2. User logs in with password Y. The cached password X doesn't match Y, but since the previous authentication was successful Dovecot does another backend passdb lookup to see if the password changed. It didn't, so the login fails. 3. Password is changed to Y. 4. User logs in with password Y. The cached password X doesn't match Y and the previous authentication was unsuccessful, so Dovecot doesn't bother doing another backend passdb lookup (until cache TTL expires). The login fails. # BSDAuth (`bsdauth`) WARNING BSDAuth is deprecated. It will be maintained on a best-effort basis for Dovecot CE, based on community patches. Users are strongly advised to use [PAM authentication database](/main/core/config/auth/databases/pam.html) instead. This is similar to [PAM authentication database](/main/core/config/auth/databases/pam.html), but used by OpenBSD. It supports `cache_key` parameter the same way as PAM. # Authentication via Remote IMAP Server (`imap`) ## Settings Removed: 2.4.0: The arg-based driver settings have been removed in favor of using the standard `imapc_*` settings. Removed: 2.4.0: The `ssl_ca_file`, `ssl_ca_dir` and `allow_invalid_cert` settings have been removed. The standard `ssl_*` settings can be used instead (also inside `passdb { ... }` if wanted). ### `imapc_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The remote IMAP host to connect to. ### `imapc_port` **Default:** `143` **Value:** [Port Number](/main/core/settings/types.html#port-number) The port on the remote IMAP host to connect to. ### `imapc_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Log all IMAP traffic input/output to this directory. ### `imapc_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``imaps``starttls` Use TLS to connect to the remote IMAP server. | Value | Description | | --- | --- | | `no` | No TLS | | `imaps` | Explicitly connect to remote IMAP port using TLS | | `starttls` | Use IMAP STARTTLS command to switch to TLS connection | ### `imapc_user` **Default:** `%{owner_user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_master_user](/main/core/summaries/settings.html#imapc_master_user)` - `[imapc_password](/main/core/summaries/settings.html#imapc_password)` - `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` The user identity to be used for performing a regular IMAP LOGIN to the source IMAP server. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ## Example Authenticates users against remote IMAP server in IP address 192.168.1.123: doveconf ``` passdb imap { imapc_host = 192.168.1.123 imapc_port = 143 imapc_user = %{owner_user} imapc_rawlog_dir = /tmp/imapc_rawlog/ imapc_ssl = starttls ssl_client_require_valid_cert = no } ``` # LDAP Authentication (`ldap`) There are two ways to do LDAP authentication in [passdb](/main/core/config/auth/passdb.html): - [Authentication Binds](#authentication-binds) - [Password Lookups](#password-lookups) LDAP can be used as [userdb ldap](#ldap-userdb). ## Connecting The LDAP server(s) endpoints must be specified as ldap URIs: - `[ldap_uris](/main/core/summaries/settings.html#ldap_uris)`: A space separated list of LDAP URIs to connect to. If multiple LDAP servers are specified, it's decided by the LDAP library how the server connections are handled. Typically the first working server is used, and it's never disconnected from. So there is no load balancing or automatic reconnecting to the "primary" server. ### Connection Authentication If LDAP server requires authentication, set: - `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` - `[ldap_auth_dn_password](/main/core/summaries/settings.html#ldap_auth_dn_password)` - `[ldap_auth_sasl_mechanisms](/main/core/summaries/settings.html#ldap_auth_sasl_mechanisms)` can be set to list of SASL mechanisms to authenticate with. Note that this is used only for the initial connection authentication, not for any subsequent [authentication binds](#authentication-binds). ### Worker Processes If `[passdb_use_worker = no](/main/core/summaries/settings.html#passdb_use_worker)` / `[userdb_use_worker = no](/main/core/summaries/settings.html#userdb_use_worker)` (default for passdb ldap), all LDAP lookups are performed by the auth master process. Each LDAP connection can keep up to 8 requests pipelined. For small systems this is sufficient and uses less resources, but it may become a bottleneck if there are a lot of queries. If `[passdb_use_worker = yes](/main/core/summaries/settings.html#passdb_use_worker)`, `auth-worker` processes are used to perform the lookups. Each auth worker process creates its own LDAP connection so this can increase parallelism. ### Connection Optimization When using - `[passdb_use_worker = no](/main/core/summaries/settings.html#passdb_use_worker)`, - `[userdb_use_worker = no](/main/core/summaries/settings.html#userdb_use_worker)`, - auth binds and - userdb ldap lookups, the userdb lookups should use a separate connection to the LDAP server. That way it can send LDAP requests asynchronously to the server, which improves the performance. This can be done by specifying distinct `[ldap_connection_group](/main/core/summaries/settings.html#ldap_connection_group)` in the LDAP `[passdb](/main/core/summaries/settings.html#passdb)` / `[userdb](/main/core/summaries/settings.html#userdb)` sections. dovecot.conf doveconf ``` passdb ldap { # ldap_connection_group left unchanged, the default is '' ... } userdb ldap { ldap_connection_group = different-connection-group ... } ``` ### SSL/TLS You can enable TLS in two alternative ways: - Connect to ldaps port (636) by using "ldaps" protocol, e.g. `ldap_uris = ldaps://secure.domain.org` - Connect to ldap port (389) and use STARTTLS command. Use `[ldap_starttls = yes](/main/core/summaries/settings.html#ldap_starttls)` to enable this. See the [SSL configuration](/main/core/config/ssl.html#configuration-overview) settings for how to configure TLS. The default LDAP settings are read (by OpenLDAP) from `/etc/ldap/ldap.conf`. These are used by Dovecot, unless overridden by the Dovecot configuration. Note that if either `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` or `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` is set, neither default is used from `ldap.conf`. Not all of Dovecot SSL settings are supported by the LDAP library. Below is the list of supported settings: #### `ssl_cipher_list` **Default:** `ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH (for ssl_server, empty for ssl_client)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` - `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The list of SSL ciphers to use for TLSv1.2 and below connections, in order of preference. Use `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` for TLSv1.3 connections. You do not need to edit this setting in order to disable specific SSL protocols; that is best done with `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` instead. This setting is used for both incoming and outgoing SSL connections. #### `ssl_client_ca_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The directory where trusted SSL CA certificates can be found. For example `/etc/ssl/certs`. These certificates are used only for outgoing SSL connections (e.g. with the imapc driver). For extra security you might want to point to a directory containing certificates only for the CAs that are actually needed for the server operation instead of all the root CAs. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. #### `ssl_client_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. #### `ssl_client_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` #### `ssl_client_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` #### `ssl_client_require_valid_cert` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Require a valid certificate when connecting to external SSL services? #### `ssl_curve_list` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Colon separated list of elliptic curves to use, in order of preference. An empty value uses the defaults from the SSL library. This setting is used for both incoming and outgoing SSL connections. Example: ``` ssl_curve_list = P-521:P-384:P-256 ``` #### `ssl_min_protocol` **Default:** `TLSv1.2` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The minimum SSL protocol version Dovecot accepts. It cannot be empty. This setting is used for both incoming and outgoing SSL connections. Supported values are: `ANY` WARNING This value is meant for tests only. It should not be used in any deployment of any value/relevance. `TLSv1` Support TLSv1+. (TLSv1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.1` Support TLSv1.1+. (TLSv1.1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.2` Support TLSv1.2+. `TLSv1.3` Support TLSv1.3+. `LATEST` Support only the latest version available. #### Custom Certs If you need to connect to ldaps secured against a custom certificate of authority (CA), you will need to install the custom CA on your system. For OpenLDAP, by default, the CA must be installed under the directory specified in the `TLS_CACERTDIR` option found under `/etc/openldap/ldap.conf` (default value is `/etc/openldap/certs`). After copying the CA, you'll need to run "c\_rehash ." inside the directory, this will create a symlink pointing to the CA. You can test the CA installation with this command: sh ``` $ openssl s_client -connect yourldap.example.org:636 \ -CApath /etc/openldap/certs -showcerts ``` This should report "Verify return code: 0 (ok)". ## LDAP Settings ### `ldap_auth_dn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specify the Distinguished Name (the username used to login to the LDAP server). Leave it commented out to bind anonymously (useful with `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`). Example: `ldap_auth_dn = uid=dov-read,dc=example,dc=com,dc=.` ### `ldap_auth_dn_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for LDAP server. Used if `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is specified. ### `ldap_auth_sasl_authz_id` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL authorization ID, ie. the `[ldap_auth_dn_password](/main/core/summaries/settings.html#ldap_auth_dn_password)` is for this "master user", but the `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is still the logged in user. Normally you want to keep this empty. ### `ldap_auth_sasl_mechanisms` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of SASL mechanism names to use. ### `ldap_auth_sasl_realm` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL realm to use. ### `ldap_base` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP base. `[Settings variables](/main/core/settings/variables.html)` can be used. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the variable. Examples: - `ldap_base = dc=mail, dc=example, dc=org` - `ldap_base = %{passdb:classOfServiceDN | safe}` ### `ldap_connection_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Only databases with the same connection group share the ldap connections. By default all the databases have the same `""`(empty string) default connection group, and as such share the connections. ### `ldap_debug_level` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP library debug level as specified by `LDAP_DEBUG_*` in `ldap_log.h`. Value `-1` means everything. You may need to recompile OpenLDAP with debugging enabled to get enough output. ### `ldap_deref` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``searching``finding``always` Specify dereference which is set as an LDAP option. ### `ldap_scope` **Default:** `subtree` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `base``onelevel``subtree` This specifies the search scope. ### `ldap_starttls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set to `yes` to use TLS to connect to the LDAP server. ### `ldap_uris` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP URIs to use. Configure this setting to specify what LDAP server(s) to connect to. The URIs are in syntax `protocol://host:port`. Example: `ldap_uris = ldaps://secure.domain.org` ### `ldap_version` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP protocol version to use. Likely `2` or `3`. ### `passdb_ldap_bind` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set `yes` to use authentication binding for verifying password's validity. This works by logging into LDAP server using the username and password given by client. The `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` is used to find the DN for the user. Note that the `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` are still used, only the password field is ignored in it. Before doing any search, the binding is switched back to the default DN. If you use this setting, it's a good idea to use a different `[ldap_connection_group](/main/core/summaries/settings.html#ldap_connection_group)` for userdb. That way one connection is used only for LDAP binds and another connection is used for user lookups. Otherwise the binding is changed to the default DN before each user lookup. ### `passdb_ldap_bind_userdn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) If authentication binding is used, you can save one LDAP request per login if users' DN can be specified with a common template. The template can use the standard `[Settings variables](/main/core/settings/variables.html)`. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the safe variables (i.e. NOT `%{user}`). Note that you can't use any `%{ldap:*}` variables in `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` if you use this setting, because no LDAP fields are looked up. Examples: - `passdb_ldap_bind_userdn = cn=%{user},ou=people,o=org` - `passdb_ldap_bind_userdn = cn=%{user},%{passdb:domain_dn | safe}` ### `passdb_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter for passdb lookup. Variables that can be used (see `[Settings variables](/main/core/settings/variables.html)` for full list). Example: ``` passdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) #... } ``` ### `userdb_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter for userdb lookup. Variables that can be used (see `[Settings variables](/main/core/settings/variables.html)` for full list). Example: ``` userdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) #... } ``` ### `userdb_ldap_iterate_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) Attributes to get a list of all users. Currently only the attribute `user` is supported. Example: ``` userdb ldap { iterate_filter = (objectClass=smiMessageRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} } } ``` ### `userdb_ldap_iterate_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter to get a list of all users. ``` userdb ldap { iterate_filter = (objectClass=smiMessageRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} } } ``` ## LDAP-Specific Variables The following variables can be used inside the `[passdb](/main/core/summaries/settings.html#passdb)` / `[userdb](/main/core/summaries/settings.html#userdb)` sections: | Variable | Description | | --- | --- | | `%{ldap:attrName}` | Fetches a single-valued attribute. Fails if the attribute is not present, unless the `|default` filter is given. If there are multiple values, all except the first are ignored (with warning). | | `%{ldap_multi:attrName}` | Added: 2.4.0 Fetches a multi-valued attribute and outputs the values separated by tabs, with each value "tab-escaped". Use the `list` [filter](/main/core/settings/variables.html#list-of-filters) to further convert it to a wanted value. For example: `mail_access_groups = %{ldap_multi:userGroups | list | default('mail')}` | | `%{ldap:dn}` | Retrieves the Distinguished Name of the entry. | ## Password Lookups Advantages over [Authentication Binds](#authentication-binds): - Faster, because Dovecot can keep sending multiple LDAP requests asynchronously to the server. With auth binds Dovecot must wait for each request to finish before sending the next one. - Supports non-plaintext [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) (if returning plaintext or [properly hashed passwords](/main/core/config/auth/schemes.html)). - When using [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) and [static userdb](/main/core/config/auth/databases/static.html), deliver can check if destination user exists. With auth binds this check isn't possible. ### LDAP Server Permissions Normally LDAP server doesn't give anyone access to users' passwords, so you'll need to create an administrator account that has access to the `userPassword` field. With OpenLDAP this can be done by modifying `/etc/ldap/slapd.conf`: /etc/ldap/slapd.conf ``` #---- there should already be something like this in the file access to attribute=userPassword #---- just add this line by dn="" read by anonymous auth by self write by * none ``` Replace `` with the DN you specified in `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` in `dovecot.conf`'s ldap settings. Alternatively, you can: 1. Create below text file and save it as `authuser_modify.ldif`. authuser\_modify.ldif ``` dn: olcDatabase={2}hdb,cn=config changetype: modify replace: olcAccess olcAccess: {0}to attrs=userPassword by self write by dn="cn=authuser,dc=test,dc=dovecot,dc=net" read by * auth olcAccess: {1}to * by self read by dn="cn=authuser,dc=test,dc=dovecot,dc=net" read by * auth ``` 2. Run `ldapmodify` to apply the change. sh ``` $ ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f doveauth_access.ldif ``` ### Dovecot Configuration The two important settings in password lookups are: - `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` specifies the LDAP filter how user is found from the LDAP. You can use all the normal `[Settings variables](/main/core/settings/variables.html)` like `%{user}` in the filter. - `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` specifies a list of attributes that are returned and how to produce the returned value. Usually the LDAP attribute names aren't the same as [the field names that Dovecot uses internally](/main/core/config/auth/passdb.html). You must create a mapping between them to get the wanted results. This is done by listing the `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` as ` = ` where expression can include ldap specific variables and other variables too. For example: dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org passdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) fields { user = %{ldap:uid} password = %{ldap:userPassword} } } ``` This maps the LDAP "uid" attribute to Dovecot's "user" field and LDAP's "userPassword" attribute to Dovecot's "password" field. These two fields should always be returned, but it's also possible to return other special [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). #### Password Most importantly, `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` must return a `password` field, which contains the user's password. The next thing Dovecot needs to know is what format the password is in. If all the passwords are in same format, you can use `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` setting in `dovecot.conf` to specify it. Otherwise each password needs to be prefixed with `{password-scheme}`, for example `{plain}plaintext-password`. See [Password Schemes](/main/core/config/auth/schemes.html) for a list of supported password schemes. #### Username LDAP lookups are case-insensitive. Unless the username is normalized, it's possible that a user logging in as "user", "User" and "uSer" are treated differently. By default Dovecot uses `[auth_username_format = %{user | lower}](/main/core/summaries/settings.html#auth_username_format)` to lowercase the username before it reaches the LDAP lookup. Alternatively, you may want to change the username to be exactly as it is in the LDAP database. You can do this by returning `user` field in `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` setting, as shown in the above example. ### Example A typical configuration would look like: dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org passdb ldap { bind = no default_password_scheme = MD5 filter = (&(objectClass=posixAccount)(uid=%{user})) fields { user = %{ldap:uid} password = %{ldap:userPassword} } } ``` ## Authentication Binds Advantages over [Password Lookups](#password-lookups) - LDAP server verifies the password, so Dovecot doesn't need to know what format the password is stored in. - A bit more secure, as a security hole in Dovecot doesn't give attacker access to all the users' password hashes. (And Dovecot admins in general don't have direct access to them.) You can enable authentication binds by setting `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`. Dovecot needs to know what DN to use in the binding. There are two ways to configure this: lookup or template. ### DN Lookup DN is looked up by sending a `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` LDAP request and getting the DN from the reply. This is very similar to doing a [password lookup](#password-lookups). The only difference is that `userPassword` attribute isn't returned. Just as with password lookups, the `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` may contain special [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). Example: dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org passdb ldap { bind = yes filter = (&(objectClass=posixAccount)(uid=%{user})) fields { user = %{ldap:uid} } } ``` ### DN Template You can do authentication binding using DN template by configuring it in the `[passdb_ldap_bind_userdn](/main/core/summaries/settings.html#passdb_ldap_bind_userdn)` setting. The main reason to use DN template is to avoid doing the DN lookup, so that the authentication consists only of one LDAP request. With IMAP and POP3 logins, the same optimization can be done by using [prefetch authentication database](/main/core/config/auth/databases/prefetch.html) and returning userdb info in the DN lookup (a total of two LDAP requests per login in both cases). If you're also using Dovecot for SMTP AUTH, it doesn't do a userdb lookup so the prefetch optimization doesn't help. If you're using DN template, there is no LDAP lookup that returns fields, so `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` can't access any `%{ldap:*}` variables. Also, `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` setting is ignored. dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org passdb ldap { bind = yes bind_userdn = cn=%{user},ou=people,o=org } ``` ## LDAP userdb Usually your LDAP database also contains the [userdb](/main/core/config/auth/userdb.html). If your home directory can be specified with a template, you're using only a single UID and GID, and you don't need any other user-specific fields, you should use [Userdb Static](/main/core/config/auth/databases/static.html#userdb) instead to avoid an unnecessary LDAP lookup. You can also use [prefetch authentication database](/main/core/config/auth/databases/prefetch.html) to avoid the userdb LDAP lookup. Userdb lookups are always done using the `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` bind. It's not possible to do the lookup using the user's DN (remember that e.g. [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) needs to do userdb lookups without knowing the user's password). The userdb lookups are configured in very much the same way as [password lookups](#password-lookups). `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_filter](/main/core/summaries/settings.html#userdb_ldap_filter)`, are used in the same way in passdb. If you're using a single UID and GID for all the users, you can specify them globally with `[mail_uid](/main/core/summaries/settings.html#mail_uid)` and `[mail_gid](/main/core/summaries/settings.html#mail_gid)` settings instead of returning them from LDAP. doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org userdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) fields { home = %{ldap:homeDirectory} uid = %{ldap:uidNumber} gid = %{ldap:gidNumber} } } ``` ### User Iteration For using `doveadm -A` or `-u` with wildcards you need to configure the userdb to support user iteration. This is done by adding `[userdb_ldap_iterate_filter](/main/core/summaries/settings.html#userdb_ldap_iterate_filter)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings to the userdb: doveconf ``` userdb ldap { # filter = ... iterate_filter = (objectClass=posixAccount) iterate_fields { user = %{ldap:uid} } } ``` ### Attribute Templates You can mix static text with the value returned from LDAP by using `%{ldap:*}` variables, which expand to the named LDAP attribute's value. #### Examples Create a `quota_storage_size` field with value `B` where `` comes from "quotaBytes" LDAP attribute: doveconf ``` userdb ldap { fields { quota_storage_size = %{ldap:quotaBytes}B } } ``` Create a `mail_path` field with value `/var/mail//Maildir` where `` comes from "sAMAccountName" LDAP attribute: doveconf ``` userdb ldap { fields { mail_path = /var/spool/vmail/%{ldap:sAMAccountName}/Maildir } } ``` You can add static fields that aren't looked up from LDAP. For example create a "mail\_path" field with value `/var/vmail/%{user | domain}/%{user | username}/Maildir`: doveconf ``` userdb ldap { fields { quota_storage_size = %{ldap:quotaBytes}B mail_path = /var/vmail/%{user | domain}/%{user | username}/Maildir } } ``` It is possible to give default values to nonexistent attributes by using e.g. `%{ldap:userDomain | default('example.com')}` where if userDomain attribute doesn't exist, example.com is used instead. ### Multiple Queries via userdbs Example: Give the user a class attribute, which defines the quota: dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.org ldap_auth_dn = cn=admin,dc=example,dc=org ldap_auth_dn_password = secret ldap_base = dc=example,dc=org userdb ldap-user { driver = ldap result_success = continue-ok ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) fields { class = %{ldap:userClass} } } userdb ldap-class { driver = ldap skip = notfound ldap_filter = (&(objectClass=classSettings)(class=%{userdb:class})) fields { quota_storage_size = %{ldap:quotaBytes}B } } ``` # Lua Authentication Database (`lua`) You can implement passdb and userdb using [Lua](https://www.lua.org/) script. ## Authentication When used in authentication, additional module `dovecot.auth` is added, which contains constants for [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html). For details about Dovecot Lua, see [Lua](/main/core/admin/lua/index.html). ## Initialization Added: 2.4.0 When passdb or userdb is initialized, there will be a lookup for a function to get the `cache_key` for the userdb or passdb. These functions are called `auth_passdb_get_cache_key()` for passdbs and `auth_userdb_get_cache_key()` for userdbs. The global `script_init()` function is called for all Lua scripts and can be used to pass arguments to the script using `[lua_settings](/main/core/summaries/settings.html#lua_settings)`. These settings can be used to pass out initialization parameters from Dovecot. dovecot.conf/etc/dovecot/auth.lua doveconf ``` passdb lua { lua_file = /etc/dovecot/auth.lua lua_settings { password = {PLAIN}test } } ``` lua ``` local password = nil function script_init(args) password = args["password"] return 0 end function auth_passdb_lookup(req) return dovecot.auth.PASSDB_RESULT_OK, { ["password"]=password } end function auth_passdb_get_cache_key() return "%{user | username}\t%{protocol}" end ``` The lua script to be used is given to the passdb using `[lua_file](/main/core/summaries/settings.html#lua_file)` setting. ### Constants #### `dovecot.auth.PASSDB_RESULT_INTERNAL_FAILURE` #### `dovecot.auth.PASSDB_RESULT_SCHEME_NOT_AVAILABLE` Indicates password scheme that cannot be understood. #### `dovecot.auth.PASSDB_RESULT_USER_UNKNOWN` #### `dovecot.auth.PASSDB_RESULT_USER_DISABLED` #### `dovecot.auth.PASSDB_RESULT_PASS_EXPIRED` #### `dovecot.auth.PASSDB_RESULT_NEXT` Indicates that this passdb did not authenticate user, next passdb should do it. #### `dovecot.auth.PASSDB_RESULT_PASSWORD_MISMATCH` #### `dovecot.auth.PASSDB_RESULT_OK` #### `dovecot.auth.USERDB_RESULT_INTERNAL_FAILURE` #### `dovecot.auth.USERDB_RESULT_USER_UNKNOWN` #### `dovecot.auth.USERDB_RESULT_OK` ### Auth Request Auth init registers object `struct auth_request*` which allows access to various parts of the auth request. You should use the loggers associated with `auth_request` when possible. #### Methods ##### `auth_request#log_debug(text)` Logs debug message (if debug is enabled, noop otherwise). ##### `auth_request#log_error(text)` Logs error message. ##### `auth_request#log_info(text)` Logs informational message. ##### `auth_request#log_warning(text)` Logs warning message. ##### `auth_request#response_from_template(template)` Removed: 2.4.1 This was a bit unsafe function. Return the table instead with the necessary `auth_request#var_expand()` calls. Takes in `key=value` template and expands it using `var_expand()` and produces table suitable for passdb result. ##### `auth_request#var_expand(template)` Performs var expansion on the template using `[Settings variables](/main/core/settings/variables.html)`. ##### `auth_request#password_verify(crypted_password, plain_password)` Checks if the plain password matches the crypted or hashed password. ##### `auth_request#event()` Returns child event for the auth request. Can be used for logging and other events. Comes with a prefix. #### Subtables ##### `auth_request#passdb` ##### `auth_request#userdb` #### Members See `[Settings variables](/main/core/settings/variables.html)` for details. ##### `auth_request#auth_domain` ##### `auth_request#auth_user` ##### `auth_request#auth_username` ##### `auth_request#cert` ##### `auth_request#client_id` ##### `auth_request#domain` ##### `auth_request#domain_first` ##### `auth_request#domain_last` ##### `auth_request#home` ##### `auth_request#lip` ##### `auth_request#local_name` ##### `auth_request#login_domain` ##### `auth_request#login_user` ##### `auth_request#login_username` ##### `auth_request#lport` ##### `auth_request#master_user` ##### `auth_request#mech` ##### `auth_request#orig_domain` ##### `auth_request#orig_user` ##### `auth_request#orig_username` ##### `auth_request#password` ##### `auth_request#pid` ##### `auth_request#real_lip` ##### `auth_request#real_lport` ##### `auth_request#real_rip` ##### `auth_request#real_rport` ##### `auth_request#rip` ##### `auth_request#rport` ##### `auth_request#secured` ##### `auth_request#service` ##### `auth_request#session` ##### `auth_request#session_pid` ##### `auth_request#user` ##### `auth_request#username` #### Additional Members ##### `skip_password_check` Set if the password has already been validated by another passdb. ##### `passdbs_seen_user_unknown` If some previous passdb has not found this user. ##### `passdbs_seen_internal_failure` If some previous passdb has had internal failure. ##### `userdbs_seen_internal_failure` If some previous userdb has had internal failure. ## passdb To configure passdb in dovecot, use: doveconf ``` passdb lua { lua_file = /path/to/lua use_worker = yes # default is yes } ``` If `auth_password_verify` is found, it's always used. By default, dovecot runs Lua scripts in auth-worker processes. If you do not want this, you can disable using worker, and Lua script will be run in auth process. This can degrade performance if your script is slow or makes external lookups. ### Execution Modes Lua passdb supports two modes of function: #### Lookup Database Function signature is `auth_passdb_lookup(request)`. Function must return a tuple, which contains: - `dovecot.auth.PASSDB_RESULT_OK` and extra fields table - `dovecot.auth.PASSDB_RESULT_*` error and error string The extra fields table must be in key-value format, as it will be imported into auth request. Removed: 2.4.1 String can no longer be returned for `PASSDB_RESULT_OK`. #### Password Verification Database Function signature is `auth_password_verify(request, password)`. Function must return a tuple, which contains: - `dovecot.auth.PASSDB_RESULT_OK` and extra fields table - `dovecot.auth.PASSDB_RESULT_*` error and error string The extra fields table must be in key-value format, as it will be imported into auth request. Removed: 2.4.1 String can no longer be returned for `PASSDB_RESULT_OK`. ## userdb To configure userdb in dovecot, use: doveconf ``` userdb lua { lua_file = /path/to/lua use_worker = yes # default is yes } ``` ### Execution Modes Lua userdb supports both single user lookup and iteration. #### Single User Lookup Function signature is `auth_userdb_lookup(request)`. Function must return a tuple, which contains: - `dovecot.auth.USERDB_RESULT_OK` and extra fields table - `dovecot.auth.USERDB_RESULT_*` error and error string The extra fields table must be in key-value format, as it will be imported into auth request. Removed: 2.4.1 String can no longer be returned for `USERDB_RESULT_OK`. #### User Iteration Function signature is `auth_userdb_iterate()`. Function returns a table of usernames. Key names are ignored. TIP The iteration will hold the whole user database in memory during iteration. ## Examples lua ``` function auth_passdb_lookup(req) if req.user == "testuser1" then return dovecot.auth.PASSDB_RESULT_OK, { password = "pass" } end return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN, "no such user" end function auth_userdb_lookup(req) if req.user == "testuser1" then return dovecot.auth.USERDB_RESULT_OK, { uid = "vmail", gid = "vmail" } end return dovecot.auth.USERDB_RESULT_USER_UNKNOWN, "no such user" end function script_init() return 0 end function script_deinit() end function auth_userdb_iterate() return {"testuser1"} end ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 ### Simple Username Password Database Example: opensmtpd authentication. The example uses whitespace separated username and password. As a special caution, the way Lua is used here means you can have multiple user password per line, instead of just one. This can be extended to more complicated separators or multiple fields per user. If you only want to authenticate users, and don't care about user listing, you can use: lua ``` function auth_passdb_lookup(req) for line in io.lines("/path/to/file") do for user, pass in string.gmatch(line, "(%w+)%s(.+)") do if (user == req.username) then -- you can add additional information here, like userdb_uid return dovecot.auth.PASSDB_RESULT_OK, { password = pass } end end end return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN end ``` 1 2 3 4 5 6 7 8 9 10 11 12 If you also want to be able to list users, so that you could use `doveadm cmd -A`: lua ``` local database = "/path/to/file" function db_lookup(username) for line in io.lines(database) do for user, pass in string.gmatch(line, "(%w+)%s(.+)") do if (user == username) then return {result=0, password=pass} end end end return {result=-1} end function auth_passdb_lookup(req) res = db_lookup(req.username) if res.result == 0 then -- you can add additional information here for passdb return dovecot.auth.PASSDB_RESULT_OK, { password = res.password } end return dovecot.auth.PASSDB_RESULT_USER_UNKNOWN end function auth_userdb_lookup(req) res = db_lookup(req.username) if res.result == 0 then -- you can add additional information here for userdb, like uid or home return dovecot.auth.USERDB_RESULT_OK, { uid = "vmail, gid = "vmail" } end return dovecot.auth.USERDB_RESULT_USER_UNKNOWN end function auth_userdb_iterate() users = {} for line in io.lines(database) do for user in string.gmatch(line, "(%w+)%s.+") do table.insert(users, user) end end return users end ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 # Open Authentication v2.0 Database (`oauth2`) This database works with a OAuth2 ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749))provider. You are recommended to use `oauthbearer` (preferred) or `xoauth2`[authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) with this database. The responses from endpoints must be JSON objects. Changed: 2.4.0: The OAuth2 mechanism no longer uses a passdb for token authentication. Password Grant still needs a oauth2 passdb. ## Settings Oauth2 overrides some of the default HTTP client and SSL settings. You can override these and any other HTTP client or SSL settings by placing them inside the `[oauth2](/main/core/summaries/settings.html#oauth2)` named filter. ### `oauth2` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [OAuth2 Authentication Database](/main/core/config/auth/databases/oauth2.html) Filter for oauth2 specific settings. ### `oauth2_active_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Attribute name for (optional) checking whether account is disabled. ### `oauth2_active_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Expected value in `active_attribute`. (empty = require present, but anything goes) ### `oauth2_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) Key-value fields to include in successful authentication. ### `oauth2_force_introspection` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Force introspection even if tokeninfo contains wanted fields. Set this to `yes` if you are using `[oauth2_active_attribute](/main/core/summaries/settings.html#oauth2_active_attribute)`. ### `oauth2_introspection_mode` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** ```auth``get``post``local` **See Also:** - `[oauth2_introspection_url](/main/core/summaries/settings.html#oauth2_introspection_url)` - `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` - `[oauth2_force_introspection](/main/core/summaries/settings.html#oauth2_force_introspection)` To enable oauth2 you must choose how to do token introspection. `[oauth2_introspection_url](/main/core/summaries/settings.html#oauth2_introspection_url)` is not required if `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` already provides all the necessary fields, or if you are using `local` validation. You can force introspection with `[oauth2_force_introspection](/main/core/summaries/settings.html#oauth2_force_introspection)`, if you need to it every time. With `local` validation, `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` is also ignored. | Value | Description | | --- | --- | | `auth` | GET request with Bearer authentication. | | `get` | GET request with token appended to URL. | | `post` | POST request with token=bearer\_token as content. | | `local` | Attempt to locally validate and decode JWT token. | ### `oauth2_introspection_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL for getting more information about token. ### `oauth2_issuers` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Valid issuer(s) for the token. ### `oauth2_local_validation` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[dict](/main/core/summaries/settings.html#dict)` A dictionary for fetching validation keys. Example: ``` oauth2_local_validation { dict fs { fs posix { prefix = /tmp/keys/ } } } ``` ### `oauth2_openid_configuration_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL to [RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628) OpenID Provider Configuration Information schema. ### `oauth2_scope` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) A list of valid scopes. ### `oauth2_send_auth_headers` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to send special headers about authentication to remote server. If you enable this, the following headers will be sent: `X-Dovecot-Auth-Protocol` Requested protocol, such as imap or pop3. `X-Dovecot-Auth-Local` Local IP address where client connected to. `X-Dovecot-Auth-Remote` Remote IP address of the client connection. ### `oauth2_token_expire_grace` **Default:** `1min` **Value:** [time](/main/core/settings/types.html#time) Specifies how long after token expiration it is still accepted. It shouldn't be 0 because clocks aren't exact, and network can add some delays. ### `oauth2_tokeninfo_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL for verifying token validity. Token is appended to the URL. Example: ``` oauth2_tokeninfo_url = http://endpoint/oauth/tokeninfo?access_token= ``` ### `oauth2_use_worker_with_mech` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[passdb_use_worker](/main/core/summaries/settings.html#passdb_use_worker)` Use worker process to verify token. This setting only applies to mechanism. If you want to use worker with `passdb oauth2`, use `[passdb_use_worker](/main/core/summaries/settings.html#passdb_use_worker)` instead. Worker processes are mostly useful for distributing local token validation to multiple CPUs. ### `oauth2_username_attribute` **Default:** `email` **Value:** [string](/main/core/settings/types.html#string) Username attribute in response. ### `oauth2_username_validation_format` **Default:** `%{user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[oauth2_username_attribute](/main/core/summaries/settings.html#oauth2_username_attribute)` Normalization for oauth2 provided username, this setting is normally not needed. You only need this if the username that comes from authentication will not otherwise match with `[oauth2_username_attribute](/main/core/summaries/settings.html#oauth2_username_attribute)` value. ## Configuration ### Common dovecot.conf doveconf ``` auth_mechanisms { oauthbearer = yes xoauth2 = yes } oauth2 { # ... } ``` ### Backend #### Examples ##### Google Configuration file example for [Google](https://developers.google.com/identity/protocols/OAuth2): doveconf ``` oauth2 { tokeninfo_url = https://www.googleapis.com/oauth2/v3/tokeninfo?access_token= introspection_url = https://www.googleapis.com/oauth2/v2/userinfo #force_introspection = yes username_attribute = email } ``` ##### WSO2 Identity Server Configuration file example for [WSO2 Identity Server](https://wso2.com/identity-and-access-management/): doveconf ``` oauth2 { introspection_mode = post introspection_url = https://client_id:client_secret@server.name:port/oauth2/introspect username_attribute = username active_attribute = active active_value = true } ``` ##### Microsoft Identity Platform Configuration file example for [Microsoft Identity Platform](https://learn.microsoft.com/en-us/entra/identity-platform/userinfo): doveconf ``` oauth2 { introspection_mode = auth introspection_url = https://graph.microsoft.com/v1.0/me # this can vary on your settings username_attribute = mail ssl_client_ca_file = /etc/ssl/certs/ca-certificates.crt } ``` ### Proxy If you want to forward oauth2 authentication to your backend, you can use various ways. Without proxy authentication: doveconf ``` passdb static { fields { nopassword = yes proxy = yes proxy_mech = %{mechanism} # ... } } ``` With proxy authentication, put into `dovecot.conf`: doveconf ``` oauth2 { # ... fields { proxy = y proxy_mech = %{mech} } } ``` #### Proxy with Password Grant If you want to configure proxy to get token and pass it to backend: dovecot.conf doveconf ``` oauth2 { client_id = verySecretClientId client_secret = verySecretSecret tokeninfo_url = http://localhost:8000/oauth2?oauth= introspection_url = http://localhost:8000/introspect introspection_mode = post username_attribute = username fields { pass = %{token} } } passdb oauth2 { mechanisms_filter = plain login oauth2 { # inherit common oauth2 settings from the global scope grant_url = http://localhost:8000/token fields { host = 127.0.0.1 proxy = y proxy_mech = xoauth2 pass = %{oauth2:access_token} } } } ``` #### Local Validation Local validation allows validating tokens without connecting to an oauth2 server. This requires that key issuer supports JWT tokens ([RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519)). You can put the validation keys into any [dictionary](/main/core/config/dict.html). The lookup key used is `/shared///`. If there is no `azp` element in token body, then `default` is used. The `alg` field is always uppercased by Dovecot. If there is no `kid` element in token header, `default` is used. Keys are cached into memory when they are fetched; to evict them from cache you need to restart Dovecot. If you want to do key rotation, it is recommended to use a new key id. Example: json ``` { "kid":"Zm9vb2Jhcgo", "alg":"ES256", "typ":"JWT" }.{ "sub":"testuser@example.org", "azp":"issuer.net-dovecot" } ``` Would turn into: `/shared/issuer.net-dovecot/ES256/Zm9vb2Jhcgo`. If using fs posix, key would be at `/etc/dovecot/keys/issuer.net-dovecot/ES256/Zm9vb2Jhcgo`. In key id and AZP field, `/` are escaped with `%2f` and `%` are escaped with `%25` with any driver. This is because `/` is a dict key component delimiter. When using dict-fs driver, if the path starts with `.`, it will be escaped using two more dots. So any `.` turns into `...`, and any `..` turns into `....`. For example: json ``` { "kid":""./../../../../etc, "alg":"ES256", "typ":"JWT" }.{ "sub":"testuser@example.org", "azp":"attack" } ``` Would turn into: `/etc/dovecot/keys/attack/ES256/...%2f....%2f....%2f....%2f....%2fetc%2fpasswd`. Local validation can be enabled with other oauth2 options, so that if key validation fails for non-JWT keys, then online validation is performed. You can use local validation with password grants too. This will save you introspection roundtrip to oauth2 server. To use local validation, put into `dovecot.conf`: doveconf ``` oauth2 { introspection_mode = local oauth2_local_validation { dict fs { fs posix { prefix=/etc/dovecot/keys/ } } } } ``` Currently, Dovecot oauth2 library implements the following features of JWT tokens: - IAT checking - NBF checking - EXP checking - ISS checking - ALG checking - SUB support - AUD support AUD check checks client\_id, not scope. If the token has scope field, this is checked for scope. KTY checking has been removed. - AZP support The following algorithms are supported - HS256, HS384, HS512 - RS256, RS384, RS512 - PS256, PS384, PS512 - ES256, ES384, ES512 There is currently no support for EdDSA algorithms. ES supports any curve supported by OpenSSL for this purpose. ### OpenID Discovery Support for [RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628) OpenID Discovery (OIDC) can be achieved with `openid_configuration_url`. Setting this causes Dovecot to report OIDC configuration URL as `openid-configuration` element in error JSON. # Authentication Databases - [BSD](/main/docs/core/config/auth/databases/bsd.html) - [IMAP](/main/docs/core/config/auth/databases/imap.html) - [LDAP](/main/docs/core/config/auth/databases/ldap.html) - [Lua](/main/docs/core/config/auth/databases/lua.html) - [OAuth2](/main/docs/core/config/auth/databases/oauth2.html) - [PAM](/main/docs/core/config/auth/databases/pam.html) - [Passwd](/main/docs/core/config/auth/databases/passwd.html) - [Passwd-file](/main/docs/core/config/auth/databases/passwd_file.html) - [Prefetch](/main/docs/core/config/auth/databases/prefetch.html) - [SQL](/main/docs/core/config/auth/databases/sql.html) - [Static](/main/docs/core/config/auth/databases/static.html) # PAM (`pam`) This is the most common way to authenticate system users nowadays. PAM is not itself a password database, but rather its configuration tells the system how exactly to do the authentication. Usually this means using the `pam_unix.so` module, which authenticates user from the system's shadow password file. Because PAM is not an actual database, only cleartext authentication mechanisms can be used with PAM. PAM cannot be used as a [userdb](/main/core/config/auth/userdb.html) either (although static user templates could be used to provide the same effect). Usually PAM is used with [passwd authentication database](/main/core/config/auth/databases/passwd.html) or [static authentication database](/main/core/config/auth/databases/static.html). Dovecot should work with Linux PAM, Solaris PAM, OpenPAM (FreeBSD), and ApplePAM (Mac OS X). ## Settings ### `passdb_pam_failure_show_msg` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Replace the default `Authentication failed` reply with PAM's failure. ### `passdb_pam_max_requests` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` Number of requests a auth-worker process handles for passdb pam before it dies. This configures similar behaviour as the `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` setting but it limits only the number of pam passdb requests, not all requests to be handled by an auth-worker. ### `passdb_pam_service_name` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) The PAM service name to be used with the pam passdb. ### `passdb_pam_session` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot open a PAM session and close it immediately. ### `passdb_pam_setcred` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot create PAM credentials. The credentials are never deleted, which may cause problems with some PAM plugins. ## Service Name The PAM configuration is usually in the `/etc/pam.d/` directory, but some systems may use a single file, `/etc/pam.conf`. By default Dovecot uses dovecot as the PAM service name, so the configuration is read from `/etc/pam.d/dovecot`. You can change this by setting the wanted service name using `[passdb_pam_service_name](/main/core/summaries/settings.html#passdb_pam_service_name)`. You can also set the service to `%{protocol}` in which case Dovecot automatically uses either `imap` or `pop3` as the service, depending on the actual service the user is logging in to. Examples: - Use `/etc/pam.d/imap` and `/etc/pam.d/pop3`: doveconf ``` passdb pam { service_name = %{protocol} } ``` - Use `/etc/pam.d/mail`: doveconf ``` passdb pam { service_name = mail } ``` ## PAM Sessions By setting `[passdb_pam_service_name = yes](/main/core/summaries/settings.html#passdb_pam_service_name)` you can make Dovecot open a PAM session and close it immediately. Some PAM plugins need this, for instance `pam_mkhomedir`. With this parameter, `dovecot.conf` might look something like this: doveconf ``` passdb pam { session = yes service_name = dovecot } ``` ## PAM Credentials By setting `[passdb_pam_setcred = yes](/main/core/summaries/settings.html#passdb_pam_setcred)` you can make Dovecot create PAM credentials. Some PAM plugins need this. The credentials are never deleted however, so using this might cause problems with other PAM plugins. ## Limiting the Number of PAM Lookups Usually in other software, PAM is used to do only a single lookup in a process, so PAM plugin writers haven't done much testing on what happens when multiple lookups are done. Because of this, many PAM plugins leak memory and possibly have some other problems when doing multiple lookups. If you notice that PAM authentication stops working after some time, you can limit the number of lookups done by the auth worker process before it dies using the `[passdb_pam_max_requests](/main/core/summaries/settings.html#passdb_pam_max_requests)` setting: doveconf ``` passdb pam { max_requests = 100 } ``` The default `max_requests` value is 100. ## Username Changing A PAM module can change the username. ## Making PAM Plugin Failure Messages Visible You can replace the default "Authentication failed" reply with PAM's failure reply by setting `[passdb_pam_failure_show_msg](/main/core/summaries/settings.html#passdb_pam_failure_show_msg)`: doveconf ``` passdb pam { failure_show_msg = yes } ``` This can be useful with e.g. `pam_opie` to find out which one time password you're supposed to give: ``` 1 LOGIN username otp 1 NO otp-md5 324 0x1578 ext, Response: ``` # Restrict IP-Addresses Allowed to Connect via PAM You can restrict the IP-Addresses allowed to connect via PAM: doveconf ``` passdb pam { fields { allow_nets = 10.1.100.0/23,2001:db8:a0b:12f0::/64 } } ``` ## Caching Dovecot supports caching password lookups by setting `[auth_cache_size](/main/core/summaries/settings.html#auth_cache_size)` to a non-zero value. Examples: doveconf ``` # 1MB auth cache size auth_cache_size = 1024 passdb pam { } ``` ## Examples ### Linux Here is an example `/etc/pam.d/dovecot` configuration file which uses standard UNIX authentication: ``` auth required pam_unix.so nullok account required pam_unix.so ``` ### Solaris For Solaris you will have to edit `/etc/pam.conf`. Here is a working Solaris example (using `[service_name = %{service}](/main/core/summaries/settings.html#service_name)` instead of the default `dovecot` service): ``` imap auth requisite pam_authtok_get.so.1 imap auth required pam_unix_auth.so.1 imap account requisite pam_roles.so.1 imap account required pam_unix_account.so.1 imap session required pam_unix_session.so.1 pop3 auth requisite pam_authtok_get.so.1 pop3 auth required pam_unix_auth.so.1 pop3 account requisite pam_roles.so.1 pop3 account required pam_unix_account.so.1 pop3 session required pam_unix_session.so.1 ``` ### Mac OS X On Mac OS X, the `/etc/pam.d/dovecot` file might look like this: ``` auth required pam_opendirectory.so try_first_pass account required pam_nologin.so account required pam_opendirectory.so password required pam_opendirectory.so ``` ...which, as the equivalent of `/etc/pam.d/login` on OS X 10.9. For very old versions of OS X (e.g. 10.4), can be represented (where?) as the following in the on that OS: doveconf ``` passdb pam { service_name = login } ``` On older versions of Mac OS X, "passwd" can be used as a userdb to fill in UID, GID, and homedir information after PAM was used as a passdb, even though Directory Services prevents "passdb passwd" from working as a username/password authenticator. This will provide full system user authentication with true homedir mail storage, without resorting to a single virtual mail user or LDAP: doveconf ``` userdb passwd { } ``` # Passwd-file (`passwd-file`) This file is compatible with a normal `/etc/passwd` file, and a password file used by [libpam-pwdfile](/main/core/config/auth/databases/pam.html) plugin. ## Configuration `user:password:uid:gid:(gecos):home:(shell):extra_fields` For a [passdb](/main/core/config/auth/passdb.html) it's enough to have only the user and password fields. For a [userdb](/main/core/config/auth/userdb.html), you need to set also uid, gid, and preferably also home [virtual users](/main/core/config/auth/users/virtual.html)). (gecos) and (shell) fields are unused by Dovecot. The password field can be in four formats (see [Password Schemes](/main/core/config/auth/schemes.html)): - `password`: Assume `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` (CRYPT) password scheme. See [Password Schemes](/main/core/config/auth/schemes.html). - `{SCHEME}password`: The password is in the given scheme. - `password[13]`: libpam-passwd file compatible format for CRYPT scheme. - `password[34]`: libpam-passwd file compatible format for MD5 scheme. `extra_fields` is a space-separated list of `key=value` pairs which can be used to set various [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) and [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). Keys which begin with a `userdb_ prefix` are used for userdb, others are used for passdb. For example, if you wish to override `[mail_path](/main/core/summaries/settings.html#mail_path)` for one use, use `userdb_mail_path=~/mail`. `[Settings variables](/main/core/settings/variables.html)` expansion is done for `extra_fields`. Empty lines and lines beginning with `#` character are ignored. ### Settings ### `passwd_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to the passwd-file. The path can consists from per-user variables such as s`%{user | domain}`. If the path starts with static path, then Dovecot ensures that the expanded path does not point outside of this static path. If the path starts with variable, this protection is disabled. For example if this is set to `/etc/dovecot/%{user | domain}/passwd`, then using login username such as `root@..` won't be allowed to expand into `/etc/dovecot../passwd`, as that would escape `/etc/dovecot`. If you use something like `%{env:PREFIX}}/%{user | domain}/passwd` as path, it is recommended that PREFIX points to deep enough path, such as `/etc/dovecot/domains/`, and you do not modify `[auth_username_chars](/main/core/summaries/settings.html#auth_username_chars)` to avoid including `/` as allowed character. Also global settings that are commonly overridden inside the passdb filter: - `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` specifies the default password scheme to be used in the passwd-files. - `[auth_username_format](/main/core/summaries/settings.html#auth_username_format)` changes the username that is looked up from the passwd-file. For example `auth_username_format = %{protocol}` can be used to lookup the current protocol instead of the username. ### Multiple passwd-files You can use all `[Settings variables](/main/core/settings/variables.html)` in the passwd-file filenames, for example: doveconf ``` passdb passwd-file { # Each domain has a separate passwd-file: passwd_file_path = /etc/auth/%{user | domain}/passwd } ``` Added: 2.4.0 To iterate databases that use `[Settings variables](/main/core/settings/variables.html)` expansion in the path, you need to provide enough information when using e.g. `[doveadm user](/main/core/summaries/doveadm.html#user)` to iterate. For example `[doveadm user *@domain.com](/main/core/summaries/doveadm.html#user)` would match with `%{user | domain}` expansion in the above example, and `[doveadm user *](/main/core/summaries/doveadm.html#user)` would iterate `/etc/auth/passwd` if available. This iteration works with all doveadm mail commands similarly. ### Variables `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` and `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` can use `%{passwd_file:}` variables to access the current passdb or userdb lookup's fields. The available fields are: - `uid` - `gid` - `home` - Any specified extra fields. The `userdb_` prefixed fields are available also in passdb lookups with the `userdb_` prefix. In userdb lookups these same fields are available without the `userdb_` prefix. ### Examples doveconf ``` passdb passwd-file { default_password_scheme = plain-md5 auth_username_format = %{user | username} passwd_file_path = /etc/imap.passwd } userdb passwd-file { auth_username_format = %{user | username} passwd_file_path = /etc/imap.passwd fields { uid:default = vmail gid:default = vmail home:default = /home/vmail/%{user} } } ``` - The `fields` is explained in `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)`. They can be used to provide userdb extra fields based on templates. If you leave any of the standard userdb fields (uid, gid, home) empty in the passwd file, these defaults will be used. If you leave out the `:default` suffix, they override the passwd file fields. This file can be used as a passdb: ``` user:{plain}password user2:{plain}password2 ``` A passdb with extra fields: ``` user:{plain}password::::::allow_nets=192.168.0.0/24 ``` This file can be used as both a passwd and a userdb: ``` user:{plain}pass:1000:1000::/home/user::userdb_mail_path=~/Maildir allow_nets=192.168.0.0/24 user2:{plain}pass2:1001:1001::/home/user2 ``` ## FreeBSD /etc/master.passwd as passdb and userdb On FreeBSD, `/etc/passwd` doesn't work as a password database because the password field is replaced by a `*`. `/etc/master.passwd` can be converted into a format usable by passwd-file. As [PAM authentication database](/main/core/config/auth/databases/pam.html) can access the system-wide credentials on FreeBSD, what follows is generally needed only if the mail accounts are different from the system accounts. If only using the result for `name:password:uid:gid` and not using [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields), you may be able to use the extract directly. However, the Linux-style passwd file has fewer fields than that used by FreeBSD and it will need to be edited if any fields past the first four are needed. In particular, it will fail if used directly as a `userdb` as the field used for `home` is not in the same place as expected by the Dovecot parser. The `:class:change:expire` stanza in each line should be removed to be consistent with the Linux-style format. While that stanza often is `::0:0` use of `cut` is likely much safer than sed or other blind substitution. In `/etc/master.passwd`, a password of `*` indicates that password authentication is disabled for that user and the token `*LOCKED*` prevents all login authentication, so you might as well exclude those: sh ``` fgrep -v '*' /etc/master.passwd | cut -d : -f 1-4,8-10 > /path/to/file-with-encrypted-passwords chmod 640 /path/to/file-with-encrypted-passwords chown root:dovecot /path/to/file-with-encrypted-passwords ``` The following will work in many situations, after disabling the inclusion of other `userdb` and `passdb` sections: doveconf ``` passdb passwd-file { auth_username_format = %{user | username} passwd_file_path = /path/to/file-with-encrypted-passwords } userdb passwd-file { auth_username_format = %{user | username} passwd_file_path = /path/to/file-with-encrypted-passwords } ``` # Passwd (`passwd`) User is looked up using `getpwnam()` call, which usually looks into `/etc/passwd` file, but depending on the NSS configuration it may also look up the user from, e.g., LDAP database. Most commonly used as a [userdb](/main/core/config/auth/userdb.html). The lookup is by default done in the auth worker processes. If you have only a small local passwd file, you can avoid having extra auth worker processes by disabling it: doveconf ``` userdb passwd { use_worker = yes } ``` ## Field Overriding and Extra Fields It's possible to override fields from passwd and add [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). For example: doveconf ``` userdb passwd { fields { home = /var/mail/%{user | username} mail_driver = maildir mail_path = /var/mail/%{user | username}/Maildir } } ``` This uses the UID and GID fields from passwd, but home directory is overridden. Also the default [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) setting is overridden. INFO `[userdb_fields_import_all](/main/core/summaries/settings.html#userdb_fields_import_all)` defaults to `yes`. If it is set to `no` the fields to be imported need to be explicitly defined. doveconf ``` userdb passwd { fields_import_all = no fields { uid = %{passwd:uid} gid = %{passwd:gid} home = /var/mail/%{user | username} mail_driver = maildir mail_path = /var/mail/%{user | username}/Maildir } } ``` ## Passwd as a passdb Many systems use shadow passwords nowadays so passwd doesn't usually work as a password database. BSDs are an exception to this, they still set the password field even with shadow passwords. With FreeBSD, passwd doesn't work as a password database because the password field is replaced by a `*`. But you can use [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) instead. # Prefetch User Database (`prefetch`) Prefetch [userdb](/main/core/config/auth/userdb.html) can be used to combine passdb and userdb lookups into a single lookup. It's usually used with [SQL authentication database](/main/core/config/auth/databases/sql.html) and [LDAP authentication](/main/core/config/auth/databases/ldap.html). Prefetch works by requiring that the passdb returns the userdb information in [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) with `userdb_` prefixes. For example if a userdb typically returns `uid`, `gid`, and `home` fields, the passdb would have to return `userdb_uid`, `userdb_gid` and `userdb_home` fields. If you're using [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) you still need a valid userdb which can be used to locate the users. You can do this by adding a normal SQL/LDAP userdb **after the userdb prefetch**. The order of definitions is significant. See below for examples. ## LDAP `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)` with `[passdb_ldap_bind_userdn](/main/core/summaries/settings.html#passdb_ldap_bind_userdn)`\-template is incompatible with prefetch, because no passdb lookup is done then. If you want zero LDAP lookups, you might want to use [static authentication database](/main/core/config/auth/databases/static.html) instead of prefetch. ### Example dovecot.conf doveconf ``` passdb ldap { ... fields { user = %{ldap:uid} password = %{ldap:userPassword} userdb_home = %{ldap:homeDirectory} userdb_uid = %{ldap:uidNumber} userdb_gid = %{ldap:gidNumber} } } userdb prefetch { driver = prefetch } # The userdb below is used only by LDA. userdb ldap { ... fields { home = %{ldap:homeDirectory} uid = %{ldap:uidNumber} gid = %{ldap:gidNumber} } } ``` ## SQL ### Example doveconf ``` sql_driver = mysql mysql localhost { } passdb sql { query = SELECT userid AS user, password, home AS userdb_home, uid AS userdb_uid, gid AS userdb_gid \ FROM users \ WHERE userid = '%{user}' } } userdb prefetch { } # The userdb below is used only by lda. userdb sql { query = SELECT home, uid, gid FROM users WHERE userid = '%{user}' } ``` # SQL Database (`sql`) SQL can be used for both [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) lookups. If all the SQL settings for the [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) are equal, only one SQL connection is used for both [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) lookups. ## Dovecot Configuration doveconf ``` # sql driver-specific settings passdb sql { sql_driver = # ... query = # ... } ``` ## passdb `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)` setting contains the SQL query to look up the password. It must return a field named `password`. If you have it by any other name in the database, you can use the SQL's `AS` keyword (`SELECT pw AS password ..`). You can use all the normal `[Settings variables](/main/core/settings/variables.html)` such as `%{user}` in the SQL query. If all the passwords are in same format, you can use `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` to specify it. Otherwise each password needs to be prefixed with `{password-scheme}`, for example `{plain}cleartext-password`. See [Password Schemes](/main/core/config/auth/schemes.html) for a list of supported password schemes. By default MySQL does case-insensitive string comparisons, so you may have a problem if your users are logging with different as `user`, `User` and `uSer`. To fix this, you can make the SQL database return a [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) which makes Dovecot modify the username to the returned value. INFO If you're using separate user and domain fields, a common problem is that you're returning only the `user` field from the database. **This drops out the domain from the username**. So make sure you're returning a concatenated `user@domain` string or username/domain fields separately. See the examples below. The query can also return other [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) which have special meaning. You can't use multiple statements in one query, but you could use a stored procedure. If you want something like a last login update, use [post-login scripting](/main/core/config/post_login_scripting.html) instead. ### Password Verification by SQL Server If the passwords are in some special format in the SQL server that Dovecot doesn't recognize, it's still possible to use them. Change the SQL query to return NULL as the password and return the row only if the password matches. You'll also need to return a non-NULL `nopassword` field. The password is in `%{password}` variable. For example: doveconf ``` passdb sql { query = SELECT NULL AS password, 'Y' as nopassword, userid AS user \ FROM users \ WHERE userid = '%{user}' AND mysql_pass = password('%{password}') } ``` This of course makes the verbose logging a bit wrong, since password mismatches are also logged as `unknown user`. ## userdb Usually your SQL database contains also the userdb information. This means user's UID, GID, and home directory. If you're using only static UID and GID, and your home directory can be specified with a template, you could use [static authentication database](/main/core/config/auth/databases/static.html) instead. It is also a bit faster since it avoids doing the userdb SQL query. `[userdb_sql_query](/main/core/summaries/settings.html#userdb_sql_query)` setting contains the SQL query to look up the userdb information. The commonly returned userdb fields are uid, gid, home, and mail. See [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) for more information about these and other fields that can be returned. If you're using a single UID and GID for all users, you can set them in dovecot.conf with: doveconf ``` mail_uid = vmail mail_gid = vmail ``` ## User Iteration Some commands, such as `doveadm -A` need to get a list of users. With SQL userdb this is done with the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting. You can either return: - `user` field containing either user or user@domain style usernames, or - `username` and `domain` fields Any other fields are ignored. ## Prefetching If you want to avoid doing two SQL queries when logging in with IMAP/POP3, you can make the `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)` return all the necessary userdb fields and use [prefetch authentication database](/main/core/config/auth/databases/prefetch.html) to use those fields. If you're using Dovecot's deliver you'll still need to have the `[userdb_sql_query](/main/core/summaries/settings.html#userdb_sql_query)` working. ## Worker processes MySQL and SQLite lookups are always done using `auth-worker` processes. Changed: 2.4.1 PostgreSQL and Cassandra lookups are done by default in the main `auth` process. They can be made to use `auth-worker` processes by setting `[passdb_use_worker = yes](/main/core/summaries/settings.html#passdb_use_worker)` and `[userdb_use_worker = yes](/main/core/summaries/settings.html#userdb_use_worker)`. ## High Availability You can add multiple [MySQL/MariaDB Configuration](/main/core/config/sql/mysql.html) or [PostgreSQL Configuration](/main/core/config/sql/postgresql.html) settings to specify multiple hosts for MySQL and PostgreSQL. Dovecot will do round robin load balancing between them. If one of them goes down, the others will handle the traffic. ## Examples INFO `user` can have a special meaning in some SQL databases, so we're using `userid` instead. SQL table creation command: sql ``` CREATE TABLE users ( userid VARCHAR(128) NOT NULL, domain VARCHAR(128) NOT NULL, password VARCHAR(64) NOT NULL, home VARCHAR(255) NOT NULL, uid INTEGER NOT NULL, gid INTEGER NOT NULL ); ``` ### MySQL/MariaDB Add to your `dovecot.conf` file: doveconf ``` sql_driver = mysql # The mysqld.sock socket may be in different locations in different systems. mysql /var/run/mysqld/mysqld.sock { user = admin password = pass dbname = mails #ssl = yes #ssl_client_ca_dir = /etc/ssl/certs } # Alternatively you can connect to localhost as well: #mysql localhost { #} passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } ``` ### PostgreSQL Add to your `dovecot.conf` file: doveconf ``` sql_driver = pgsql pgsql localhost { parameters { user = admin # You can also set up non-password authentication by modifying PostgreSQL's # pg_hba.conf password = pass dbname = mails } } passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } ``` ### SQLite Add to your `dovecot.conf` file: doveconf ``` sql_driver = sqlite sqlite_path = /path/to/sqlite.db passdb sql { query = SELECT userid AS username, domain, password \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT home, uid, gid \ FROM users \ WHERE userid = '%{user | username}' AND domain = '%{user | domain}' # For using doveadm -A: iterate_query = SELECT userid AS username, domain FROM users } ``` # Static Password Database (`static`) ## passdb Static password database is typically used only for testing, proxying setups, and perhaps some other special kind of setups. DANGER **Static passdb allows users to log in with any username.** For password you can either set: - `[passdb_static_password](/main/core/summaries/settings.html#passdb_static_password)` or - `[passdb_fields = nopassword=yes](/main/core/summaries/settings.html#passdb_fields)`. You can return any other [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). You can use `[Settings variables](/main/core/settings/variables.html)` everywhere. ### Example without passwordwith password ``` passdb static { fields { nopassword = yes proxy = yes host = 127.0.0.1 } } ``` ``` passdb static { password = secret fields { proxy = yes host = 127.0.0.1 } } ``` ## userdb Static user database can be used when you want to use only single UID and GID values for all users, and their home directories can be specified with a simple template. The syntax is: doveconf ``` userdb static { fields { uid = gid = home = } } ``` The home is optional. You can also return other [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). You can use `[Settings variables](/main/core/settings/variables.html)` everywhere. For the above 3 fields a static userdb isn't actually necessary at all. Instead, you can just leave out the userdb configuration and set: - `[mail_uid](/main/core/summaries/settings.html#mail_uid)` - `[mail_gid](/main/core/summaries/settings.html#mail_gid)` - `[mail_home](/main/core/summaries/settings.html#mail_home)` ### LDA and passdb Lookup for User Verification Unless your MTA already verifies that the user exists before calling dovecot-lda, you'll most likely want dovecot-lda itself to verify the user's existence. Since dovecot-lda looks up the user only from the userdb, it of course doesn't work with static userdb because there is no list of users. Normally static userdb handles this by doing a passdb lookup instead. This works with most passdbs, with [PAM authentication database](/main/core/config/auth/databases/pam.html) being the most notable exception. If you want to avoid this user verification, you can add `[userdb_static_allow_all_users = yes](/main/core/summaries/settings.html#userdb_static_allow_all_users)` to the settings of the userdb in which case the passdb lookup is skipped. ### Example doveconf ``` userdb static { fields { uid = 500 gid = 500 home = /home/%{user} } } ``` # Master Users/Passwords It is possible to configure "master" users who are able to log in as other users. It's also possible to directly log in as any user using a master password. ## Master Users There are two ways for master users to log in as other users: 1. Give the login username in the [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) authorization ID field. 2. Specify both the master username and the login username in the same username field. See `[auth_master_user_separator](/main/core/summaries/settings.html#auth_master_user_separator)` for the format of the string. Master users are configured by adding a new [passdb](/main/core/config/auth/passdb.html) with `master=yes` setting. The users in the master passdb cannot log in as themselves, only as other people. That means they don't need to exist in the [userdb](/main/core/config/auth/userdb.html), because the userdb lookup is done only for the user they're logging in as. You should also add the `result_success=continue` setting to the master passdb if possible. It means that Dovecot verifies that the login user really exists before allowing the master user to log in. Without the setting, if a nonexistent login username is given, depending on the configuration, it could either return an internal login error (the userdb lookup failed) or create a whole new user (with, e.g., [static authentication database](/main/core/config/auth/databases/static.html)). `result_success=continue` doesn't work with PAM or LDAP without `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`, because both of them require knowing the user's password. If you want master users to be able to log in as themselves, you'll need to either add the user to the normal passdb or add the passdb to dovecot.conf twice, with and without `master=yes`. INFO If the passdbs point to different locations, the user can have a different password when logging in as other users than when logging in as himself. This is a good idea since it can avoid accidentally logging in as someone else. Usually it's better to have **only** a few special master users that are used only to log in as other people. One example could be a special "spam" master user that trains the users' spam filters by reading the messages from the user's spam mailbox. ### ACLs If [acl plugin](/main/core/plugins/acl.html) plugin is enabled, the master user is still subject to ACLs just like any other user, which means that by default the master user has no access to any mailboxes of the user. The options for handling this are: 1. Adding a global [ACL](/main/core/plugins/acl.html) for the master user. You can create a `default ACL`, that applies to all mailboxes. See example below. 2. Set `[acl_user = %{user}](/main/core/summaries/settings.html#acl_user)`. This preserves the master\_user for other purposes (e.g. `%{master_user}` variable). 3. Change userdb to return `userdb_fields { master_user=%{user} }`. This fully hides that master user login is being used. Example configuration: doveconf ``` auth_master_user_separator = * passdb passwd-file { passwd_file_path = /etc/dovecot/passwd.masterusers master = yes result_success = continue } userdb passwd { } ``` To grant the master user access to all Mailboxes, the `dovecot-acl` file can contain: ``` * user=masteruser lr ``` Where the `passwd.masterusers` file would contain the master usernames and passwords: ``` admin:{SHA1}nU4eI71bcnBGqeO0t9tXvY1u5oQ= admin2:{SHA1}i+UhJqb95FCnFio2UdWJu1HpV50= ``` One way to create this master file is to use the htaccess program as follows: sh ``` htpasswd -b -c -s passwd.masterusers user password ``` ### SQL Example The master passdb doesn't have to be passwd-file, it could be an SQL query as well: doveconf ``` sql_driver = mysql mysql localhost { } auth_master_user_separator = * passdb db1 { driver = sql query = SELECT password FROM users WHERE userid = '%{user}' and master_user = true master = yes result_success = continue } passdb db2 { driver = sql query = # ... } userdb sql { query = # ... } ``` ### Testing ``` # nc localhost 143 * OK Dovecot ready. 1 login loginuser*masteruser masterpass 1 OK Logged in. ``` If you had any problems, set `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)` and look at the logs. ## Master Passwords You can configure a passdb which first performs authentication using the master password. Then it continues to the primary passdb to verify that the user exists and get other extra fields. doveconf ``` # master password passdb passdb static { password = master-password result_success = continue } # primary passdb passdb pam { } ``` ### Advanced SQL Examples For these examples, we will create 3 kinds of master users: - Users who can read all email for all domains - Users who can read all email for their domain only - Users who can read email of domains listed in a separate ownership table. We will use MySQL and create 2 tables with the following structure. sql ``` CREATE TABLE `users` ( `uid` int(4) NOT NULL AUTO_INCREMENT, `user_name` varchar(80) NOT NULL, `domain_name` varchar(80) NOT NULL, `password` varchar(60) DEFAULT NULL, `last_login` datetime DEFAULT NULL, `masteradmin` tinyint(1) NOT NULL DEFAULT '0', `owns_domain` tinyint(1) NOT NULL DEFAULT '0', UNIQUE KEY `emaillookup` (`domain_name`,`user_name`), UNIQUE KEY `uid` (`uid`) ) ENGINE=InnoDB AUTO_INCREMENT=995 DEFAULT CHARSET=utf8 CREATE TABLE `ownership` ( `login_id` varchar(128) NOT NULL, `owned_object` varchar(128) NOT NULL, UNIQUE KEY `login_id_full` (`login_id`,`owned_object`), KEY `login_id` (`login_id`), KEY `owned_object` (`owned_object`), KEY `login_id_index` (`login_id`), KEY `owned_object_index` (`owned_object`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ``` The `dovecot.conf` file for all 3 master user configurations will be as follows: doveconf ``` passdb db1 { driver = sql args = /etc/dovecot/ownership-sql.conf master = yes result_success = continue } passdb db2 { driver = sql args = /etc/dovecot/domain-owner-sql.conf master = yes result_success = continue } passdb db3 { driver = sql args = /etc/dovecot/masteradmin-sql.conf master = yes result_success = continue } passdb db4 { driver = sql args = /etc/dovecot/sql.conf } ``` Before we get into the master user tricks, we start with normal email authentication. The query for that is as follows: doveconf ``` passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' } ``` In this first example, suppose you want to allow a few people to be master users over all domains. These users will have the `masteradmin` field set to `1`. The query would be: doveconf ``` passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND masteradmin='1' } ``` In the second example, suppose you are hosting multiple domains and you want to allow a few users to become master users of their domain only. Your query would be as follows: doveconf ``` passdb sql { query = SELECT user_name, domain_name, password \ FROM users \ WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND owns_domain='1' AND '%{user | domain}'='%{login_domain}' } ``` This will allow you to log in using `joe@dovecot.org*master@dovecot.org` to read Joe's email if [master@dovecot.org](mailto:master@dovecot.org) is flagged as the `domain_owner`. In the third example, we have a table of owners. There are a list of pairs between owner email addresses and domains that are owned. That way if a person controls a lot of domains then they can view all the users in all the domains they control. The query would be as follows: doveconf ``` passdb sql { query = SELECT user_name, domain_name, password \ FROM users, ownership \ WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' AND login_id='%{user}' AND owned_object='%{login_domain}' } ``` If you really want to get tricky and efficient you can combine all 3 queries into one giant query that does everything. doveconf ``` passdb sql { query = SELECT user_name, domain_name, password \ FROM users, ownership \ WHERE user_name = '%{user | username}' AND domain_name = '%{user | domain}' \ AND ( (masteradmin='1') OR (owns_domain='1' AND '%{user | domain}'='%{login_domain}') \ OR (login_id='%{user}' and owned_object='%{login_domain}') ) \ GROUP BY uid } ``` # Digest-MD5 Digest-MD5 has two things that make it special and which can cause problems: - Instead of using user@domain usernames, it supports **realms**. - User name and realm are part of the MD5 hash that's used for authentication. For these and other reasons Digest-MD5 has been obsoleted ([RFC 6331](https://datatracker.ietf.org/doc/html/rfc6331)) by SCRAM ([RFC 5802](https://datatracker.ietf.org/doc/html/rfc5802)). ## Realms Realms are an integral part of Digest-MD5. You will need to specify realms you want to advertise to the client in the config file: `[auth_realms = example.com another.example.com foo](/main/core/summaries/settings.html#auth_realms)`. The realms don't have to be domains. All listed realms are presented to the client and it can select to use one of them. Some clients always use the first realm. Some clients use your domain name, whenever given more than one realm to choose from. Even if this was NOT one of the choices you provided (KMail, others?). In both cases the user never sees the advertised realms. WARNING Any settings that modify the username before the passdb lookup (e.g. `[auth_default_domain](/main/core/summaries/settings.html#auth_default_domain)`) will not work with Digest-MD5 password scheme, because the password hash was calculated using the unmodified username. Any username modification will result in hash mismatch. ## DIGEST-MD5 Scheme Password must be stored in either cleartext or with DIGEST-MD5 scheme. See [Password Schemes](/main/core/config/auth/schemes.html). The Digest is the MD5 sum of the string "user:realm:password". For example, if you want to log in as `user` with password `pass` and the realm should be `example.com` (usually not provided by the user, see above), create the digest with: sh ``` echo -n "user:example.com:pass" | md5sum c19c4c6e32f9d8026b26ba77c21fb8eb - ``` And save it as: `user@example.com:c19c4c6e32f9d8026b26ba77c21fb8eb`. Note that if you're using DIGEST-MD5 scheme to store the passwords, you can't change the users' names or realms in any way or the authentication will fail because the MD5 sums don't match. Also not that this is different from what Apache does with HTTP AUTH Digest. There it would be `user:example.com:c19c4c6e32f9d8026b26ba77c21fb8eb` and is created with `htdigest`. ## Testing You can use `imtest` from [Cyrus SASL](https://www.cyrusimap.org/sasl/) library to test an IMAP connection: ``` # With realm: imtest -a user -r example.com # Without realm: imtest -a user@example.com ``` # GSSAPI GSSAPI (Generic Security Services Application Program Interface) is an authentication mechanism that provides mutual authentication using opaque messages (such as tokens). GSSAPI is usually used with Kerberos, and is a good choice when dealing with centralized authentications, like Active Directory or FreeIPA. These instructions cover using Kerberos, and assume that you have a fully functional Kerberos environment. Most importantly, you need to ensure your DNS lookups and reverse DNS lookups work and return correct names. ## Preparations First thing, you need to create Service Principal for Dovecot. This is done with `kadmin` tool. sh ``` kadmin -q 'addprinc -randkey imap/fully.qualified.host@REALM' kadmin -q 'addprinc -randkey IMAP/fully.qualified.host@REALM' ``` To create a keytab, use: sh ``` kadmin -q 'ktadd -k /root/keytab imap/fully.qualified.host@REALM' kadmin -q 'ktadd -k /root/keytab IMAP/fully.qualified.host@REALM' ``` and put the keytab file into `/etc/dovecot`, set mode `0440` and ownership to `root:dovecot`. On Active Directory, you need to create a Service User with password that never expires and cannot be changed, and then use `setspn.exe` to create the service principals for this user. sh ``` setspn -A IMAP/hostname service_user_name setspn -A imap/hostname service_user_name ``` Then you need to use `ktpass` utility to export the keytab. sh ``` ktpass -princ imap/hostname -mapuser service_user_name \ -crypto ALL -ptype KRB5_NT_PRINCIPAL -pass service_user_password \ -target dc.test.com -out c:\share\keytab ktpass -princ IMAP/hostname -mapuser service_user_name -crypto ALL \ -ptype KRB5_NT_PRINCIPAL -pass service_user_password \ -target dc.test.com -out c:\share\keytab ``` Use `klist` on your Dovecot server to verify the keytab contains the expected results: sh ``` klist -k /etc/dovecot/keytab ``` ``` Keytab name: FILE:/etc/krb5.keytab KVNO Principal ---- -------------------------------------------------------------------------- 2 imap/hostname@REALM ``` ## Configuring Dovecot Once you have a keytab, configure GSSAPI on Dovecot. doveconf ``` auth_mechanisms { gssapi = yes } auth_gssapi_hostname = hostname-from-spn # or "$ALL" auth_krb5_keytab = /etc/dovecot/keytab ``` This should enable GSSAPI support for Dovecot. GSSAPI requires at least one passdb configured. Note that authentication will normally succeed, even if user is not found in any passdb. Please configure a userdb if you do not want this. ### Specifying Accepted Credential If passdb lookup succeeds, it can optionally return `k5principals` extra field, which contains comma separated list of identities to accepted for the user. This enables cross-realm and on-behalf authentication. See [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). ### Testing You need an GSSAPI capable client, such as Thunderbird, running on a fully working Kerberos workstation. You can choose GSSAPI as your authentication mechanism and you should be able to log in. If necessary, you can use `[auth_username_format](/main/core/summaries/settings.html#auth_username_format)` to normalize usernames. # Kerberos Authentication Dovecot supports Kerberos 5 using GSSAPI. The Kerberos authentication mechanism doesn't require having a [passdb](/main/core/config/auth/passdb.html), but you do need a [userdb](/main/core/config/auth/userdb.html) so Dovecot can lookup user-specific information, such as where their mailboxes are stored. With centralized systems, such as Microsoft Active Directory, LDAP is a good choice. INFO If you only wish to authenticate clients using their Kerberos _passphrase_ (as opposed to ticket authentication), you will probably want to use [PAM authentication database](/main/core/config/auth/databases/pam.html) with `pam_krb5.so` instead. ## Pre-requisites This document assumes that you already have a Kerberos Realm up and functioning correctly at your site, and that each host in your realm also has a host _keytab_ installed in the appropriate location. For Dovecot, you will need to install the appropriate _service_ keys on your server. By default, Dovecot will look for these in the host's keytab file, typically `/etc/krb5.keytab`, but you can specify an alternate path using `[auth_krb5_keytab](/main/core/summaries/settings.html#auth_krb5_keytab)`. Keytab file should be readable by user "dovecot" (or whatever user the auth process is running as). If you wish to provide an IMAP service, you will need to install a service ticket of the form `imap/hostname@REALM`. For POP3, you will need a service ticket of the form `pop/hostname@REALM`. When using Dovecot's [SASL](/main/core/admin/sasl.html) with [MTA](/main/core/config/delivery/mta.html), you will need to install service ticket of the form `smtp/hostname@REALM`. ## Setting up samba Create symlink for krb5.conf, if you do not have krb5.conf ready: sh ``` ln -sf /usr/local/samba/private/krb5.conf /etc/krb5.conf ``` Create Dovecot user to your samba instance (choose random password): sh ``` samba-tool user create dovecot ``` ``` New Password: Retype Password: User 'dovecot' created successfully ``` Add Service Principal Names (SPNs) and create keytab: sh ``` samba-tool spn add imap/host.domain.com dovecot samba-tool domain exportkeytab --principal imap/host.domain.com /etc/dovecot/dovecot.keytab ``` Dovecot needs to be able to read the keytab: sh ``` chgrp dovecot /etc/dovecot/dovecot.keytab chmod g+r /etc/dovecot/dovecot.keytab ``` Make sure your keytab has entry for `imap/host.domain.name@REALM`: sh ``` klist -Kek /etc/dovecot/dovecot.keytab ``` ``` Keytab name: FILE:/etc/dovecot/dovecot.keytab KVNO Principal ---- -------------------------------------------------------------------------- 1 imap/host.domain.name@REALM (des-cbc-crc) 1 imap/host.domain.name@REALM (des-cbc-md5) 1 imap/host.domain.name@REALM (arcfour-hmac) ``` ## Example Configurations If you only want to use Kerberos ticket-based authentication: doveconf ``` auth_gssapi_hostname = "$ALL" auth_mechanisms = gssapi auth_krb5_keytab = /etc/dovecot/dovecot.keytab userdb static { fields { uid = vmail gid = vmail home = /var/vmail/%{user} } } ``` (In this virtual-hosting example, all mail is stored in `/var/vmail/$username` with uid and gid set to 'vmail') If you also want to support plaintext authentication in addition to ticket-based authentication, you will need something like: doveconf ``` auth_mechanisms = plain login gssapi auth_gssapi_hostname = "$ALL" auth_krb5_keytab = /etc/dovecot/dovecot.keytab passdb pam { } userdb passwd { } ``` INFO In this example, you will also need to configure PAM to use whichever authentication backends are appropriate for your site. ## Enable Plaintext Authentication to use Kerberos This is needed when some of your clients don't support GSSAPI and you still want them to authenticate against Kerberos. Install pam\_krb5 module for PAM, and create `/etc/pam.d/dovecot`: ``` auth sufficient pam_krb5.so account sufficient pam_krb5.so ``` Then enable PAM passdb: doveconf ``` passdb pam { } ``` Check `/var/log/auth.log` if you have any problems logging in. The problem could be that PAM is still trying to use pam\_unix.so rather than pam\_krb5.so. Make sure pam\_krb5.so is the first module for account or just change pam\_unix.so to sufficient. ## Cross-Realm Authentication This seems to have all kinds of trouble. Search Dovecot mailing list for previous threads about it. Some points about it: - `krb5_kuserok()` is used to check if access is allowed. It may try to do the check by reading `~user/.k5login` (good!) or `~dovecot/.k5login` (bad!) - Solaris uses `gss_userok()` instead of `krb5_kuserok()`. See "k5principals" [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) which is a comma separated list of usernames that are allowed to log in. If it's set, it bypasses the `krb5_kuserok()` check. INFO For this to work, you need a password database which supports **credential lookups**. This excludes LDAP databases using authentication binds (`[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`). However, a second LDAP passdb entry without `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)` may be added for the sole purpose of Kerberos principals mapping. This passdb doesn't need to return a password attribute (and usually shouldn't). Authentication-bind LDAP databases are able to provide `k5principals` lookups if configured with `pass_filter`. ## Client Support Mail clients that support Kerberos GSSAPI authentication include: - Evolution - Mozilla Thunderbird - SeaMonkey - Mutt - UW Pine - Apple Mail ## Testing This test demonstrates that the server can acquire its private credentials. You need to configure your server accordingly, and then you can use mutt client to test this. First telnet directly to the server: sh ``` telnet localhost 143 ``` ``` * OK Dovecot ready. ``` or, if you are using IMAPS then use openssl instead of telnet to connect: sh ``` openssl s_client -connect localhost:993 ``` ``` CONNECTED(00000003) ... * OK Dovecot ready. ``` Check that GSSAPI appears in the authentication capabilities: ``` a capability * CAPABILITY ... AUTH=GSSAPI ``` Attempt the first round of GSS communication. The '+' indicates that the server is ready: ``` a authenticate GSSAPI + ``` Abort the telnet session by typing control-\] and then 'close': ``` ^] telnet> close ``` The test: - Setup mutt in /etc/Muttrc to use kerberos using gssapi and imap configuration - This is done with `set imap_authenticators="gssapi"` - run `kinit` (type in password for kerb) - run command `mutt` - If you get error "No Authentication Method" - run command `klist` (list all kerberos keys) should show imap/HOSTNAME - DNS has to function correctly so that kerberos works. # Authentication (SASL) Mechanisms ## Cleartext Authentication The simplest authentication mechanism is PLAIN. The client simply sends the password unencrypted to Dovecot. All clients support the PLAIN mechanism, but obviously there's the problem that anyone listening on the network can steal the password. For that reason (and some others) other mechanisms were implemented. Today however many people use [SSL](/main/core/config/ssl.html), and there's no problem with sending unencrypted password inside SSL secured connections. So if you're using SSL, you probably don't need to bother worrying about anything else than the PLAIN mechanism. Another cleartext mechanism is LOGIN. It's typically used only by SMTP servers to let Outlook clients perform SMTP authentication. Note that LOGIN mechanism is not the same as IMAP's LOGIN command. The LOGIN command is internally handled using PLAIN mechanism. ## Non-Cleartext Authentication Non-cleartext mechanisms have been designed to be safe to use even without [SSL](/main/core/config/ssl.html) encryption. Because of how they have been designed, they require access to the cleartext password or their own special hashed version of it. This means that it's impossible to use non-cleartext mechanisms with password hashes. If you want to use more than one non-cleartext mechanism, the passwords must be stored as cleartext so that Dovecot is able to generate the required special hashes for all the different mechanisms. If you want to use only one non-cleartext mechanism, you can store the passwords using the mechanism's own [Password Schemes](/main/core/config/auth/schemes.html). With success/failure password databases (see [passdb](/main/core/config/auth/passdb.html), e.g. [PAM authentication database](/main/core/config/auth/databases/pam.html)), it's not possible to use non-cleartext mechanisms at all, because they only support verifying a known cleartext password. ### Dovecot Support | Mechanism | Summary | | --- | --- | | CRAM-MD5 | Protects the password in transit against eavesdroppers. Somewhat good support in clients. | | [DIGEST-MD5](/main/core/config/auth/mechanisms/digest_md5.html) | Somewhat stronger cryptographically than CRAM-MD5, but clients rarely support it. | | SCRAM-SHA-1 | Salted Challenge Response Authentication Mechanism (SCRAM) SAS and GSS-API Mechanisms. Intended as DIGEST-MD5 replacement. [RFC 5802](https://datatracker.ietf.org/doc/html/rfc5802) | | SCRAM-SHA-1-PLUS | SCRAM-SHA-1 with channel binding support [RFC 5802](https://datatracker.ietf.org/doc/html/rfc5802). | | SCRAM-SHA-256 | Stronger replacement for SCRAM-SHA-1 [RFC 7677](https://datatracker.ietf.org/doc/html/rfc7677). | | SCRAM-SHA-256-PLUS | SCRAM-SHA-256 with channel binding support [RFC 7677](https://datatracker.ietf.org/doc/html/rfc7677). | | APOP | This is a POP3-specific authentication. Similar to CRAM-MD5, but requires storing password in cleartext. | | [GSS-SPNEGO](/main/core/config/auth/mechanisms/gssapi.html) | A wrapper mechanism defined by [RFC 4178](https://datatracker.ietf.org/doc/html/rfc4178). Can be accessed via GSSAPI. | | [GSSAPI](/main/core/config/auth/mechanisms/gssapi.html) | Kerberos v5 support. | | ANONYMOUS | Support for logging in anonymously. This may be useful if you're intending to provide publicly accessible IMAP archive. | | OTP | Removed: 2.4.5 One time password mechanisms. | | EXTERNAL | EXTERNAL SASL mechanism. | | [OAUTHBEARER](/main/core/config/auth/databases/oauth2.html) | OAuth2 bearer authentication [RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628). | | [XOAUTH2](/main/core/config/auth/databases/oauth2.html) | [Google flavor OAUTHBEARER](https://developers.google.com/gmail/imap/xoauth2-protocol) | ## Configuration By default only PLAIN mechanism is enabled. To use more, see `[auth_mechanisms](/main/core/summaries/settings.html#auth_mechanisms)`. # Winbind Mechanisms Dovecot supports NTLM and GSS-SPNEGO authentication mechanisms using [Samba](https://www.samba.org)'s winbind daemon. It is useful when you need to authenticate users against a Windows domain (either AD or NT). By default NTLM mechanism is handled internally. You can use winbind instead by setting `[auth_use_winbind = yes](/main/core/summaries/settings.html#auth_use_winbind)`. The usernames, returned by winbind, can contain some domain part (either "DOMAIN\\user" or "[user@example.com](mailto:user@example.com)"). Such usernames are always transformed to the form of "user@domain". To strip domain part (to obtain corresponding local username, for example), set `[auth_username_format = %{user | username}](/main/core/summaries/settings.html#auth_username_format)`. Dovecot needs path to Samba's `ntlm_auth` binary to perform the authentication. You can change the path with `[auth_winbind_helper_path = /usr/bin/ntlm_auth](/main/core/summaries/settings.html#auth_winbind_helper_path)`. Dovecot currently does blocking lookups, so if `ntlm_auth` is slow on responding (e.g. network problems), Dovecot blocks all other authentication requests until it's finished. # Multiple Authentication Databases Dovecot supports defining multiple authentication databases, so that if the password doesn't match in the first database, it checks the next one. This can be useful if you want to easily support having both local system users in `/etc/passwd` and virtual users. Currently the fallback works only with the PLAIN authentication mechanism. Often you also want a different mail location for system and virtual users. The best way to do this would be to always have mails stored below the home directory. - System users' mails: /home/user/Maildir - Virtual users' mails: /var/vmail/domain/user/Maildir This can be done by simply having both system and virtual userdbs return home directory properly (i.e. virtual users' `home=/var/vmail/%{user | domain}/%{user | username}`) and then set `[mail_path = ~/Maildir](/main/core/summaries/settings.html#mail_path)`. If it's not possible to have a home directory for virtual users (avoid that if possible), you can do this by pointing multiple authentication databases to system users' mail location and have the virtual userdb override it by returning mail [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). ## Example with Home Dirs - System users' mails: /home/user/Maildir - Virtual users' mails: /var/vmail/domain/user/Maildir dovecot.conf doveconf ``` # Mail location for both system and virtual users: mail_driver = maildir mail_path = ~/Maildir sql_driver = mysql mysql localhost { } # try to authenticate using SQL database first passdb sql { query = SELECT userid AS user, password FROM users WHERE userid = '%{user}' } # fallback to PAM passdb pam { } # look up users from SQL first (even if authentication was done using PAM!) userdb sql { query = SELECT uid, gid, '/var/vmail/%{user | domain}/%{user | username}' AS home FROM users WHERE userid = '%{user}' } # if not found, fallback to /etc/passwd userdb passwd { } ``` # Authentication Dovecot authentication is split into four parts: 1. [Authentication Mechanisms](/main/core/config/auth/mechanisms/overview.html) 2. [Password Schemes](/main/core/config/auth/schemes.html) 3. [Password Databases (passdb)](/main/core/config/auth/passdb.html) - [passdb Extra Fields](/main/core/config/auth/passdb.html#extra-fields) 4. [User Databases (userdb)](/main/core/config/auth/userdb.html) - [User Databases (userdb)](/main/core/config/auth/userdb.html) For authentication policy topics, see also: - [Authentication Penalty](/main/core/config/auth/penalty.html) - [Authentication Policy](/main/core/config/auth/policy.html) ## Cleartext Mechanisms A cleartext mechanism is an authentication mechanism that contains users' passwords or credentials in non-encrypted and non-hashed format. For example, PLAIN, LOGIN or XOAUTH2 mechanisms contain credentials which an attacker can use to authenticate if they are captured. To protect against this, connection encryption with TLS (or some other mechanism) is required by default. See `[auth_allow_cleartext](/main/core/summaries/settings.html#auth_allow_cleartext)` for removing this requirement. ## Authentication Mechanisms vs. Password Schemes Authentication mechanisms and password schemes are often confused, because they have somewhat similar values. For example there is a PLAIN auth mechanism and PLAIN password scheme. But they mean completely different things. - **Authentication mechanism is a client/server protocol**. It's about how the client and server talk to each others in order to perform the authentication. Most people use only PLAIN authentication, which basically means that the user and password are sent without any kind of encryption to the server. SSL/TLS can then be used to provide the encryption to make PLAIN authentication secure. **Password scheme is about how the password is hashed in your password database**. If you use a PLAIN scheme, your passwords are stored in cleartext without any hashing in the password database. A popular password scheme MD5-CRYPT (also commonly used in `/etc/shadow`) where passwords looks like `$1$oDMXOrCA$plmv4yuMdGhL9xekM.q.I/`. - Cleartext authentication mechanisms work with ALL password schemes. - Non-cleartext authentication mechanisms require either PLAIN password scheme or a mechanism-specific password scheme. ## Debugging Set `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)` which makes Dovecot log a debug line for just about anything related to authentication. If you're having problems with passwords, you can also set `[auth_debug_passwords = yes](/main/core/summaries/settings.html#auth_debug_passwords)` which will log them in cleartext. After that you'll see in the logs exactly what dovecot-auth is doing, and that should help you to fix the problem. For easily testing authentication, use `[doveadm auth test user@domain password]()`. For looking up userdb information for a user, use `[doveadm user user@domain](/main/core/summaries/doveadm.html#user)`. For simulating a full login with both passdb and userdb lookup, use `[doveadm auth login user@domain password]()`. ### PLAIN SASL Mechanism With IMAP and POP3, it's easy to log in manually using the IMAP's LOGIN command or POP3's USER and PASS commands (see [testing Dovecot installation](/main/core/admin/testing.html) for details), but with SMTP AUTH you'll need to use PLAIN authentication mechanism, which requires you to build a base64-encoded string in the correct format. The PLAIN authentication is also used internally by both IMAP and POP3 to authenticate to dovecot-auth, so you see it in the debug logs. The PLAIN mechanism's authentication format is: ` NUL NUL `. Authorization ID is the username who you want to log in as, and authentication ID is the username whose password you're giving. If you're not planning on doing a [master user login](/main/core/config/auth/master_users.html), you can either set both of these fields to the same username, or leave the authorization ID empty. #### Encoding with mmencode printf(1) and mmencode(1) should be available on most Unix or GNU/Linux systems. (If not, check with your distribution. GNU coreutils includes printf(1), and metamail includes mmencode(1). In Debian, mmencode is called mimencode(1).) sh ``` printf 'username\0username\0password' | mmencode ``` ``` dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ= ``` This string is what a client would use to attempt PLAIN authentication as user `username` with password `password`. With `[auth_debug_passwords = yes](/main/core/summaries/settings.html#auth_debug_passwords)`, it would appear in your logs. #### Decoding with mmencode You can use `mmencode -u` to interpret the encoded string pasted into stdin: sh ``` mmencode -u ``` ``` bXl1c2VybmFtZUBkb21haW4udGxkAG15dXNlcm5hbWVAZG9tYWluLnRsZABteXBhc3N3b3Jk myusername@domain.tldmyusername@domain.tldmypassword # ``` You should see the correct user address (twice) and password. The null bytes won't display. #### Encoding with Perl Unfortunately, mmencode on FreeBSD chokes on `\0`. As an alternate, if you have MIME::Base64 on your system, you can use a perl statement to do the same thing: sh ``` perl -MMIME::Base64 -e 'print encode_base64("myusername\@domain.tld\0myusername\@domain.tld\0mypassword");' ``` As `mmencode -u` doesn't encounter any `\0` you can still do: sh ``` perl -MMIME::Base64 -e 'print encode_base64("myusername\@domain.tld\0myusername\@domain.tld\0mypassword");' | mmencode -u ``` to check that you have encoded correctly. #### Encoding with Python With python you can do: sh ``` python -c "import base64; print(base64.encodestring('myusername@domain.tld\0myusername@domain.tld\0mypassword'));" ``` # Password Databases (`passdb`) Dovecot uses `passdb` and [userdb](/main/core/config/auth/userdb.html) as part of the authentication process. `passdb` authenticates the user. It also provides any other pre-login information needed for users, such as: - Which server user is proxied to. - If user should be allowed to log in at all (temporarily or permanently). | Passdb Lookups | Dovecot Proxy | Dovecot Backend | | --- | --- | --- | | IMAP & POP3 logins | YES | YES | | LMTP mail delivery | YES | YES | | doveadm commands | YES | YES | See also [userdb](/main/core/config/auth/userdb.html). ## Authentication Databases See [Authentication Databases](/main/core/config/auth/databases/overview.html). ## Passwords The password can be in any format that Dovecot supports, but you need to tell the format to Dovecot because it won't try to guess it. The SQL and LDAP configuration files have the `default_pass_scheme` setting for this. If you have passwords in multiple formats, or the `passdb` doesn't have such a setting, you'll need to prefix each password with `{}`: `{PLAIN}cleartext-password` or `{PLAIN-MD5}1a1dc91c907325c69271ddf0c944bc72`. Dovecot authenticates users against password databases. It can also be used to configure things like [proxy passdb](/main/core/config/proxy/overview.html). ## Multiple Passdbs You can use multiple databases, so if the password doesn't match in the first database, Dovecot checks the next one. This can be useful if you want to easily support having both virtual users and also local system users (see [Multiple Authentication Databases](/main/core/config/auth/multiple.html)). You can control the behavior of what happens after a passdb lookup is successful or unsuccessful with settings: - `[passdb_result_success](/main/core/summaries/settings.html#passdb_result_success)` - `[passdb_result_failure](/main/core/summaries/settings.html#passdb_result_failure)` - `[passdb_result_internalfail](/main/core/summaries/settings.html#passdb_result_internalfail)` When using passdbs inside a `protocol ... { ... }` filter, the protocol-specific passdbs are executed first, and only then followed by the globally defined passdbs. Currently it's not possible to configure passdbs inside other filters. ## Success/Failure Database These **databases** simply verify if the given password is correct for the user. Dovecot doesn't get the correct password from the database, it only gets a `success` or a `failure` reply. This means that these databases can't be used with non-cleartext [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html). Databases that belong to this category are: | Database | Description | | --- | --- | | [PAM](/main/core/config/auth/databases/pam.html) | Pluggable Authentication Modules. | | [IMAP](/main/core/config/auth/databases/imap.html) | Authenticate against remote IMAP server. | | [OAuth2](/main/core/config/auth/databases/oauth2.html) | Authenticate against OAuth2 provider. | | [BSDAuth](/main/core/config/auth/databases/bsd.html) | BSD authentication (deprecated, unsupported). | ## Lookup Database Dovecot does a lookup based on the username and possibly other information (e.g. IP address) and verifies the password validity itself. Databases that support looking up only passwords, but no user or extra fields: | Database | Description | | --- | --- | | [Passwd](/main/core/config/auth/databases/passwd.html) | System users (NSS, `/etc/passwd`, or similar). | INFO Dovecot supports reading all [Password Schemes](/main/core/config/auth/schemes.html) from passwd databases (if prefix is specified), but that is of course incompatible with all other tools using/modifying the passwords. Databases that support looking up everything: | Database | Description | | --- | --- | | [Passwd-file](/main/core/config/auth/databases/passwd_file.html) | `/etc/passwd`\-like file. | | [LDAP](/main/core/config/auth/databases/ldap.html) | Lightweight Directory Access Protocol. | | [SQL](/main/core/config/auth/databases/sql.html) | SQL database (PostgreSQL, MySQL, SQLite, Cassandra). | | [Static](/main/core/config/auth/databases/static.html) | Static `passdb` for simple configurations. | | [Lua](/main/core/config/auth/databases/lua.html) | Lua script for authentication. | ### Fields Fields that the lookup can return: #### `password` User's password. See [Password Schemes](/main/core/config/auth/schemes.html). #### `password_noscheme` Like `password`, but if a password begins with `{`, assume it belongs to the password itself instead of treating it as a [Password Schemes](/main/core/config/auth/schemes.html) prefix. This is usually needed only if you use cleartext passwords. #### `user` Returning a user field can be used to change the username. Typically used only for case changes (e.g. `UseR` -> `user`). See [passdb: User Extra Fields](/main/core/config/auth/passdb.html#user-extra-fields). #### `username` Like `user`, but doesn't drop existing domain name (e.g. `username=foo` for `user@domain` gives `foo@domain`). #### `domain` Updates the domain part of the username. #### User Extra Fields Other special [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). ## Settings ### `passdb` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` **See Also:** - [passdb](/main/core/config/auth/passdb.html) - `[passdb_name](/main/core/summaries/settings.html#passdb_name)` - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` Creates a new [passdb](/main/core/config/auth/passdb.html). The filter name refers to the `[passdb_name](/main/core/summaries/settings.html#passdb_name)` setting. ### `passdb_default_password_scheme` **Default:** `CRYPT` specific [passdb](/main/core/config/auth/passdb.html) have different defaults `passdb_static`: `PLAIN` **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Changed: 2.4.3 Changed from `PLAIN` to `CRYPT`. The scheme that passwords are in the passdb, unless overridden by the passdb entry (typically by prefixing with `{SCHEME}`). ### `passdb_deny` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes` and the user is found from the `denied user database` the authentication will fail. ### `passdb_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[passdb_name](/main/core/summaries/settings.html#passdb_name)` - [passdb](/main/core/config/auth/passdb.html) - [Authentication Databases](/main/core/config/auth/databases/overview.html) The driver used for this password database. If empty, defaults to `[passdb_name](/main/core/summaries/settings.html#passdb_name)`. ### `passdb_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) Passdb fields (and [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields)). The values can contain [%variables](/main/core/settings/variables.html). All `%variables` used here reflect the state **after** the current passdb lookup, and can refer to fields returned by previous passdb lookups. Depending on the passdb driver, it can also refer to variable fields returned by it (e.g. `%{ldap:fieldName}`). INFO The LDAP driver provides additional specific variables, see [LDAP authentication](/main/core/config/auth/databases/ldap.html) for more details. For example: ``` passdb ldap { fields { user = %{ldap:userId} proxy = yes host = %{ldap:proxyHost} } } ``` ### `passdb_fields_import_all` **Default:** `yes` For `passdb ldap` the default is `no`. **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` If yes, import all fields returned by the passdb lookup. If no, require `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` to explicitly add wanted fields. ### `passdb_master` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes` and the user is found from the [Master Users](/main/core/config/auth/master_users.html) the user is allowed to login as other users. ### `passdb_mechanisms_filter` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Added: 2.4.0 Skip the passdb if non-empty and the current auth mechanism is not listed here. If the value contains `lookup`, it matches for non-authenticating passdb lookups (e.g. lmtp/doveadm lookups). Example: ``` passdb passwd-file { driver = passwd-file mechanisms_filter = PLAIN LOGIN # ... } ``` ### `passdb_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[passdb](/main/core/summaries/settings.html#passdb)` - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` Name of the passdb. The `[passdb](/main/core/summaries/settings.html#passdb)` filter name refers to this setting. If the `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` setting is empty, the `passdb_name` is used as the driver. This allows doing e.g.: ``` passdb passwd-file { passwd_file_path = /etc/dovecot/passwd ``` ### `passdb_pam_failure_show_msg` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Replace the default `Authentication failed` reply with PAM's failure. ### `passdb_pam_max_requests` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` Number of requests a auth-worker process handles for passdb pam before it dies. This configures similar behaviour as the `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` setting but it limits only the number of pam passdb requests, not all requests to be handled by an auth-worker. ### `passdb_pam_service_name` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) The PAM service name to be used with the pam passdb. ### `passdb_pam_session` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot open a PAM session and close it immediately. ### `passdb_pam_setcred` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot create PAM credentials. The credentials are never deleted, which may cause problems with some PAM plugins. ### `passdb_result_failure` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication failed. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). ### `passdb_result_internalfail` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication failed due to an internal error. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). If any of the passdbs had an internal failure and the final passdb also returns `continue` the authentication will fail with `internal error`. ### `passdb_result_success` **Default:** `return-ok` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication succeeded. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). This is commonly used together with master passdb to specify that even after a successful master user authentication, the authentication should continue to the actual non-master passdb to lookup the user. ### `passdb_skip` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``authenticated``unauthenticated` Configures when passdbs should be skipped: | Value | Description | | --- | --- | | `never` | Never skip over this passdb. | | `authenticated` | Skip if an earlier passdb already authenticated the user successfully. | | `unauthenticated` | Skip if user hasn't yet been successfully authenticated by the previous passdbs. | ### `passdb_sql_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to lookup the passdb fields (`password` and other extra fields). ### `passdb_sql_update_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Removed: 2.4.5 Removed together with the `OTP` [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html), which was its only user. SQL query to update the password. Currently used only by the `OTP` auth mechanism. ### `passdb_static_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 The static password to be used for all users authenticating using this passdb. ### `passdb_use_worker` **Default:** `no` specific [passdb](/main/core/config/auth/passdb.html) have different defaults **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, run the passdb lookup in auth-worker process instead of the main auth process. This setting is only used by some of the passdb drivers. ### `passdb_username_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Skip the passdb if non-empty and the username doesn't match the filter. This is mainly used to assign specific passdbs to specific domains. Space or comma-separated list of username filters that can have `*` or `?` wildcards. If any of the filters matches, the filter succeeds. Define negative matches by preceding `!`. If any of the negative filter matches, the filter won't succeed. **Example**: - Filter: `*@example.com *@example2.com !user@example.com` - Matches: - `any@example.com` - `user@example2.com` - Won't match: - `user@example.com` ## Result Values The following values control the behavior of a passdb lookup result: ### `return-ok` Return success, don't continue to the next `passdb`. ### `return-fail` Return failure, don't continue to the next `passdb`. ### `return` Return earlier `passdb`'s success or failure, don't continue to the next `passdb`. If this was the first `passdb`, return failure. ### `continue-ok` Set the current authentication state to "success", and continue to the next `passdb`. The following `passdb`s will skip password verification. INFO When using this value on a master `passdb { master = yes }`, execution will jump to the first non-master `passdb` instead of continuing with the next master `passdb`. ### `continue-fail` Set the current authentication state to "failure", and continue to the next `passdb`. The following `passdb`s will still verify the password. INFO When using this value on a master `passdb { master = yes }`, execution will jump to the first non-master `passdb` instead of continuing with the next master `passdb`. ### `continue` Continue to the next `passdb` without changing the authentication state. The initial state is "failure found". If this was set in `[passdb_result_success](/main/core/summaries/settings.html#passdb_result_success)`, the following `passdb`s will skip password verification. INFO When using this value on a master `passdb` (`master = yes`), execution will jump to the first non-master `passdb` instead of continuing with the next master `passdb`. ## Extra Fields The primary purpose of a password database lookup is to return the password for a given user. It may however also return other fields which are treated specially. How to return these extra fields depends on the password database you use. Some `passdb`s don't support returning them at all, such as [PAM authentication database](/main/core/config/auth/databases/pam.html). Boolean fields are true always if the field exists. So `nodelay`, `nodelay=yes`, `nodelay=no` and `nodelay=0` all mean that the "nodelay" field is true. With SQL the field is considered to be nonexistent if its value is NULL. Changed: 2.4.0 Extra fields can now also be set to empty string, while previously they were changed to `yes`. Extra fields without value (without `=`) will default to `yes`. ### `userdb_` The password database may also return fields prefixed with `userdb_`. These fields are only saved and used later as if they came from the [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). Typically this is used only when using [prefetch authentication database](/main/core/config/auth/databases/prefetch.html). ### Suffixes The following suffixes added to a field name are handled specially: #### `:default` Set this field only if it hasn't been set before. Changed: 2.4.0 This was called `:protected` in earlier versions. #### `:remove` Remove this field entirely. ### Fields #### `user` Change the username (eg. lowercase it). This is mostly useful in case-insensitive username lookups to get the username returned back using the same casing always. Otherwise depending on your configuration it may cause problems, such as `/var/mail/user` and `/var/mail/User` mailboxes created for the same user. ##### SQL Example An example `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)` would be: doveconf ``` passdb sql { query = SELECT concat(user, '@', domain) AS user, password \ FROM users \ WHERE user = '%{user | username}' and domain = '%{user | domain}' } ``` You can also update "username" and "domain" fields separately: doveconf ``` passdb sql { query = SELECT user AS username, domain, password \ FROM users \ WHERE user = '%{user | username}' and domain = '%{user | domain}' } ``` #### `login_user` Master `passdb` can use this to change the username. #### `allow_nets` Allow user to log in from only specified IPs (checks against remote client IP). This field is a comma separated list of IP addresses and/or networks where the user is allowed to log in from. If the user tries to log in from elsewhere, the authentication will fail the same way as if a wrong password was given. Example: `allow_nets=127.0.0.0/8,192.168.0.0/16,1.2.3.4,4.5.6.7`. IPv6 addresses are also allowed. IPv6 mapped IPv4 addresses (eg. `::ffff:1.2.3.4`) are converted to standard IPv4 addresses before matching. Example: `allow_nets=::1,2001:abcd:abcd::0:0/80,1.2.3.4`. Using `local` matches any auth connection that doesn't have an IP address. This usually means internal auth lookups from, e.g., doveadm. Example: `allow_nets=127.0.0.0/8,local`. ##### Example [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) example: ``` user:{plain}password::::::allow_nets=192.168.0.0/24 ``` ##### 'local' Keyword The keyword `local` is accepted for Non-IP connections like Unix socket. For example, with a Postfix/LMTP delivery setup, you must include `local` for Postfix to verify the email account: doveconf ``` passdb static { password = test fields { allow_nets = local,127.0.0.1/32 } } ``` Otherwise, you will see this error in the log: "\[/var/run/dovecot/lmtp\] said: 550 5.1.1 [test2@example.com](mailto:test2@example.com) User doesn't exist: [test2@example.com](mailto:test2@example.com) (in reply to RCPT TO command))". #### `allow_real_nets` Allow user's network connection to log in from only specified IPs (checks against real remote IP, e.g. a Dovecot proxy). See [`allow_nets`](#allow-nets) for additional documentation. #### `proxy` Proxy the connection to another IMAP/POP3 server. See [proxy passdb](/main/core/config/proxy/overview.html). #### `proxy_maybe` Proxy the connection to another IMAP/POP3 server. See [proxy passdb](/main/core/config/proxy/overview.html). #### `host` Send login referral to client (if `proxy=y` field isn't set). See [login referrals](/main/core/config/proxy/referrals.html). #### `nologin` User isn't actually allowed to log in even if the password matches, with optionally a different reason given as the authentication failure message. Commonly used with [proxy passdb](/main/core/config/proxy/overview.html) and [login referrals](/main/core/config/proxy/referrals.html), but may also be used standalone. One way to use this would be: - `nologin=` - `reason=System is being upgraded, please try again later`. Unfortunately many clients don't show the reason to the user at all and just assume that the password was given wrong, so it might not be a good idea to use this unless the system will be down for days and you don't have a better way to notify the users. WARNING The `nologin` field is mainly intended for user logins (IMAP, POP3, ManageSieve). It is ignored with `doveadm`, because the intention is that admin could still be able to access a disabled user via `doveadm`. Changed: 2.4.0 The `nologin` field prevents LMTP access now as long as `[lmtp_proxy = yes](/main/core/summaries/settings.html#lmtp_proxy)`. However, the `reason` field is ignored. INFO If you want to entirely block the user from logging in (i.e. account is suspended), with no IMAP referral information provided, you must ensure that neither `proxy` nor `host` are defined as one of the `passdb` extra fields. The order of preference is: `proxy`, `host`, then `nologin`. #### `nodelay` Don't delay reply to client in case of an authentication failure. However, this doesn't affect internal failure delays (`[auth_internal_failure_delay](/main/core/summaries/settings.html#auth_internal_failure_delay)`). If the authentication fails, Dovecot typically waits 0-2 seconds (`[auth_failure_delay](/main/core/summaries/settings.html#auth_failure_delay)`) before sending back the "authentication failed" reply. If this field is set, no such delay is done. Additionally, [Authentication Penalty](/main/core/config/auth/penalty.html) won't be increased. Commonly used with [proxy passdb](/main/core/config/proxy/overview.html) and [login referrals](/main/core/config/proxy/referrals.html), but may also be used standalone. INFO If [PAM authentication database](/main/core/config/auth/databases/pam.html) is used as the `passdb`, it adds an extra delay which can't be removed by this setting. #### `nopassword` If you want to allow all passwords, use an empty password and this field. #### `fail` If set, explicitly fails the `passdb` lookup. #### `k5principals` If using `[auth_mechanisms = gssapi](/main/core/summaries/settings.html#auth_mechanisms)`, may contain Kerberos v5 principals allowed to map to the current user, bypassing the internal call to `krb5_kuserok()`. The database must support credentials lookup. #### `delay_until=[+]` Delay login until this time. The timestamp must be less than 5 minutes into future or the login will fail with internal error. The extra random seconds can be used to avoid a load spike of everybody getting logged in at exactly the same time. #### `noauthenticate` Do not perform any authentication, just store extra fields if user is found. ##### `check_client_fp` Match client certificate or public key fingerprint. This is intended to replace CA certificates with verifying client certificates using fingerprints, or to enforce that particular public key or certificate is being used. To enable this feature, you need to configure: - `[auth_ssl_require_client_cert = yes](/main/core/summaries/settings.html#auth_ssl_require_client_cert)` - `[ssl_peer_certificate_fingerprint_hash = sha256](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)`, or some other valid hash - `[ssl_server_request_client_cert = any-cert](/main/core/summaries/settings.html#ssl_server_request_client_cert)` if you don't want to validate cert against certificate authority ::warning If CA certificates are not used, a passdb must provide a valid check\_client\_fp (or variant) to validate the client certificate. If none is provided, the authentication will fail. When feature is enabled, and certificate has not been validated by certificate authority (or is self-signed), at least one passdb must successfully match the fingerprint, otherwise the whole authentication will fail with "Client didn't present valid SSL certificate". If your certificate is authenticated by certificate authority, using any of the fingerprint matching keywords will fail that passdb if the fingerprint does not match, but next password database can still authenticate the user. Note though that none of the passdbs are required to successfully match the fingerprint in this case. ##### `check_client_cert_fp` Match client certificate fingerprint. See `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)`. Certificate fingerprint is calculated by taking hash value of DER encoded X509 client certificate. See [passdb: check\_client\_fp Extra field](/main/core/config/auth/passdb.html#check-client-fp) ##### \`check\_client\_pubkey\_fp \` Match client public key fingerprint (but not certificate). See `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)`. Public key fingerprint is calculated by taking hash value of DER encoded certificate public key. See [passdb: check\_client\_fp Extra field](/main/core/config/auth/passdb.html#check-client-fp) #### `forward_` In a proxy, pass the variable to the next hop (backend) as `forward_`. See [forwarding fields](/main/core/config/proxy/overview.html#forwarding-fields). #### `event_` Import `name=value` to login events. ### Examples #### SQL doveconf ``` passdb sql { query = SELECT userid AS user, password, 'Y' as proxy, host \ FROM users WHERE userid = '%{user}' } } ``` #### LDAP dovecot.conf doveconf ``` passdb ldap { ... fields { user = %{ldap:user} password = %{ldap:userPassword} proxy = %{ldap:proxyEnabled} host = %{ldap:hostName} } } ``` WARNING About the `proxy`, `proxy_maybe` and any other boolean type fields: these represent an existence test. Currently this translates to `will proxy (or proxy_maybe) if this attribute exists`. This allows the proxy behaviour to be selectable per user. To have it `always` on, use a template, e.g.: dovecot.conf doveconf ``` fields { user = %{ldap:user} password = %{ldap:userPassword} host = %{ldap:hostName} proxy = y } ``` ### passwd-file ``` user:{plain}pass::::::proxy=y host=127.0.0.1 ``` # Authentication Penalty Dovecot anvil process tracks authentication penalties for different IPs to slow down brute force login attempts. The penalty is increased after failed logins until a maximum value, unless [passdb: nodelay Extra Field](/main/core/config/auth/passdb.html#nodelay) is used. The penalty is applied for the IP before passdb lookups are done, so the delay might exist even with `nodelay` if it is not used for all authentication attempts for the IP. ## Algorithm - First auth failure reply will be delayed for 2 seconds (this happens even without auth penalty) - `AUTH_PENALTY_INIT_SECS` in `src/auth/auth-penalty.h` - The delay will be doubled for 4 -> 8 seconds, and then the upper limit of 15 seconds is reached. - `AUTH_PENALTY_MAX_SECS` and `AUTH_PENALTY_MAX_PENALTY` in `src/auth/auth-penalty.h` - If the IP is in `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` (e.g. webmail), skip any authentication penalties - If the username+password combination is the same as one of the last 10 login attempts, skip increasing authentication penalty. - `CHECKSUM_VALUE_PTR_COUNT` in `src/anvil/penalty.c` - The idea is that if a user has simply configured the password wrong, it shouldn't keep increasing the delay. - The username+password is tracked as the CRC32 of them, so there is a small possibility of hash collisions ## Problems - It is still possible to do multiple auth lookups from the same IP in parallel. - For IPv6 it currently blocks the entire /48 block, which may or may not be what is wanted. - `PENALTY_IPV6_MASK_BITS` in `auth-penalty.c` ## Disabling Authentication penalty tracking can be disabled completely with: doveconf ``` service anvil { unix_listener anvil-auth-penalty { mode = 0 } } ``` # Authentication Policy Dovecot supports interfacing with an external authentication policy server. This server can be used to decide whether the connecting user is permitted, tarpitted or outright rejected. While dovecot can do tarpitting and refusal on its own, this feature adds support for making cluster-wide decisions to make it easier to deter and defeat brute force attacks. ## Configuration The auth-policy server is a core feature and does not require plugin(s) to work. To activate this feature, you need to configure it. Auth policy overrides some of the default HTTP client settings: - `[http_client_request_absolute_timeout = 2s](/main/core/summaries/settings.html#http_client_request_absolute_timeout)` - `[http_client_max_idle_time = 10s](/main/core/summaries/settings.html#http_client_max_idle_time)` - `[http_client_max_parallel_connections = 100](/main/core/summaries/settings.html#http_client_max_parallel_connections)` - `[http_client_user_agent = dovecot/auth-policy-client](/main/core/summaries/settings.html#http_client_user_agent)` You can override these and any other HTTP client or SSL settings by placing them inside `[auth_policy](/main/core/summaries/settings.html#auth_policy)` [Named Filters](/main/core/settings/syntax.html#named-filters). ### `auth_policy` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [Authentication Policy](/main/core/config/auth/policy.html) **Changes:** - Added: 2.4.0 Filter for auth policy specific settings. ### `auth_policy_check_after_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Do policy lookup after authentication is completed? ### `auth_policy_check_before_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Do policy lookup before authentication is started? ### `auth_policy_hash_mech` **Default:** `sha256` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `md4``md5``sha1``sha256``sha512` **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Hash mechanism to use for password. ### `auth_policy_hash_nonce` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Cluster-wide nonce to add to hash. This should contain a secret randomly generated string, which is the same for each Dovecot server within the cluster. REQUIRED configuration when you want to use authentication policy. Example: ``` auth_policy_hash_nonce = ``` ### `auth_policy_hash_truncate` | Key 1 | | --- | | Default | `12` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` | | Advanced Setting; this should not normally be changed. | How many bits to use from password hash when reporting to policy server. ### `auth_policy_log_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Only log what the policy server response would do? If `yes`, no request is made to the policy server. ### `auth_policy_reject_on_fail` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` If policy request fails for some reason, should users be rejected? ### `auth_policy_report_after_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Report authentication result? If `no`, there will be no report for the authentication result. ### `auth_policy_request_attributes` **Default:** `login=%{requested_username} pwhash=%{hashed_password} remote=%{remote_ip} device_id=%{client_id} protocol=%{protocol} session_id=%{session} fail_type=%{fail_type}` **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` **Changes:** - Changed: 2.4.0 Default has changed. Request attributes specification. See [Auth Policy Variables](/main/core/config/auth/policy.html#list-of-fields) for variables that can be used for this setting. ### `auth_policy_server_api_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Header and value to add to request (for API authentication). Note: See [https://en.wikipedia.org/wiki/Basic\_access\_authentication#Client\_side](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side) This can be used when you are using the weakforced policy server and the web listener password is "super": ``` $ echo -n wforce:super | base64 d2ZvcmNlOnN1cGVy ``` Then the correct value for this setting is: ``` auth_policy_server_api_header = Authorization: Basic d2ZvcmNlOnN1cGVy ``` ### `auth_policy_server_url` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) URL of the policy server. URL is appended with `?command=allow/report`. If URL ends with `&`, the `?` is not appended. REQUIRED configuration when you want to use authentication policy. Example: ``` auth_policy_server_url = http://example.com:4001/ ``` ### Required Minimum Configuration doveconf ``` auth_policy_server_url = http://example.com:4001/ auth_policy_hash_nonce = localized_random_string # OPTIONAL settings #auth_policy_server_api_header = Authorization: Basic #auth_policy_hash_mech = sha256 #auth_policy_request_attributes { # login = %{requested_username} # pwhash = %{hashed_password} # remote = %{rip} # device_id = %{client_id} # protocol = %{protocol} #} #auth_policy_reject_on_fail = no #auth_policy_hash_truncate = 12 #auth_policy_check_before_auth = yes #auth_policy_check_after_auth = yes #auth_policy_report_after_auth = yes ``` ## Password Hash Algorithm To generate the hash, you concatenate nonce, login name, nil byte, and password and run it through the hash algorithm once. The hash is truncated when truncation is set to non-zero. The hash is truncated by first choosing bits from MSB to byte boundary (rounding up), then right-shifting the remaining bits. ``` hash = H(nonce||user||'\x00'||password) bytes = round8(bits*8) hash = HEX(hash[0:bytes] >> (bytes-bits*8)) ``` ## Request Attributes Auth policy server requests are JSON requests. The JSON format can be specified with `[auth_policy_request_attributes](/main/core/summaries/settings.html#auth_policy_request_attributes)`. The syntax is key=value pairs, and key can contain one or more `/` to designate that a JSON object should be made. Examples: ConfigurationJSON Result ``` login=%{orig_username} pwhash=%{hashed_password} remote=%{real_rip} attrs/ja3=%{ssl_ja3_hash} attrs/extra=value ``` json ``` { "login": "john.doe", "pwhash": "1234", "remote": "127.0.0.1", "attrs": { "extra":"value", "ja3":"md5 hash" } } ``` dovecot.confJSON Result doveconf ``` login=%{orig_username} pwhash=%{hashed_password} remote=%{real_rip} attrs/cos=%{userdb:cos} ``` json ``` { "login": "john.doe", "pwhash": "1234", "remote": "127.0.0.1", "attrs": { "cos": "premium" } } ``` ### IMAP ID You can include IMAP ID command result in auth policy requests, by using `%{client_id}`, which will expand to IMAP ID command arglist. You must set `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)` for this to work. ## List of Fields All fields supported by `[Authentication variables](/main/core/settings/variables.html#authentication-variables)` can be used. In addition, you can use following fields: ### `hashed_password` User's password hashed with [password hash algorithm](#password-hash-algorithm). ### `requested_username` Username for regular logins. For master user logins, this is the requested login username (not the master username). ### `fail_type` Added: 2.4.0 The reason request failed. Results: | Result | Description | | --- | --- | | `internal` | Dovecot internal processing error. | | `credentials` | The user's credentials were wrong. | | `account` | Account is not known. | | `expired` | User's password is expired. | | `disabled` | Account was disabled. | | `policy` | Login was rejected by policy server. | ### `tls` TLS protection level. Always available. ### `policy_reject` Deprecated: 2.4.0 Obsolete field indicating whether the request was rejected by policy server. ### `success` Overall indicator whether the request succeeded or not. ## Expected Response json ``` { "status": -1, "msg": "go away" } ``` `status` values are explained below. ## Mode of Operation ### Auth Policy check: Authentication 'Before' userdb/passdb First query is done **before** password and user databases are consulted. This means that any userdb/passdb attributes are left empty. The command used here is `allow` and will appear on the URL as `command=allow`. `status` result values: - `-1`: Reject - `0`: Accept - `(Any other positive value)`: Tarpit for this number of seconds. ### Auth Policy Check: Authentication 'After' Successful userdb/passdb Lookup Second lookup is done **after** authentication succeeds. The command used here is `allow` and will appear on the URL as `command=allow`. `status` result values: - `-1`: Authentication fail - `>= 0`: Authentication succeed ### Auth Policy Check: Reporting After Authentication Succeeds A report request is sent at end of authentication. The command used here is `report` and will appear on the URL as `command=report`. The `status` result value is ignored. The JSON request is sent with two additional attributes: #### `success` Boolean true/false depending on whether the overall authentication succeeded #### `policy_reject` Boolean true/false whether the failure was due to policy server ## Compatible Auth Policy Servers - [OX Abuse Shield](https://oxpedia.org/wiki/index.php?title=AppSuite:OX_Abuse_Shield) # Password Schemes Password scheme means the format in which the password is stored in [passdb](/main/core/config/auth/passdb.html). The main reason for choosing a scheme other than `PLAIN` is to prevent someone with access to the password database from stealing users' passwords and using them to access other services. Deprecated: 2.4.0 Some password schemes are disabled by default due to being considered weak. This includes MD based (except DIGEST-MD5 and CRAM-MD5), LANMAN, NTLM and a few others. Please read the documentation carefully. You can enable these with `[auth_allow_weak_schemes = yes](/main/core/summaries/settings.html#auth_allow_weak_schemes)`. ## What Scheme to Use? You should choose the strongest crypt scheme that's supported by your system. A few articles about why choosing a good password scheme is important: - [How To Safely Store A Password](https://codahale.com/how-to-safely-store-a-password/) - [Speed Hashing](https://blog.codinghorror.com/speed-hashing/) It's not possible to easily switch from one password scheme to another. The only practical way to do this is to wait until user logs in and change the password during the login. INFO Dovecot's schemes are implemented by the libc's `crypt()` function. Using them is especially useful when sharing the same passwords with other software, because most of them support using `crypt()` to verify the password. However, not all libcs (especially older ones) implement all of the schemes. See below for other password schemes that are implemented by Dovecot internally (instead of libc). From strongest to weakest: ### ARGON2I/ARGON2ID Added: 2.4.0 [Argon2](https://en.wikipedia.org/wiki/Argon2) is the winner of a password hashing competition held at July 2015. The password will start with `$argon2i$` or `$argon2id$.` You can use `-r` to tune computational complexity, minimum is 3. ARGON2ID is only available if your libsodium is recent enough. ARGON2 can require quite a hefty amount of virtual memory, so we recommend that you set service `auth { vsz_limit = 2G }` at least, or more. ### ARGON2 Added: 2.4.0 This scheme is also accepted and processed according to the actual algorithm as described in the hash, e.g, `{ARGON2}$argon2id$...` is recognized and processed properly as ARGON2I/ARGON2ID (as long as libsodium is recent enough to support it). ### CRYPT This is an umbrella term for all password schemes libc's `crypt()` can verify. On a current Linux system (2025) these are the following roughly in the order from strongest to weakest: | Scheme | Prefix | Maximum passphrase length | | --- | --- | --- | | yescrypt | `$y$` | unlimited | | gost-yescrypt | `$gy$` | unlimited | | sm3-yescrypt | `$sm3y$` | unlimited | | scrypt | `$7$` | unlimited | | bcrypt | `$2b$` | 72 characters | | sha512crypt | `$6$` | unlimited | | sha256crypt | `$5$` | unlimited | | sm3crypt | `$sm3$` | unlimited | | sha1crypt | `$sha1` | unlimited | | SunMD5 | `$md5` | unlimited | | md5crypt | `$1$` | unlimited | | bsdicrypt | `_` | (ignores 8th bit) | | descrypt | | 8 characters (ignores 8th bit) | | bigcrypt | | 128 characters (ignores 8th bit) | | NT | `$3$` | unlimited | WARNING On older Linux distros `crypt()` might not include all of the schemes in the list above. You can verify which ones are supported on your system by reading `man 5 crypt` ### BLF-CRYPT This is the Blowfish crypt (bcrypt) scheme. It is generally considered to be very secure. The encrypted password will start with `$2y$` (other generators can generate passwords that have other letters after $2, those should work too.) bcrypt is bundled with Dovecot, so it does not require an external library. You can tune the computational cost using -r parameter for doveadm. ### SHA512-CRYPT A strong scheme. The encrypted password will start with `$6$`. ### SHA256-CRYPT A strong scheme. The encrypted password will start with `$5$`. ### MD5-CRYPT A weak but common scheme often used in `/etc/shadow`. The encrypted password will start with `$1$`. ## Generating Encrypted Passwords You can generate passwords for a particular scheme easily with `[doveadm pw](/main/core/summaries/doveadm.html#pw)` utility. If you do not have Dovecot configured, you can use `doveadm -O pw` to avoid complaints about config file. The scheme defaults to `CRYPT` (with the `$2y$` bcrypt format), but you can use `-s` to override it: `[doveadm pw -s SHA512-CRYPT](/main/core/summaries/doveadm.html#pw)`. To provide password, for scripting purposes, you can use either `[doveadm pw -p password](/main/core/summaries/doveadm.html#pw)` or: sh ``` printf 'password\npassword\n' | doveadm pw ``` ## Default password Schemes Password databases get the default password scheme using the `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` setting. However, there are exceptions: | Database | Default | | --- | --- | | [passwd authentication database](/main/core/config/auth/databases/passwd.html) | `CRYPT` is used by default and can't be changed currently. | | [PAM authentication database](/main/core/config/auth/databases/pam.html) [BSD authentication database](/main/core/config/auth/databases/bsd.html) | Dovecot never even sees the password with these databases, so Dovecot has nothing to do with what password scheme is used. | The password scheme can be overridden for each password by prefixing it with `{SCHEME}`, for example: `{PLAIN}pass`. ## Non-cleartext Authentication Mechanisms See [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) for an explanation of auth mechanisms. Most installations use only cleartext mechanisms, so you can skip this section unless you know you want to use them. The problem with non-cleartext auth mechanisms is that the password must be stored either in cleartext, or using a mechanism-specific scheme that's incompatible with all other non-cleartext mechanisms. In addition, the mechanism-specific schemes often offer very little protection. This isn't a limitation of Dovecot, it's a requirement for the algorithms to even work. For example if you're going to use CRAM-MD5 authentication, the password needs to be stored in either PLAIN or CRAM-MD5 scheme. If you want to allow both CRAM-MD5 and DIGEST-MD5, the password must be stored in cleartext. In future it's possible that Dovecot could support multiple passwords in different schemes for a single user. | Mechanism | Comment | | --- | --- | | CRAM-MD5 | Used with CRAM-MD5 mechanism. | | [DIGEST-MD5](/main/core/config/auth/mechanisms/digest_md5.html) | Used with DIGEST-MD5 mechanism. The username is included in the hash, so it's not possible to use the hash for different usernames. | | SCRAM-SHA-1 | Used with SCRAM-SHA-1 and SCRAM-SHA-1-PLUS mechanisms. Added: 2.4.0 | | SCRAM-SHA-256 | Stronger replacement for SCRAM-SHA-1. Added: 2.4.0 | ## Other Supported Password Schemes Strong schemes and mechanism-specific schemes are listed above. For some schemes (e.g. PLAIN-MD5, SHA) Dovecot is able to detect if the password hash is base64 or hex encoded, so both can be used. ### PLAIN Password is in cleartext. ### CRYPT Password is encrypted. Dovecot uses libc's `crypt()` function, which means that it is able to recognize all password schemes available on your system (e.g. the ones used in `/etc/passwd`). See [CRYPT](#crypt) above for a list. #### BLF-CRYPT Bcrypt based hash. (`$2y$`) #### DES-CRYPT Traditional DES based hash. The DES-crypt scheme only uses the first 8 characters of the password, the rest is ignored. Other schemes may have other password length limitations (if they limit the password length at all). Changed: 2.4.0 Disabled by default. #### MD5-CRYPT MD5-based hash (`$1$`) Changed: 2.4.0 Disabled by default. #### SHA256-CRYPT SHA-256 based hash (`$5$`) #### SHA512-CRYPT SHA-512 based hash (`$6$`) ### OTP Removed: 2.4.5 The `OTP` password scheme has been removed together with the `OTP` [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html). [RFC 2289](https://datatracker.ietf.org/doc/html/rfc2289) based One-Time Password system. ### MD5 Based Schemes Changed: 2.4.0 Disabled by default. #### PLAIN-MD4 MD4 sum of the password stored in hex. #### MD5 Alias for MD5-CRYPT. #### PLAIN-MD5 MD5 sum of the password stored in hex. #### LDAP-MD5 MD5 sum of the password stored in base64. #### SMD5 Salted MD5 sum of the password stored in base64. #### HMAC-MD5 Alias CRAM-MD5. ### SHA Based Schemes INFO See below for libc's SHA\* support. #### SHA Alias for SHA1. #### SHA1 SHA1 sum of the password stored in base64. #### SSHA Salted SHA1 sum of the password stored in base64. #### SHA256 SHA256 sum of the password stored in base64. #### SSHA256 Salted SHA256 sum of the password stored in base64. #### SHA512 SHA512 sum of the password stored in base64. #### SSHA512 Salted SHA512 sum of the password stored in base64. ### Other Schemes #### ARGON2I ARGON2i password scheme, needs libsodium. #### ARGON2ID ARGON2id password scheme, needs libsodium. #### PBKDF2 Added: 2.4.0 PKCS5 Password hashing algorithm. Note that there is no standard encoding for this format, so this scheme may not be interoperable with other software. Dovecot implements it as `$1$salt$rounds$hash`. #### ARGON2 ARGON2 password scheme, needs libsodium ## Encoding The base64 vs. hex encoding simply the default encoding that is used. You can override it for any scheme by adding a ".hex", ".b64" or ".base64" suffix. For example: - `{SSHA.b64}986H5cS9JcDYQeJd6wKaITMho4M9CrXM` contains the password encoded to base64 (just like {SSHA}) - `{SSHA.HEX}3f5ca6203f8cdaa44d9160575c1ee1d77abcf59ca5f852d1` contains the password encoded to hex This can be especially useful with cleartext passwords to encode characters that would otherwise be illegal. For example, in [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) you couldn't use a ":" character in the password without encoding it to base64 or hex. For example: `{PLAIN}{\}:!"` is the same as `{PLAIN.b64}e1x9OiEiCg==`. You can also specify the encoding with doveadm pw. For example: `[doveadm pw -s plain.b64](/main/core/summaries/doveadm.html#pw)`. ## Salting For the SHA512-CRYPT, SHA256-CRYPT, and MD5-CRYPT schemes, the salt is stored before the hash, e.g.: `$6$salt$hash`. For the BLF-CRYPT scheme, bcrypt stores the salt as part of the hash. For most of the other salted password schemes (SMD5, SSHA\*) the salt is stored after the password hash and its length can vary. When hashing the password, append the salt after the cleartext password, e.g.: SSHA256(pass, salt) = SHA256(pass + salt) + salt. For example with SSHA256 you know that the hash itself is 32 bytes (256 bits/8 bits per byte). Everything after that 32 bytes is the salt. For example if you have a password: ``` {SSHA256}SoR/78T5q0UPFng8UCXWQxOUKhzrJZlwfNtllAupAeUT+kQv ``` After base64 decoding it you'll see that its length is 36 bytes, so the first 32 bytes are the hash and the following 4 bytes are the salt: - length: `echo SoR/78T5q0UPFng8UCXWQxOUKhzrJZlwfNtllAupAeUT+kQv|base64 -d|wc -c`\-> 36 - hash: `echo SoR/78T5q0UPFng8UCXWQxOUKhzrJZlwfNtllAupAeUT+kQv|base64 -d|dd bs=1 count=32|hexdump -C`\-> 4a 84 7f ef c4 f9 ab 45 0f 16 78 3c 50 25 d6 43 13 94 2a 1c eb 25 99 70 7c db 65 94 0b a9 01 e5 - salt: `echo SoR/78T5q0UPFng8UCXWQxOUKhzrJZlwfNtllAupAeUT+kQv|base64 -d|dd bs=1 skip=32|hexdump -C`\-> 13 fa 44 2f ### Common Hash Sizes - MD5: 16 bytes - SHA: 20 bytes - SHA256: 32 bytes - SHA512: 64 bytes # User Databases (`userdb`) Dovecot uses [passdb](/main/core/config/auth/passdb.html) and `userdb` as part of the authentication process. [passdb](/main/core/config/auth/passdb.html) authenticated the user. `userdb` lookup then retrieves post-login information specific to the authenticated user. This may include: - Mailbox location information - Quota limit - Overriding settings for the user (almost any setting can be overridden) | Userdb Lookups | Dovecot Proxy | Dovecot Backend | | --- | --- | --- | | IMAP & POP3 logins | No | YES | | LMTP mail delivery | No | YES | | doveadm commands | No | YES | The `userdb` and [passdb](/main/core/config/auth/passdb.html) may be the same or they may be different depending on your needs. You can also have [Multiple Authentication Databases](/main/core/config/auth/multiple.html). ## Authentication Databases See [Authentication Databases](/main/core/config/auth/databases/overview.html). ## Multiple Userdbs You can use multiple databases, so if the user isn't found from the first database, Dovecot checks the next one. You can control the behavior of what happens after a userdb lookup is successful or unsuccessful with settings: - `[userdb_result_success](/main/core/summaries/settings.html#userdb_result_success)` - `[userdb_result_failure](/main/core/summaries/settings.html#userdb_result_failure)` - `[userdb_result_internalfail](/main/core/summaries/settings.html#userdb_result_internalfail)` When using userdbs inside a `protocol ... { ... }` filter, the protocol-specific userdbs are executed first, and only then followed by the globally defined userdbs. Currently it's not possible to configure userdbs inside other filters. ## Fields The user database lookup can return these fields: ### `uid` User's UID (UNIX user ID), overrides the global `[mail_uid](/main/core/summaries/settings.html#mail_uid)` setting. ### `gid` User's GID (UNIX group ID), overrides the global `[mail_gid](/main/core/summaries/settings.html#mail_gid)` setting. ### `home` User's home directory, overrides the global `[mail_home](/main/core/summaries/settings.html#mail_home)` setting. Although not required, it's highly recommended even for virtual users. ### Optional Extra Fields The extra fields are also passed to post-login scripts. See [post-login scripting](/main/core/config/post_login_scripting.html). You can override settings in `dovecot.conf`, see [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). #### `user` Changes the username (can also be done by the [passdb](/main/core/config/auth/passdb.html) lookup). ## Supported Databases | Database | Description | | --- | --- | | [Passwd](/main/core/config/auth/databases/passwd.html) | System users (NSS, `/etc/passwd`, or similar). | | [Passwd-file](/main/core/config/auth/databases/passwd_file.html) | `/etc/passwd`\-like file. | | [LDAP](/main/core/config/auth/databases/ldap.html) | Lightweight Directory Access Protocol. | | [SQL](/main/core/config/auth/databases/sql.html) | SQL database (PostgreSQL, MySQL, SQLite, Cassandra). | | [Static](/main/core/config/auth/databases/static.html) | Userdb information generated from a given template. | | [Prefetch](/main/core/config/auth/databases/prefetch.html) | Prefetch database. This assumes that the [passdb](/main/core/config/auth/passdb.html) already returned also all the required user database information. | | [Lua](/main/core/config/auth/databases/lua.html) | Lua script for authentication. | ## Settings ### `userdb` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` **See Also:** - [userdb](/main/core/config/auth/userdb.html) - `[userdb_name](/main/core/summaries/settings.html#userdb_name)` - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` Creates a new [userdb](/main/core/config/auth/userdb.html). The filter name refers to the `[userdb_name](/main/core/summaries/settings.html#userdb_name)` setting. ### `userdb_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[userdb_name](/main/core/summaries/settings.html#userdb_name)` - [userdb](/main/core/config/auth/userdb.html) - [Authentication Databases](/main/core/config/auth/databases/overview.html) The driver used for this user database. If empty, defaults to `[userdb_name](/main/core/summaries/settings.html#userdb_name)`. ### `userdb_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) Userdb fields (and [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields)). The values can contain [%variables](/main/core/settings/variables.html). All `%variables` used here reflect the state **after** the current userdb lookup, and can refer to fields returned by previous userdb lookups. Depending on the userdb driver, it can also refer to variable fields returned by it (e.g. `%{ldap:fieldName}`). INFO The LDAP driver provides additional specific variables, see [LDAP authentication](/main/core/config/auth/databases/ldap.html) for more details. For example: ``` userdb ldap { fields { user = %{ldap:userId} home = /home/%{ldap:mailboxPath} uid = vmail gid = vmail } } ``` ### `userdb_fields_import_all` **Default:** `yes` For `userdb ldap` the default is `no`. **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` If yes, import all fields returned by the userdb lookup. If no, require `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` to explicitly add wanted fields. ### `userdb_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[userdb](/main/core/summaries/settings.html#userdb)` - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` Name of the userdb. The `[userdb](/main/core/summaries/settings.html#userdb)` filter name refers to this setting. If the `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` setting is empty, the `userdb_name` is used as the driver. This allows doing e.g.: ``` userdb passwd-file { passwd_file_path = /etc/dovecot/passwd ``` ### `userdb_result_failure` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do if the user was not found from the userdb. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). ### `userdb_result_internalfail` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do after the userdb failed due to an internal error. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). If any of the userdbs had an internal failure and the final userdb also returns `continue` the authentication will fail with `internal error`. ### `userdb_result_success` **Default:** `return-ok` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do if the user was successfully found from the userdb. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). ### `userdb_skip` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``found``notfound` Configures when userdbs should be skipped: | Value | Description | | --- | --- | | `never` | Never skip over this userdb. | | `found` | Skip if an earlier userdbs already found the user. | | `notfound` | Skip if previous userdbs haven't yet found the user. | ### `userdb_sql_iterate_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to list all available usernames. ### `userdb_sql_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to lookup the userdb fields. ### `userdb_static_allow_all_users` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 Skip user existence verification via passdb lookup. ### `userdb_use_worker` **Default:** `no` specific [userdb](/main/core/config/auth/userdb.html) have different defaults **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, run the userdb lookup in auth-worker process instead of the main auth process. This setting is only used by some of the userdb drivers. ## Result Values The following values control the behavior of a userdb lookup result: ### `return-ok` Return success, don't continue to the next `userdb`. ### `return-fail` Return "user doesn't exist", don't continue to the next `userdb`. ### `return` Return earlier `userdb`'s success or failure, don't continue to the next `userdb`. If this was the first `userdb`, return "user doesn't exist". ### `continue-ok` Set the current user existence state to "found", and continue to the next `userdb`. ### `continue-fail` Set the current user existence state to "not found", and continue to the next `userdb`. ### `continue` Continue to the next `userdb` without changing the user existence state. The initial state is "not found". ## Extra Fields A user database lookup typically returns `uid`, `gid`, and `home` fields, as per traditional `/etc/passwd` lookups. Other fields may also be stored in the `userdb`, and these are called 'extra fields'. These fields can be returned the exact same way as `uid`, `gid`, and `home` fields. It's also possible to override settings from `dovecot.conf`. For example the `[mail_path](/main/core/summaries/settings.html#mail_path)` and `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` settings are commonly overridden to provide per-user mail path or quota limit. The extra fields are also passed to [post-login scripting](/main/core/config/post_login_scripting.html). Changed: 2.4.0 Extra fields can now also be set to empty string, while previously they were changed to `yes`. Extra fields without value (without `=`) will default to `yes`. ### Suffixes The following suffixes added to a field name are handled specially: #### `:default` Set this field only if it hasn't been set before. Changed: 2.4.0 This was called `:protected` in earlier versions. #### `:remove` Remove this field entirely. For example you can return `uid:remove` to remove the `uid` field from the userdb reply. This differs from `uid=` in that the field is removed entirely (and default is used) instead of just being set to an empty value. ### Fields #### `nice` Set the mail process's priority to be the given value. #### `chroot` Chroot to given directory. Overrides `[mail_chroot](/main/core/summaries/settings.html#mail_chroot)`. #### `system_groups_user` Specifies the username whose groups are read from `/etc/group` (or wherever NSS is configured to taken them from). The logged in user has access to those groups. This may be useful for shared mailboxes. #### `userdb_import` This allows returning multiple extra fields in one TAB-separated field. It's useful for `userdb`s which are a bit less flexible for returning a variable number of fields (e.g. SQL). #### `uidgid_file` Get `uid` and `gid` for user based on the given filename. #### `user` User can be overridden (normally set in [passdb](/main/core/config/auth/passdb.html)). #### `event_` Import `name=value` to mail user event. ### Overriding Settings For example if you have `quota_storage_size=100M` in `doveconf.conf` and the `userdb` lookup returns `quota_storage_size=200M`, the original quota setting gets overridden. In fact, if the lookup always returns a `quota_storage_size` field, there's no point in having `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` in `dovecot.conf` at all, because it always gets overridden anyway. To understand how imap and pop3 processes see their settings, it may be helpful to know how Dovecot internally passes them: 1. First all actual settings are first read into memory. 2. Next all the extra fields returned by `userdb` lookup are used to override the settings. 3. Last, if [post-login scripting](/main/core/config/post_login_scripting.html) is used, it may modify the settings. If you want to override settings inside sections, you can separate the section name and key with `/`. For example: doveconf ``` namespace default { inbox = yes separator = . } ``` The separator setting can be overridden by returning `[namespace/default/separator](/main/core/summaries/settings.html#namespace_separator)` extra field. ### Examples #### SQL dovecot.conf doveconf ``` userdb sql { query = SELECT home, uid, gid, CONCAT(quota_bytes, 'B') AS quota_storage_size, separator AS "namespace/default/separator" \ FROM users \ WHERE username = '%{user | username}' and domain = '%{user | domain}' } ``` #### LDAP dovecot.conf doveconf ``` userdb ldap { ... fields { home = %{ldap:homeDirectory} uid = %{ldap:uidNumber} gid = %{ldap:gidNumber} quota_storage_size = %{ldap:quotaBytes}B namespace/default/separator = %{ldap:mailSeparator} } } ``` #### passwd-file Example that shows how to give two `userdb` extra fields (`mail_driver` and `quota`). Note that all `userdb` extra fields must be prefixed with `userdb_`, otherwise they're treated as [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). ``` user:{plain}pass:1000:1000::/home/user::userdb_mail_driver=mbox userdb_mail_path=~/mail userdb_quota_storage_size=100M userdb_namespace/default/separator=/ user2:{plain}pass2:1001:1001::/home/user2::userdb_mail_driver=maildir userdb_mail_path=~/Maildir userdb_quota_storage_size=200M ``` ## See Also - [Authentication Caching](/main/core/config/auth/caching.html) # System Users System users are typically defined in `/etc/passwd` file, but this isn't necessary. Using [NSS](http://en.wikipedia.org/wiki/Name_Service_Switch) you can configure the lookups to be done from elsewhere (e.g. LDAP). See [passwd authentication database](/main/core/config/auth/databases/passwd.html) userdb configuration for how to set this up. Especially if you're using nss\_ldap you must set `[userdb_use_worker = yes](/main/core/summaries/settings.html#userdb_use_worker)`. System users usually have their own separate user IDs (UIDs). This is good from a security point of view, because it means that the kernel will also prevent users from accessing each others' mails. If the users have direct write access to the mail files (eg. the users have shell access), they can easily cause all sorts of mailbox corruptions. That may generate all kinds of error messages to Dovecot's error logs, so it may be sometimes difficult to tell if there really is a problem or if the user is doing something stupid. If users are going to access the mailboxes with other software than Dovecot, it's important to make sure that their mailbox accesses are compatible. This mostly means that with [mbox](/main/core/config/mailbox_formats/mbox.html)you must make sure that everyone uses the same locking methods in the same order. ## Authentication Admins often wish to use different passwords for IMAP and POP3 than for other services (eg. SSH), because IMAP and POP3 clients often send the password unencrypted over the internet without even bothering to give users any warnings. Dovecot can easily support non-system passwords for system users. If you wish to use system passwords, you'll want to use one of these passdbs: - [PAM authentication database](/main/core/config/auth/databases/pam.html): Most commonly used in Linux and BSDs nowadays. - [BSD authentication database](/main/core/config/auth/databases/bsd.html): BSD authentication is used by OpenBSD. - [passwd authentication database](/main/core/config/auth/databases/passwd.html): System users (NSS, `/etc/passwd`, or similar). This may work instead of PAM (mostly in some BSDs). If you wish to use non-system passwords, you can use pretty much any [passdb](/main/core/config/auth/passdb.html), but for simple installations you'll probably want to use [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html). [userdb](/main/core/config/auth/userdb.html) for system users is always [passwd authentication database](/main/core/config/auth/databases/passwd.html). ## Mail Location Usually UNIX systems are configured by default to deliver mails to `/var/mail/username` or `/var/spool/mail/username` mboxes. You may decide to use these, or use newer mailbox formats instead, such as [Maildir](/main/core/config/mailbox_formats/maildir.html) or [dbox](/main/core/config/mailbox_formats/dbox.html). Dovecot detects the mailbox format and location automatically if `[mail_driver](/main/core/summaries/settings.html#mail_driver)` aren't set, but it's still a good idea to explicitly tell Dovecot where to find the mails. This ensures that Dovecot behaves correctly also when the user's mailbox doesn't exist at the moment (eg. a new user). If Dovecot can't figure out where the existing mails are, it will give an error message and quits. It never tries to create a missing mailbox when autodetection is used. See [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) for more information how to configure the mailbox location. Below are the highlights for mbox and maildir. ### mbox The `/var/mail/username` mbox is called user's INBOX. IMAP protocol supports multiple mailboxes however, so Dovecot needs some directory where to store the other mailboxes. Typically they're stored in `~/mail/` or `~/Mail/` directory. All of these locations are included in mailbox location autodetection. You can specify them manually with: doveconf ``` mail_driver = mbox mail_path = ~/mail mail_inbox_path = /var/mail/%{user} ``` Remember that the `[mail_path](/main/core/summaries/settings.html#mail_path)` setting is the mailbox root directory, don't try to use the INBOX `/var/mail/%{user}` path in there because that isn't going to work (unless you really want to store mails under `/var/mail/%{user}/` directory). If you're also using other software than Dovecot to access mboxes, you should try to figure out what locking methods exactly they're using and update `[mbox_read_locks](/main/core/summaries/settings.html#mbox_read_locks)` and `[mbox_write_locks](/main/core/summaries/settings.html#mbox_write_locks)` settings accordingly. See locking section in [mbox](/main/core/config/mailbox_formats/mbox.html) for more information. ### Maildir Maildir is typically stored in `~/Maildir` directory. You can specify this manually with: doveconf ``` mail_driver = maildir mail_path = ~/Maildir ``` See [Maildir](/main/core/config/mailbox_formats/maildir.html) for more information. ## Chrooting Dovecot, including several other software, allow using "/./" in home directory path to specify the chroot path. For example `/home/./user` would chroot to `/home`. If you want to enable this for Dovecot, add the chroot path to `[valid_chroot_dirs](/main/core/summaries/settings.html#valid_chroot_dirs)` setting (`/home` in the previous example). If this isn't done, Dovecot ignores the "/./". See [chrooting](/main/core/config/chrooting.html) for more details. # Virtual Users There are many ways to configure Dovecot to use virtual users. Users are often categorized as being either system users (in `/etc/passwd`) or virtual users (not in `/etc/passwd`). However from Dovecot's point of view there isn't much of a difference between them. If a [passwd authentication database](/main/core/config/auth/databases/passwd.html) lookup and a [SQL authentication database](/main/core/config/auth/databases/sql.html) lookup return the same [userdb](/main/core/config/auth/userdb.html) information, Dovecot's behavior is identical. ## Password and User Databases Dovecot supports many different [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) With virtual users the most commonly used ones are [LDAP authentication](/main/core/config/auth/databases/ldap.html), [SQL authentication database](/main/core/config/auth/databases/sql.html), and [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html). The databases usually contain the following information: - Username - Password - UNIX User ID (UID) and primary UNIX Group ID (GID) - Home directory and/or mail location ## Usernames and Domains Dovecot doesn't care much about domains in usernames. IMAP and POP3 protocols currently have no concept of "domain", so the username is just something that shows up in your logs and maybe in some configuration, but they have no direct functionality. So although Dovecot makes it easier to handle "user@domain" style usernames (eg. `%{user | username}` and `%{user | domain}` `[Settings variables](/main/core/settings/variables.html)`), nothing breaks if you use, for example, `domain%user` style usernames instead. However some [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) do have an explicit support for realms (pretty much the same as domains). If those mechanisms are used, the username is changed to be `user@realm`. And of course there's no need to have domains at all in the usernames. ## Passwords The password can be in [any format that Dovecot supports](/main/core/config/auth/schemes.html) but you need to tell the format to Dovecot because it won't try to guess it. The SQL and LDAP configuration files have the `default_pass_scheme` setting for this. If you have passwords in multiple formats, or the passdb doesn't have such a setting, you'll need to prefix each password with `{}`, for example `{PLAIN}plaintext-password` or `{PLAIN-MD5}1a1dc91c907325c69271ddf0c944bc72`. ## UNIX UIDs The most important thing you need to understand is that **Dovecot doesn't access the users' mails as the dovecot user**! So **do not** put _dovecot_ into the _mail_ group, and don't make mails owned by the _dovecot_ user. That will only make your Dovecot installation less secure. So, if not the _dovecot_ user, what then? You can decide that yourself. You can create, for example, one _vmail_ user which owns all the mails, or you can assign a separate UID for each user. See [System Users](/main/core/config/auth/users/system.html) for more information about different ways to allocate UIDs for users. ## UNIX GIDs Unless you're using [shared mailboxes](/main/core/config/shared_mailboxes.html) and multiple UIDs, it doesn't really matter what GIDs you use. You can, for example, use a single GID for all users, or create a separate GID for each user. See [System Users](/main/core/config/auth/users/system.html) for more information. ## Home Directories Home directory is a per-user directory where **Dovecot can save user-specific files**. - Dovecot's home directories have nothing to do with system users' home directories. - It's irrelevant if it's under `/home/` or `/var/mail/` or wherever. - If you have trouble understanding this, mentally replace all occurrences of "home directory" with "mail user's private state directory". And in particular: - Never configure your userdb to return the same home directory for multiple users, this will break things. - Home directory must be an absolute path, don't even try to use relative paths, these do not work. Some uses for home directory are: - By default [Sieve](/main/core/config/sieve/overview.html) scripts are in a user's home directory. - The Duplicate mail check database is in a user's home directory. Suppression of duplicate rejects/vacations won't work if home directory isn't specified. - Debugging: If an imap or pop3 process crashes, the core file is written to the user's home directory. ### Home vs. Mail Directory Home directory shouldn't be the same as mail directory with mbox or Maildir formats (but with dbox it's fine). It's possible to do that, but you might run into trouble with it sooner or later. Some problems with this are: - Non-mailbox files may show up as mailboxes. - If you see this with Maildir, `[maildir_stat_dirs = yes](/main/core/summaries/settings.html#maildir_stat_dirs)` hides them. - Or a user might not be able to create mailbox with some wanted name, because there already exists a conflicting file or directory. - e.g., with Maildir if you have `.dovecot.sieve` file, user can't create a mailbox called "dovecot.sieve" (i.e. "dovecot" mailbox that has a "sieve" child) - And vice versa: If user creates "dovecot.sieve" mailbox, Dovecot will probably start logging all kinds of errors because the mailbox directory isn't a valid [Sieve](/main/core/config/sieve/overview.html) script. - If you ever intend to migrate to another mailbox format, it's much easier to do if you can have both old and new mail directories under the user's home directory. ### Ways to Setup Home Directory The directory layouts for home and mail directories could look like one of these (in the preferred order): 1. Mail directory under home, for example: - `home=/var/vmail/domain/user/` - `mail_path=/var/vmail/domain/user/mail/` 2. Completely distinct home and mail directories: - `home=/home/virtual/domain/user/` - `mail_path=/var/vmail/domain/user/` 3. Home directory under mail, for example: - Maildir: - `home=/var/vmail/domain/user/home/` - `mail_path=/var/vmail/domain/user/` - mbox: There's really no good and safe way to do it. 4. The home directory is the same as the mail directory. If for example `home=/var/vmail/domain/user/` and `mail_path=/var/vmail/domain/user/mail/`, set: dovecot.conf doveconf ``` mail_home = /var/vmail/%{user | domain}/%{user | username} mail_driver = maildir mail_path = ~/mail ``` ### LDAP with Relative Directory Paths If your LDAP database uses, e.g., `mailDirectory = domain/user/`, you can use it as a base for home directory: dovecot.conf doveconf ``` userdb ldap { ... fields { home = %{ldap:mailDirectory} } } ``` Then use `[mail_path = ~/Maildir](/main/core/summaries/settings.html#mail_path)`. ### Mail Location If your users have varying locations for mail location, which cannot be represented by templating, userdb can return the [mail field](/main/core/config/auth/userdb.html#extra-fields) to override the default `[mail_path](/main/core/summaries/settings.html#mail_path)`. Normally this is not needed, and it is sufficient to have the setting in config file. ### Dynamic passwd-file Locations In the following example users are expected to log in as `user@domain`. Their mail is kept in their home directory at `/home///Maildir`. The usernames in the passwd and shadow files are expected to contain only the user part, no domain. This is because the path itself already contained %{user | domain} to specify the domain. If you want the files to contain full `user@domain` names, you can change `[auth_username_format](/main/core/summaries/settings.html#auth_username_format)` to `%{user}` or leave it out (its default value is `%{user|lower}`). doveconf ``` mail_driver = maildir mail_path = /home/%{user | domain}/%{user | username}/Maildir passdb passwd-file { passwd_file_path = /home/%{user | domain}/etc/shadow } userdb passwd-file { passwd_file_path = /home/%{user | domain}/etc/passwd } ``` #### Static userdb Many people store only usernames and passwords in their database and don't want to deal with UIDs or GIDs. In that case the easiest way to get Dovecot running is to use the [static userdb](/main/core/config/auth/databases/static.html). doveconf ``` mail_driver = maildir mail_path = ~/Maildir passdb pam { } userdb static { fields { uid = vmail gid = vmail home = /var/mail/virtual/%{user | domain}/%{user | username} } } ``` This makes Dovecot look up the mails from `/var/mail/virtual///Maildir/` directory, which should be owned by vmail user and vmail group. ## Virtual and System Users If you need to do PAM/passwd lookup for system users, and also have domain users, you can configure authentication to drop the domain part after doing virtual user lookup. \`\`doveconf\[dovecot.conf\] ## Your virtual passdb passdb ldap { args = /path/to/ldap/config } passdb static { fields { user = %{domain|lower} noauthenticate = yes } skip = authenticated } passdb pam { skip = authenticated } userdb ldap { args = /path/to/ldap/config } userdb passwd { } # Chrooting (change root) Traditionally chrooting has been done to run the whole server within a single chroot. This is also possible with Dovecot, but it requires manually setting up the chroot and it can be a bit tricky. Dovecot however supports internally running different parts of it in different chroots: - Login processes (imap-login, pop3-login) are chrooted by default into an empty non-writable directory. - Authentication process (dovecot-auth) can be chrooted by setting `chroot=` inside `service auth` and/or `service auth-worker` sections. This could be a good idea to change if you're not using a passdb or userdb that needs to access files outside of the chroot. Also make sure not to run the auth process as root then. - Mail processes (imap, pop3) can be made to chroot in different ways. See below. ## Security Problems If chrooting is used incorrectly, it allows local users to gain root privileges. This is possible by hardlinking setuid binaries inside the chroot jail and tricking them. There are at least two possibilities: 1. Hardlink `/bin/su` inside the chroot and create your own `/etc/passwd`. Then simply run `su root`. 2. Create your own `/lib/libc.so` and run any setuid binary. Of course both of these require that the setuid binary can be run inside the chroot. This isn't possible by default. Either user would have to find a security hole from Dovecot, or the administrator would have had to set up something special that allows running binaries. In any case it's a good idea not to allow users to hardlink setuid binaries inside the chroots. The safest way to do this is to mount the filesystem with "nosuid" option. ## Mail Process Chrooting Due to the potential security problem described above, Dovecot won't chroot mail processes to directories which aren't listed in `[valid_chroot_dirs](/main/core/summaries/settings.html#valid_chroot_dirs)` setting. For example if your users may be chrooting under `/var/mail//` and `/home//`, use: doveconf ``` valid_chroot_dirs = /var/mail /home ``` You can chroot all users globally into the same directory by using `[mail_chroot](/main/core/summaries/settings.html#mail_chroot)` setting. For example: doveconf ``` mail_chroot = /home ``` You can also make userdb return a chroot. There are two ways to do that: 1. Make userdb return `chroot=` field. 2. Insert "/./" inside the returned home directory, eg.: `home=/home/./user` to chroot into `/home`, or `home=/home/user/./` to chroot into `/home/user`. # Dovecot LDA The Dovecot LDA is a [MDA](/main/core/config/delivery/mda.html), which takes mail from an [MTA](/main/core/config/delivery/mta.html) and delivers it to a user's mailbox, while keeping Dovecot index files up to date. TIP These days most people should use [LMTP Server](/main/core/config/delivery/lmtp.html) as the MDA instead, as it's somewhat easier to configure (especially related to permissions) and gives better performance. This page describes the common settings required to make LDA work. ## Main features of Dovecot LDA - [Mailbox indexing during mail delivery](#lda-indexing), providing faster mailbox access later - Quota enforcing by a plugin ([quota plugin](/main/core/plugins/quota.html)) - Sieve language support [Sieve](/main/core/config/sieve/overview.html) - Mail filtering - Mail forwarding - Vacation auto-reply ## Common Delivery Settings - `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)` is used as the From: header address in bounce mails - `[hostname](/main/core/summaries/settings.html#hostname)` is used in generated Message-IDs and in `Reporting-UA:` header in bounce mails - `[sendmail_path](/main/core/summaries/settings.html#sendmail_path)` is used to send mails. Note that the default is `/usr/sbin/sendmail`, which doesn't necessarily work the same as `/usr/lib/sendmail`. - Alternatively you can use `[submission_host](/main/core/summaries/settings.html#submission_host)` to send mails via the specified SMTP server. - `[auth_socket_path](/main/core/summaries/settings.html#auth_socket_path)` specifies the UNIX socket to auth-userdb where LDA can lookup userdb information when `-d` parameter is used. See below how to configure Dovecot to configure the socket. TIP The config files must be world readable to enable dovecot-lda process to read them while running with user privileges. You can put password related settings to a separate file, which you include with \`!include\_try\` and dovecot-lda skips them. ## Parameters See `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`. ## System Users You can use LDA with a few selected system users (i.e. user is found from `/etc/passwd` / NSS) by calling dovecot-lda in the user's `~/.forward` file: ``` | "/usr/local/libexec/dovecot/dovecot-lda" ``` This should work with any MTA which supports per-user `.forward` files. This method doesn't require the authentication socket explained below since it's executed as the user itself. ## Virtual Users ### With a Lookup Give the destination username to dovecot-lda with `-d` parameter, for example: sh ``` dovecot-lda -f $FROM_ENVELOPE -d $DEST_USERNAME ``` You'll need to set up a auth-userdb socket for dovecot-lda so it knows where to find mailboxes for the users: doveconf ``` service auth { unix_listener auth-userdb { mode = 0600 user = vmail # User running dovecot-lda #group = vmail # Or alternatively mode 0660 + dovecot-lda user in this group } } ``` The auth-userdb socket can be used to do [userdb](/main/core/config/auth/userdb.html) lookups for given usernames or get a list of all users. Typically the result will contain the user's UID, GID and home directory, but depending on your configuration it may return other information as well. So the information is similar to what can be found from eg. `/etc/passwd` for system users. This means that it's probably not a problem to use mode=0666 for the socket, but you should try to restrict it more just to be safe. ### Without a Lookup If you have already looked up the user's home directory and you don't need a userdb lookup for any other reason either (such as overriding settings for specific users), you can run dovecot-lda similar to how it's run for system users: sh ``` HOME=/path/to/user/homedir dovecot-lda -f $FROM_ENVELOPE ``` This way you don't need to have a userdb listener socket. Note that you should verify the user's existence prior to running dovecot-lda, otherwise you'll end up having mail delivered to nonexistent users as well. You must have set the proper UID (and GID) before running dovecot-lda. It's not possible to run dovecot-lda as root without `-d` parameter. ### Multiple UIDs If you're using more than one UID for users, you're going to have problems running dovecot-lda, as most MTAs won't let you run dovecot-lda as root. The only recommended solution is to use [LMTP Server](/main/core/config/delivery/lmtp.html). It is not recommended to make `dovecot-lda` binary setuid-root, nor to run it via `sudo`, as these cannot be used safely. ## Problems with dovecot-lda - If you are using [prefetch authentication database](/main/core/config/auth/databases/prefetch.html), keep in mind that `dovecot-lda` does not make a password query and thus will not work if `-d` parameter is used. The [prefetch authentication database](/main/core/config/auth/databases/prefetch.html) page explains how to fix this. ## Logging - Normally Dovecot logs everything through its log process, which is running as root. dovecot-lda doesn't, which means that you might need some special configuration for it to log anything at all. - If dovecot-lda fails to write to log files it exits with temporary failure. - If you have trouble finding where Dovecot logs by default, see [logging](/main/core/admin/logging.html). - Note that Postfix's `mailbox_size_limit` setting applies to all files that are written to. So if you have a limit of 50 MB, dovecot-lda can't write to log files larger than 50 MB and you'll start getting temporary failures. If you want dovecot-lda to keep using Dovecot's the default log files: - If you're logging to syslog, make sure the syslog socket (usually `/dev/log`) has enough write permissions for dovecot-lda. For example set it world-read/writable: `chmod a+rw /dev/log`. - If you're logging to Dovecot's default log files again you'll need to give enough write permissions to the log files for dovecot-lda. You can also specify different log files for dovecot-lda. This way you don't have to give any extra write permissions to other log files or the syslog socket. You can do this by overriding the `[log_path](/main/core/summaries/settings.html#log_path)` and `[info_log_path](/main/core/summaries/settings.html#info_log_path)` settings: doveconf ``` protocol lda { ... # remember to give proper permissions for these files as well log_path = /var/log/dovecot-lda-errors.log info_log_path = /var/log/dovecot-lda.log } ``` For using syslog with dovecot-lda, set the paths empty: doveconf ``` protocol lda { ... log_path = info_log_path = # You can also override the default syslog_facility: #syslog_facility = mail } ``` ## Plugins - Most of the Dovecot plugins work with dovecot-lda. - Virtual quota can be enforced using [quota plugin](/main/core/plugins/quota.html) - Sieve language support can be added with [Sieve](/main/core/config/sieve/overview.html). ### Non-Dovecot LDA Dovecot allows using non-Dovecot LDA to deliver mails to mbox and Maildir files. Dovecot adds the newly delivered mails to its index files, which is relatively fast operation. However, IMAP clients often want to first fetch some of the email headers and other metadata. This requires Dovecot to open and parse the emails, which may add user-visible latency. By using Dovecot LDA this is done as part of the mail delivery stage, so the user visible latency is smaller. # LMTP Server LMTP is a network-available service that handles local delivery of messages. It is defined by [RFC 2033](https://datatracker.ietf.org/doc/html/rfc2033). The main difference from LDA is that the LDA is a short-running process, started as a binary from command line, while LMTP is a long-running process started by Dovecot's master process. TIP LMTP is the recommended method for mail delivery for most installations. ## Common Delivery Settings - `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)` is used as the From: header address in bounce mails - `[hostname](/main/core/summaries/settings.html#hostname)` is used in generated Message-IDs and in `Reporting-UA:` header in bounce mails - `[sendmail_path](/main/core/summaries/settings.html#sendmail_path)` is used to send mails. Note that the default is `/usr/sbin/sendmail`, which doesn't necessarily work the same as `/usr/lib/sendmail`. - Alternatively you can use `[submission_host](/main/core/summaries/settings.html#submission_host)` to send mails via the specified SMTP server. - `[auth_socket_path](/main/core/summaries/settings.html#auth_socket_path)` specifies the UNIX socket to auth-userdb where LDA can lookup userdb information when `-d` parameter is used. See below how to configure Dovecot to configure the socket. TIP The config files must be world readable to enable dovecot-lda process to read them while running with user privileges. You can put password related settings to a separate file, which you include with \`!include\_try\` and dovecot-lda skips them. ## Envelope Addresses Compared to dovecot-lda parameters, the addresses are taken from: | LDA Flag | LMTP Command | Description | | --- | --- | --- | | `-f` | `MAIL FROM:` | Envelope sender address | | `-r` | `RCPT TO:` | Final envelope recipient address | | `-a` | `RCPT TO:`, but may be overridden by `[lda_original_recipient_header](/main/core/summaries/settings.html#lda_original_recipient_header)` | Original envelope recipient address | | `-d` | `RCPT TO:`, but with the `+extension` part removed when `[recipient_delimiter](/main/core/summaries/settings.html#recipient_delimiter)` is enabled | Destination username. If usernames differ from recipient email addresses, the userdb must handle the translation. | ## Listeners You can configure LMTP to be listening on TCP or UNIX sockets: TIP By general convention, LMTP is expected to listen on port 24. doveconf ``` # add lmtp to protocols, otherwise its listeners are ignored protocols { lmtp = yes } service lmtp { inet_listener lmtp { listen = 192.168.0.24 127.0.0.1 ::1 port = 24 } unix_listener lmtp { #mode = 0666 } } ``` The UNIX listener on `$base_dir/lmtp` is enabled by default when protocols setting contains lmtp. ## Security Unfortunately LMTP process currently needs to run as root, and only temporarily drop privileges to users. Otherwise it couldn't handle mail deliveries to more than a single user with different UID. If you're using only a single global UID/GID (i.e. virtual users), you can improve security by running lmtp processes as that user: doveconf ``` service lmtp { user = vmail } ``` ## LMTP Proxying It's possible to use Dovecot LMTP server as a proxy to remote LMTP or SMTP servers. The configuration is similar to [proxy passdb](/main/core/config/proxy/overview.html), but you'll need to tell Dovecot LMTP to issue passdb lookups: `[lmtp_proxy = yes](/main/core/summaries/settings.html#lmtp_proxy)`. ## Performance For higher volume sites, it may be desirable to increase the number of active listener processes. A range of 5 - 20 is probably good for most sites: doveconf ``` service lmtp { process_min_avail = 5 } ``` ## Logging If you want to store LMTP delivery logs to a different file, you can do it with: doveconf ``` service lmtp { executable = lmtp -L } protocol lmtp { info_log_path = /var/log/dovecot-lmtp.log } ``` For rawlogs, please see [rawlog](/main/core/admin/rawlog.html). ## Plugins - Most of the Dovecot plugins work with LMTP. - Virtual quota can be enforced using [quota plugin](/main/core/plugins/quota.html). - `[lmtp_rcpt_check_quota = yes](/main/core/summaries/settings.html#lmtp_rcpt_check_quota)` enables quota checking already at RCPT TO stage. This check isn't done for proxied connections. - Sieve language support can be added with [Sieve](/main/core/config/sieve/overview.html). ## Address Extension Delivery To make address extension work with LMTP you must check that these variables are set: - `[lmtp_save_to_detail_mailbox = yes](/main/core/summaries/settings.html#lmtp_save_to_detail_mailbox)` - `[recipient_delimiter = +](/main/core/summaries/settings.html#recipient_delimiter)` ## Using LMTP with different MTAs - Browse the How To section: [How To's](/main/howto/index.html) - [Halon](https://docs.halon.io/kb/delivery/lmtp) # MDA An MDA is a Mail Delivery Agent. An MDA is being passed messages from a [MTA](/main/core/config/delivery/mta.html) and delivers it to a real or virtual mailbox. Dovecot MDAs: - [LDA](/main/core/config/delivery/lda.html) - [LMTP Server](/main/core/config/delivery/lmtp.html) ## Other Choices - [maildrop](https://www.courier-mta.org/maildrop/) - [procmail](https://github.com/BuGlessRB/procmail) # MTA MTA is an acronym for Mail Transport Agent. It is the software that works behind the scenes to transport E-Mail messages from one computer to another. MUAs (such as Thunderbird, Outlook, Apple Mail, etc.) hand off newly sent messages to an MTA. MTAs talk to other MTAs, and either deliver mail locally or hand it off for delivery to a [MDA](/main/core/config/delivery/mda.html). MTA is a generic term and usually refers to one of these popular software packages: - [Postfix](https://www.postfix.org/) flexible mailer. - [Exim](https://www.exim.org/) - [Sendmail](https://www.proofpoint.com/us/products/email-protection/open-source-email-solution), the original BSD mailer. - [Courier](https://www.courier-mta.org/) - [qmail](https://cr.yp.to/qmail.html) is an obsolete and unmaintained server. If you really intend to continue using it, read [Dave Sill's Life with qmail](http://www.lifewithqmail.org/) which contains instructions to work around some of qmail's security issues. - [HALON](https://halon.io/) is a commercial MTA, which supports Dovecot Auth and LMTP. Some people also subsume mail fetching utilities under the MTA category, among them: - [fetchmail](https://www.fetchmail.info/), a fast mail retriever. - [getmail](https://pyropus.ca/software/getmail/),a mail retrieval utility written in Python. These mail fetching utilities can be used to store mail for later retrieval by Dovecot. # Dovecot Dictionary (dict) Dovecot's lib-dict can be used to access simple key-value databases. This is used by, for example, [last-login plugin](/main/core/plugins/last_login.html) and `[imap_metadata](/main/core/summaries/settings.html#imap_metadata)`. The dictionaries can be accessed either directly by the mail processes or they can be accessed via [proxy processes](#dictionary-proxy-process). Currently supported drivers are: | Name | Description | | --- | --- | | [`file`](#flat-files) | Flat Files | | [`fs`](#filesystem-lib-fs-wrapper) | FS (lib-fs wrapper) | | [`ldap`](#ldap) | LDAP (read only) | | [`proxy`](#proxy) | Proxy | | [`redis`](#redis) | Redis | | [`sql`](#sql) | SQL | ## Connection Pooling The SQL drivers keep a persistent connection open to the database after it's been accessed once. The connection is reused for other SQL lookups as long as their SQL settings are exactly the same. Opened SQL connections are currently never closed. ## Drivers ### Flat Files The file will contain all the keys that are used. Not very efficient for large databases, but good for small ones such as a single user's quota. #### `dict_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path for the dictionary file. ### Filesystem (lib-fs-wrapper) This is a wrapper for lib-fs, which most importantly has the `posix` driver. Use the `[fs](/main/core/summaries/settings.html#fs)` setting to configure the filesystem. For example: doveconf ``` dict fs { fs posix { prefix = /var/lib/dovecot/dict/ } } ``` This create a separate file under `/var/lib/dovecot/dict` for each key. ### LDAP LDAP support is very similar to [`sql`](#sql) support, but there is no write support. Note that the LDAP driver must be used via [`proxy`](#proxy). See [LDAP authentication](/main/core/config/auth/databases/ldap.html). #### Configuration dovecot.conf doveconf ``` dict_server { dict ldap { driver = ldap ldap_uris = ldap://{{LDAPHOST}} ldap_auth_dn = uid=testadmin,cn=users,dc=dovecot,dc=net ldap_auth_dn_password = testadmin ldap_timeout_secs = 5 ldap_base = dc=dovecot,dc=net ldap_starttls = no ssl_client_require_valid_cert = no dict_map priv/test/home { ldap_filter = (&(homeDirectory=*)(uid=%{user})) value = %{ldap:homeDirectory} } } } ``` #### LDAP Settings ### `dict_map` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` Creates a new dict mapping. The filter name refers to the `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` setting. ### `dict_map_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The ldap filter to use to find the ldap entry. This setting is required for ldap `[dict_map](/main/core/summaries/settings.html#dict_map)` ### `dict_map_pattern` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Pattern that is matched to the accessed dict keys. The `[dict_map](/main/core/summaries/settings.html#dict_map)` filter name refers to this setting. If the pattern matches the key, this dict map (and no other) is used. The dict maps are processed in the order listed in the configuration file. ### `dict_map_value_field` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` - sql: Creates a new value for the dict map. The filter name refers to the `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` setting. Dict supports reading/writing multiple values for the same key. - ldap: Value to be returned from an ldap search, as a [variable-expression](/main/core/settings/variables.html) ### `ldap_auth_dn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specify the Distinguished Name (the username used to login to the LDAP server). Leave it commented out to bind anonymously (useful with `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`). Example: `ldap_auth_dn = uid=dov-read,dc=example,dc=com,dc=.` ### `ldap_auth_dn_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for LDAP server. Used if `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is specified. ### `ldap_base` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP base. `[Settings variables](/main/core/settings/variables.html)` can be used. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the variable. Examples: - `ldap_base = dc=mail, dc=example, dc=org` - `ldap_base = %{passdb:classOfServiceDN | safe}` ### `ldap_debug_level` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP library debug level as specified by `LDAP_DEBUG_*` in `ldap_log.h`. Value `-1` means everything. You may need to recompile OpenLDAP with debugging enabled to get enough output. ### `ldap_max_idle_time` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) Disconnect from LDAP server after connection has been idle for this many seconds. ### `ldap_scope` **Default:** `subtree` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `base``onelevel``subtree` This specifies the search scope. ### `ldap_starttls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set to `yes` to use TLS to connect to the LDAP server. ### `ldap_uris` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP URIs to use. Configure this setting to specify what LDAP server(s) to connect to. The URIs are in syntax `protocol://host:port`. Example: `ldap_uris = ldaps://secure.domain.org` ### `ssl_cipher_list` **Default:** `ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH (for ssl_server, empty for ssl_client)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` - `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The list of SSL ciphers to use for TLSv1.2 and below connections, in order of preference. Use `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` for TLSv1.3 connections. You do not need to edit this setting in order to disable specific SSL protocols; that is best done with `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` instead. This setting is used for both incoming and outgoing SSL connections. ### `ssl_client_ca_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The directory where trusted SSL CA certificates can be found. For example `/etc/ssl/certs`. These certificates are used only for outgoing SSL connections (e.g. with the imapc driver). For extra security you might want to point to a directory containing certificates only for the CAs that are actually needed for the server operation instead of all the root CAs. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_require_valid_cert` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Require a valid certificate when connecting to external SSL services? ### `ssl_curve_list` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Colon separated list of elliptic curves to use, in order of preference. An empty value uses the defaults from the SSL library. This setting is used for both incoming and outgoing SSL connections. Example: ``` ssl_curve_list = P-521:P-384:P-256 ``` ### `ssl_min_protocol` **Default:** `TLSv1.2` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The minimum SSL protocol version Dovecot accepts. It cannot be empty. This setting is used for both incoming and outgoing SSL connections. Supported values are: `ANY` WARNING This value is meant for tests only. It should not be used in any deployment of any value/relevance. `TLSv1` Support TLSv1+. (TLSv1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.1` Support TLSv1.1+. (TLSv1.1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.2` Support TLSv1.2+. `TLSv1.3` Support TLSv1.3+. `LATEST` Support only the latest version available. #### Examples To map a key to a search: doveconf ``` dict_map priv/test/mail { ldap_filter = (&(uid=%{user})(mail=*)) ldap_base = ou=container,dc=domain value = %{ldap:mail} } ``` To do a more complex search: doveconf ``` dict_map priv/test/mail/$location { ldap_filter = (&(uid=%{user})(mail=*)(uid=%{pattern:location})) ldap_base = ou=container,dc=domain value = %{ldap:mail} } ``` ### Proxy The proxy driver performs dictionary accessing via the [Dictionary Proxy Process](/main/core/config/dict.html#dictionary-proxy-process). (The dict processes exist only if dict proxying is used.) This is especially useful with drivers where their initialization is relatively expensive, such as SQL. The dict processes will perform connection pooling. #### `dict_proxy_idle_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to keep the connection open to dict server before disconnecting. `0` means immediate disconnection after finishing the operation. #### `dict_proxy_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_name](/main/core/summaries/settings.html#dict_name)` Name of the dict to access in the dict server. This refers to the `[dict_name](/main/core/summaries/settings.html#dict_name)` setting. #### `dict_proxy_slow_warn` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Log a warning about dict lookups that take longer than this interval. #### `dict_proxy_socket_path` **Default:** `dict` **Value:** [string](/main/core/settings/types.html#string) Points to the dict server's UNIX socket. The path is relative to the the `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. This should be changed to `dict-async` if the dict driver supports asynchronous lookups (e.g. ldap, pgsql, cassandra, NOT mysql). The `dict-async` service allows more than one client, so this configuration prevents creating unnecessarily many dict processes. ### Redis The Redis driver is recommended to be used via [`proxy`](#proxy) to support [connection pooling](#connection-pooling). WARNING Currently using Redis without proxying may cause crashes. #### `redis_db_id` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Database number. #### `redis_expire` **Default:** `infinite` **Value:** [time](/main/core/settings/types.html#time) Expiration value for all keys. #### `redis_host` **Default:** `127.0.0.1` **Value:** [string](/main/core/settings/types.html#string) Redis server host. #### `redis_key_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Prefix to add to all keys. #### `redis_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Redis server password. #### `redis_port` **Default:** `6379` **Value:** [Port Number](/main/core/settings/types.html#port-number) Redis server port. #### `redis_request_timeout` **Default:** `30s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait for answer before aborting request. #### `redis_socket_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) UNIX socket path to the Redis server. This is preferred over `[redis_host](/main/core/summaries/settings.html#redis_host)` if both are set. ### SQL WARNING Note that the SQL driver must be used with [`proxy`](#proxy). #### `dict_map` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` Creates a new dict mapping. The filter name refers to the `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` setting. #### `dict_map_expire_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Field in the SQL table to use for tracking dict key expiration. This field is optional if no expiration is used by the code accessing the dict map. #### `dict_map_key_field` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` Creates a new field for the dict map. The filter name refers to the `[dict_map_key_field_value](/main/core/summaries/settings.html#dict_map_key_field_value)` setting. The fields are part of the SQL query looking up the dict key. #### `dict_map_key_field_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Field in the SQL table to use for the `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. #### `dict_map_key_field_type` **Default:** `string` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `string``int``uint``double``hexblob``uuid` **See Also:** - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Type of the field in the SQL table for the `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. #### `dict_map_key_field_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Variable in the `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` that maps to this `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. The value must always begin with `$`. #### `dict_map_pattern` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Pattern that is matched to the accessed dict keys. The `[dict_map](/main/core/summaries/settings.html#dict_map)` filter name refers to this setting. If the pattern matches the key, this dict map (and no other) is used. The dict maps are processed in the order listed in the configuration file. #### `dict_map_sql_table` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL table to use for accessing this dict map. #### `dict_map_username_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Field in the SQL table to use for accessing private dict keys in this dict map. This setting is optional if only shared keys are accessed. #### `dict_map_value_field` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` - sql: Creates a new value for the dict map. The filter name refers to the `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` setting. Dict supports reading/writing multiple values for the same key. - ldap: Value to be returned from an ldap search, as a [variable-expression](/main/core/settings/variables.html) #### `dict_map_value_field_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)` Field in the SQL table to use for the `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)`. #### `dict_map_value_field_type` **Default:** `string` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `string``int``uint``double``hexblob``uuid` **See Also:** - `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)` Type of the field in the SQL table for the `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)`. #### SQL Mapping The SQL database fields are mapped into dict keys using the `[dict_map](/main/core/summaries/settings.html#dict_map)` setting. When a dict lookup or update is done, Dovecot goes through all the maps and uses the first one whose pattern matches the dict key. For example when using dict for a per-user quota value the map looks like: doveconf ``` dict_map priv/quota/storage { sql_table = quota username_field = username value_field quota_bytes { } } ``` - The dict key must match exactly `priv/quota/storage`. The dict keys are hardcoded in the Dovecot code, so depending on what functionality you're configuring you need to know the available dict keys used it. - This is a private dict key (`priv/` prefix), which means that there must be a `username_field`. The `username_field` is assumed to be (at least part of) the primary key. In this example we don't have any other primary keys. - With MySQL the above map translates to SQL queries: - `SELECT quota_bytes FROM quota WHERE username = '$username_field'` - `INSERT INTO quota (username, quota_bytes) VALUES ('$username_field', '$value') ON DUPLICATE KEY UPDATE quota_bytes='$value'` You can also access multiple SQL fields. For example `[acl_sharing_map](/main/core/summaries/settings.html#acl_sharing_map)` can contain: doveconf ``` dict_map shared/shared-boxes/user/$to/$from { sql_table = user_shares value_field dummy { } key_field from_user { value = $from } key_field to_user { value = $to } } ``` - The `[acl_sharing_map](/main/core/summaries/settings.html#acl_sharing_map)` always uses `1` as the value, so here the `value` is called `dummy`. - The SQL `from_user` and `to_user` fields are the interesting ones. Typically the extra fields would be part of the primary key. - With MySQL the above map translates to SQL queries: - `SELECT dummy FROM user_shares WHERE from_user = '$from' AND to_user = '$to'` - `INSERT INTO user_shares (from_user, to_user, dummy) VALUES ('$from', '$to', '$value') ON DUPLICATE KEY UPDATE dummy='$value'` #### SQL dict with `mail_attribute` It's possible to implement `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` also with SQL dict. WARNING Using shared attributes in `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` requires the mailbox GUID to be unique between users. This is not the case when mails were migrated via imapc, because it uses a hash of the mailbox name as the GUID. So every migrated user would have exactly the same INBOX GUID, preventing the use of dict-sql. It is currently not possible to add a username as an additional unique identifier. ``` # CREATE TABLE mailbox_private_attributes ( # username VARCHAR(255), # mailbox_guid VARCHAR(32), # attr_key VARCHAR(255), # attr_value TEXT, # PRIMARY KEY (username, mailbox_guid, attr_key) # ) dict_map priv/$mailbox_guid/$key { sql_table = mailbox_private_attributes username_field = user value_field attr_value { } key_field attr_key { value = $key } key_field mailbox_guid { value = $mailbox_guid } } # CREATE TABLE mailbox_shared_attributes ( # mailbox_guid VARCHAR(32), # attr_key VARCHAR(255), # attr_value TEXT, # PRIMARY KEY (mailbox_guid, attr_key) # ); dict_map shared/$mailbox_guid/$key { sql_table = mailbox_shared_attributes value_field attr_value { } key_field attr_key { value = $key } key_field mailbox_guid { value = $mailbox_guid } } ``` ## Dictionary Proxy Process Dict server is used for providing dictionary access via server processes instead of doing it directly from whichever process wants to access the dictionary. This is useful for some drivers with relatively high connection cost (e.g. [`sql`](#sql)), but not necessarily for others (e.g., [`redis`](#redis)). When a mail process uses the dict proxy, it needs to have access the dict UNIX socket. By default only the `dovecot` user has access to the dict socket, which doesn't typically work in any installation. However, giving too wide permissions by default might allow untrusted users to access the dict and cause problems. If all users share a single UNIX UID (e.g. `vmail`), you could make the dict socket accessible only to it: doveconf ``` service dict { unix_listener dict { mode = 0600 user = vmail } } ``` If you use multiple UNIX UIDs, you can add an extra group for all Dovecot mail processes. This works even if you have untrusted system users who have shell access to the server: doveconf ``` mail_access_groups = dovecot service dict { unix_listener dict { mode = 0660 group = dovecot } } ``` However, it works with [LDA](/main/core/config/delivery/lda.html) only if it's started as root. If this isn't possible, use [LMTP Server](/main/core/config/delivery/lmtp.html) instead. ### Settings #### `dict_server` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[dict](/main/core/summaries/settings.html#dict)` Named filter for the dict server settings. Add the available named dicts for the dict server under this filter using the `[dict](/main/core/summaries/settings.html#dict)` settings. For example: ``` dict_server { dict quota { driver = sql # ... } dict acl { driver = file # ... } } ``` # Event Export TIP See Also: - [All Dovecot Events](/main/core/summaries/events.html#all-dovecot-events), - [Event Filtering](/main/core/config/events/filter.html), - [Statistics](/main/core/config/statistics.html), and - [Events Design](/main/core/admin/events.html). ## Exporter Definition The `[event_exporter](/main/core/summaries/settings.html#event_exporter)` named list filter defines how [All Dovecot Events](/main/core/summaries/events.html#all-dovecot-events) should be exported. The basic definition is split into two orthogonal parts: the format and the driver. The format and its settings specify _how_ an event is serialized, while the driver and its settings specify _where_ the serialized event is sent. In both cases, the behavior is tweaked via the corresponding arguments setting. For example, the following block defines an exporter that uses the `http-post` driver and `json` format: doveconf ``` event_exporter http-localhost { driver = http-post http_post_url = http://localhost:1234/ http_client_request_absolute_timeout = 500msec format = json time_format = rfc3339 } ``` ### Formats The format and its settings specify _how_ an event is serialized. Supported Formats: | Formats | Description | | --- | --- | | `json` | JSON output | | `tab-text` | TAB-separated text fields | #### Example: JSON NOTE This example is pretty-printed. The actual exported event omits the whitespace between the various tokens. json ``` { "event" : "imap_command_finished", "hostname" : "dovecot-dev", "start_time" : "2019-06-19T10:38:25.422744Z", "end_time" : "2019-06-19T10:38:25.424812Z", "categories" : [ "imap" ], "fields" : { "net_in_bytes" : 7, "net_out_bytes" : 311, "last_run_time" : "2019-06-19T10:38:25.422709Z", "lock_wait_usecs" : 60, "name" : "SELECT", "running_usecs" : 1953, "session" : "xlBB1KqLz1isGwB+", "tag" : "a0005", "tagged_reply" : "OK [READ-WRITE] Select completed", "tagged_reply_state" : "OK", "user" : "jeffpc" } } ``` #### Example: tab-text ``` event:imap_command_finished hostname:dovecot-dev start_time:2019-06-19T10:38:25.422744Z end_time:2019-06-19T10:38:25.424812Z category:imap field:user=jeffpc field:session=xlBB1KqLz1isGwB+ field:tag=a0005 field:cmd_name=SELECT field:tagged_reply_state=OK field:tagged_reply=OK [READ-WRITE] Select completed field:last_run_time=2019-06-19T10:38:25.422709Z field:running_usecs=1953 field:lock_wait_usecs=60 field:net_in_bytes=7 field:net_out_bytes=311 ``` ## Drivers The driver and its settings specify _where_ the serialized event is sent. Supported drivers: | Driver | Description | | --- | --- | | `drop` | Ignore the serialized event | | `log` | Send serialized event to syslog | | `http-post` | Send the serialized event as a HTTP POST payload to `[event_exporter_http_post_url](/main/core/summaries/settings.html#event_exporter_http_post_url)`. The driver defaults to `[http_client_request_absolute_timeout = 250 milliseconds](/main/core/summaries/settings.html#http_client_request_absolute_timeout)`. | | `file` | Send serialized events to a file specified in `[event_exporter_file_path](/main/core/summaries/settings.html#event_exporter_file_path)` Added: 2.4.0 | | `unix` | Send serialised events to a unix socket specified in `[event_exporter_unix_path](/main/core/summaries/settings.html#event_exporter_unix_path)`. The `[event_exporter_unix_connect_timeout](/main/core/summaries/settings.html#event_exporter_unix_connect_timeout)` setting is used to specify how long the unix socket connection can take. Default is `250 milliseconds`. Added: 2.4.0 | The `drop` driver is useful when one wants to disable the event exporter temporarily. Note that serialization still occurs, but the resulting payload is simply freed. The `log` driver is useful for debugging as typically one is already looking at the logs. WARNING It is possible for the stats process to consume a large amount of memory buffering the POST requests if the timeout for `http-post` is set very high, a lot of events are being generated, and the HTTP server is slow. To reopen the files created by `file` driver, see `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html#reopen)`. ## Event Definition The event definition reuses and extends the `metric` config block used for statistics gathering. The only additions to the block are the `exporter` and `exporter_include` settings. These are only meaningful if the event matches the predicate (categories, filter, etc.) specified in the metric block. ### Filtering Events One uses the `metric` block settings documented in [Statistics](/main/core/config/statistics.html) to select and filter the event to be exported. See `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)` and `[metric_exporter_include](/main/core/summaries/settings.html#metric_exporter_include)` settings. ## Settings ### `event_exporter` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` Creates a new event exporter. The filter name refers to the `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` setting. ### `event_exporter_driver` **Default:** `log` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `log``file``unix``http-post``drop` **See Also:** - [Event Export: Drivers](/main/core/config/events/export.html#drivers) The event exporter driver to use. ### `event_exporter_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to event log file with `[event_exporter_driver = file](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_format` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Event Export: Formats](/main/core/config/events/export.html#formats) Format used for serializing the event. ### `event_exporter_http_post_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Target URL for `[event_exporter_driver = http-post](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)` Name of the event exporter. It is referred by the `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)` settings. ### `event_exporter_time_format` **Default:** `rfc3339` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `rfc3339``unix` **See Also:** - [Event Export: Formats](/main/core/config/events/export.html#formats) `rfc3339` Serialize timestamps as strings using the [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) format (`YYYY-MM-DDTHH:MM:SS.uuuuuuZ`). `unix` Serialize timestamps as a floating point number of seconds since the Unix epoch. ### `event_exporter_unix_connect_timeout` **Default:** `250ms` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[event_exporter_unix_path](/main/core/summaries/settings.html#event_exporter_unix_path)` Timeout when connecting to unix socket with `[event_exporter_driver = unix](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_unix_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[event_exporter_unix_connect_timeout](/main/core/summaries/settings.html#event_exporter_unix_connect_timeout)` Path to event unix socket with `[event_exporter_driver = unix](/main/core/summaries/settings.html#event_exporter_driver)`. ## Example Configs If one wishes to send the events associated with IMAP commands completion to a datalake having a HTTP API, one could use config such as: doveconf ``` event_exporter datalake { driver = http-post http_post_url = https://datalake.example.com/api/endpoint/somewhere http_client_request_absolute_timeout = 1sec format = json time_format = rfc3339 } metric imap_commands { exporter = datalake exporter_include = name hostname timestamps filter = event=imap_command_finished } ``` When debugging, it is sometimes useful to dump information to the log. For example, to output all named events from the IMAP service: doveconf ``` event_exporter log { driver = log format = json time_format = rfc3339 } metric imap_commands { exporter = log filter = event=* AND category=service:imap } ``` # Event Filtering Dovecot's event support includes the ability to narrow down which events are processed by filtering them based on the administrator-supplied predicate. Individual events can be identified either by their name or source code location. The source location of course can change between Dovecot versions, so it should be avoided. TIP See Also: - [All Dovecot Events](/main/core/summaries/events.html#all-dovecot-events), - [Event Export](/main/core/config/events/export.html), - [Statistics](/main/core/config/statistics.html), and - [Events Design](/main/core/admin/events.html). ## Matching Regardless of the syntax used, matching is performed the same way: - Event names are compared using a case-sensitive wildcard match. The wildcards supported are `?` and `*`. - If wildcard characters are needed as literal characters, they can be escaped with the `\` character, e.g. `\*`. - Event location is compared in two parts: the file name is compared case-sensitively, and the line number is compared as an integer. For a match to occur, the filename must match _and_ the line number must either match or be unspecified. - Event categories are compared using a "has a" relationship. A category in the filter must be present in the event for a match to occur. Any other categories on the event do not influence the match. - Event fields are compared using a case-insensitive wildcard match. The wildcards supported are `?` and `*`. ## Common (Unified) Filter Language The unified event filtering language is a SQL-like boolean expression that supports the `AND`, `OR`, and `NOT` boolean operators, the `=`, `<`. `>`, `<=`, and `>=` comparison operators, and parentheses to clarify evaluation order. The key-value comparisons are of the form: ` ` Where the key is one of: - `event` - `category` - `source_location` - a field name The operator is one of: - `=` - `>` - `<` - `>=` - `<=` And the value is either: - a single word token, or - a quoted string The value may contain wildcards if the comparison operator is `=`. The value comparison is case-insensitive, but the key is case-sensitive. There are some limitations on which operators work with what field types: - string: Only the `=` operator is supported. - ip: Only the `=` operator is supported. - The IPs are matched in their parsed form, e.g. `2001::1` matches `2001:0:0:0:0:0:0:1`. - The IPs can be matched against network bitmasks, e.g. `127.0.0.0/8` matches `127.4.3.2`. - Wildcards match the IP as if it was a string, i.e. `2001::1*` will match the IPs `2001::1` and `2001::1234`. However, `2001:0:0:0:0:0:0:1*` will not match either of them. - Link-local addresses match only against the same interface, e.g. `"fe80::1%lo"` won't match against `"fe80::1%eth0"`. Note that the `%` character needs to be inside a quoted string or event filter parsing fails. - number: All operators are supported. - Wildcards match the number as if it was a string, i.e. `40*` will match numbers `40` and `401`. - timestamp: No operators are supported. - a list of strings: Only the `=` operator is supported. It returns true if the key is one of the values in the list. If the value is an empty string, it returns true if the list is empty. Event fields have specific types that constrain the possible values they can be filtered by. For example, `net_out_bytes` and `message_size` are numeric and can only be matched against numeric values. Previously type mismatches were silently ignored, beginning with this version each type mismatch and unsupported operation generate a respective warning. Sizes can be expressed using the unit values `B` - which represents single byte values - as well as `KB`, `MB`, `GB` and `TB` which are all powers of 1024. If no unit is specified `B` is used by default. All size units are case-insensitive. Times can be specified with the units `milliseconds` (abbrev. `msecs`), `seconds` (abbrev. `secs`), `minutes` (abbrev. `mins`), `days`, and `weeks`. ### Examples For example, to match events with the event name `abc`, one would use one of the following expressions. Note that white space is not significant between tokens, and therefore the following are all equivalent: doveconf ``` event=abc event="abc" event = abc event = "abc" ``` A more complicated example: doveconf ``` event=abc OR (event=def AND (category=imap OR category=lmtp) AND \ NOT category=debug AND NOT (net_in_bytes<1024 OR net_out_bytes<1024)) ``` A complicated example using size matching: doveconf ``` (category=debug AND NOT (net_in_bytes<1KB OR net_out_bytes<1KB)) OR \ (event=abc AND (message_size>1gb and message_size<1tB)) OR \ (event=def AND (duration<1mins)) ``` ## Metric Filter Syntax Events can be filtered inside the `metric` blocks (see [Statistics](/main/core/config/statistics.html)) based on the event name, source location, the categories present, and field values. The `filter` metric key is set to the desired common filter language expression. For example: doveconf ``` metric example_http_metric { filter = event=http_request_finished AND \ source_location=http-client.c:123 AND category=storage AND \ category=imap AND user=testuser* AND status_code=200 } ``` ## Global Filter Syntax Settings such as `[log_debug](/main/core/summaries/settings.html#log_debug)` use the common filtering language. For example: doveconf ``` log_debug = (event=http_request_finished AND category=imap) OR \ (event=imap_command_finished AND user=testuser) ``` # Execute Scripts Some features, e.g. [welcome](/main/core/plugins/welcome.html) execute an external script. This is configured with the `[execute](/main/core/summaries/settings.html#execute)` settings. Currently only a single script execution at a time is supported. Supported script execution drivers are: | Name | Description | | --- | --- | | [unix](/main/core/config/execute.html#execute-unix) | Connect to UNIX socket. | | [tcp](/main/core/config/execute.html#execute-tcp) | Connect to TCP socket. | | [fork](/main/core/config/execute.html#execute-fork) | Fork and execute the script directly. | ## UNIX Sockets Execute the script via a script service listening on a UNIX socket. The service must execute the `script` binary to provide the proper communication API. Example: doveconf ``` execute test-script { #driver = unix # default args = hello %{user} } service test-script-service { executable = script /usr/local/bin/test-script.sh one unix_listener test-script { mode = 0666 } } ``` The `test-script.sh` is executed with parameters `one hello `. ## TCP sockets Execute the script via a script service listening on a TCP socket. The service must execute the `script` binary to provide the proper communication API. Example: doveconf ``` execute localhost:12345 { driver = tcp # default args = hello %{user} } service test-script-service { executable = script /usr/local/bin/test-script.sh one inet_listener script { port = 12345 } } ``` The `test-script.sh` is executed with parameters `one hello `. ## Fork and Execute Fork the process and execute the script directly. Example: doveconf ``` execute /usr/local/bin/test-script.sh { driver = fork args = hello %{user} } ``` The `test-script.sh` is executed with parameters `hello `. ## Execute Settings ### `execute` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Execute Scripts](/main/core/config/execute.html) Configure external execution script. Used by various different features, such as [welcome](/main/core/plugins/welcome.html). Currently only a single execute block (per feature) is allowed. ### `execute_args` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) External execution script arguments. The parameters are split by space characters. Currently escape characters are not supported. ### `execute_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `unix``fork``tcp` **See Also:** - [Execute Scripts](/main/core/config/execute.html) How to execute the external script: `unix` Use UNIX socket connection to a `script` service listening in `[execute_unix_socket_path](/main/core/summaries/settings.html#execute_unix_socket_path)`. `fork` Fork and execute the `[execute_fork_path](/main/core/summaries/settings.html#execute_fork_path)` binary directly. `tcp` Use TCP connection to a `script` service listening in `[execute_tcp_host](/main/core/summaries/settings.html#execute_tcp_host)`:`[execute_tcp_port](/main/core/summaries/settings.html#execute_tcp_port)`. ### `execute_fork_path` **Default:** `[execute_name](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) Path to the binary that is executed with `[execute_driver = fork](/main/core/summaries/settings.html#execute_driver)`. ### `execute_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the execution script. This is the `[execute](/main/core/summaries/settings.html#execute)` named filter name. It is also used to provide a default driver-specific settings: `unix` Used as the default for `[execute_unix_socket_path](/main/core/summaries/settings.html#execute_unix_socket_path)`. `fork` Used as the default for `[execute_fork_path](/main/core/summaries/settings.html#execute_fork_path)`. `tcp` Used as the default for `[execute_tcp_host](/main/core/summaries/settings.html#execute_tcp_host)`:`[execute_tcp_port](/main/core/summaries/settings.html#execute_tcp_port)`. ### `execute_tcp_host` **Default:** `[execute_name = host:](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) TCP host where to connect to with `[execute_driver = tcp](/main/core/summaries/settings.html#execute_driver)`. ### `execute_tcp_port` **Default:** `[execute_name = :port](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) TCP port where to connect to with `[execute_driver = tcp](/main/core/summaries/settings.html#execute_driver)`. ### `execute_unix_socket_path` **Default:** `[execute_name](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) UNIX socket path where to connect to with `[execute_driver = unix](/main/core/summaries/settings.html#execute_driver)`. # Dovecot Filesystems Dovecot's `lib-fs` is a simplified API to access filesystems and databases that can be made to look similar to filesystems. It is similar to [dictionary](/main/core/config/dict.html) but generally where [dictionary](/main/core/config/dict.html) is generally used for small data `fs` is used for larger data. Currently supported FS drivers are: | Name | Description | | --- | --- | | [posix](/main/core/config/fs.html#posix-filesystem) | POSIX filesystem. | | [dict](/main/core/config/fs.html#dictionary-filesystem) | Dictionary (`lib-dict` wrapper). | ## Dovecot Filesystem Wrappers Wrapper drivers used on top of other drivers: | Name | Description | | --- | --- | | [metawrap](/main/core/config/fs.html#metawrap-filesystem) | File metadata. | | [crypt](/main/core/plugins/mail_crypt.html#mail-crypt-plugin-mail-crypt) | File encryption. | | [compress](/main/core/plugins/fs_compress.html#compression-fs-compress-plugin) | File compression. | When using fs drivers, place the fs wrappers first in the configuration and the primary fs driver as the last one. ### Examples Enable [metawrap](/main/core/config/fs.html#metawrap-filesystem) to support storing metadata with [posix](/main/core/config/fs.html#posix-filesystem) by configuring the `fs metawrap {}` block before the `fs posix {}` block: Example: doveconf ``` fs metawrap { } fs posix { } ``` It is also possible to use multiple fs wrappers on top of each other like this: Example: doveconf ``` fs metawrap { } fs compress { } fs posix { } ``` ## FS Settings ### `fs` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Dovecot Filesystems](/main/core/config/fs.html) - `[fs_name](/main/core/summaries/settings.html#fs_name)` - `[fs_driver](/main/core/summaries/settings.html#fs_driver)` Create a new [fs](/main/core/config/fs.html) to the list of filesystems. The filter name refers to `[fs_name](/main/core/summaries/settings.html#fs_name)` setting. Example: ``` fs posix { # ... } ``` Since an empty `[fs_driver](/main/core/summaries/settings.html#fs_driver)` default to `[fs_name](/main/core/summaries/settings.html#fs_name)` there is no need to specify `[fs_driver](/main/core/summaries/settings.html#fs_driver)` explicitly. It's possible to specify the same `fs` multiple times by separating the `[fs_name](/main/core/summaries/settings.html#fs_name)` and `[fs_driver](/main/core/summaries/settings.html#fs_driver)` settings: ``` fs compress1 { fs_driver = compress } fs compress2 { fs_driver = compress } ``` ### `fs_driver` **Default:** `[fs_name](/main/core/summaries/settings.html#fs_name)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fs](/main/core/summaries/settings.html#fs)` - `[fs_name](/main/core/summaries/settings.html#fs_name)` The `[fs](/main/core/summaries/settings.html#fs)` driver to use. Defaults to `[fs_name](/main/core/summaries/settings.html#fs_name)`. ### `fs_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fs](/main/core/summaries/settings.html#fs)` - `[fs_driver](/main/core/summaries/settings.html#fs_driver)` Name of the `[fs](/main/core/summaries/settings.html#fs)`. The `[fs_driver](/main/core/summaries/settings.html#fs_driver)` setting default to this. ## POSIX Filesystem Regular POSIX filesystem. It can also be used with [NFS](/main/core/config/nfs.html). It doesn't support file metadata, in case you have a need for that use [fs: Metawrap Filesystem](/main/core/config/fs.html#metawrap-filesystem). ### Settings ### `fs_posix_accurate_mtime` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | Configure whether `utimes()` is called after writes to guarantee microsecond precision timestamps for files. By default Linux updates the `mtime` only on timer interrupts, which is not remotely close to microsecond precision. ### `fs_posix_autodelete_empty_directories` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | If the last file in a directory is deleted, should the parent directory be automatically deleted? INFO Using this setting makes the POSIX filesystem behave more like an object storage would. WARNING This setting can cause the POSIX filesystem to also delete the parent directory hierarchy farther up than anticipated. ### `fs_posix_fsync` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Configure whether `fsync()` is called after writes to guarantee that the file is written to disk. ### `fs_posix_lock_method` | Key 1 | | --- | | Default | `flock` | | Value | [string](/main/core/settings/types.html#string) | | Allowed Values | `flock``dotlock` | | Advanced Setting; this should not normally be changed. | Lock method to use for locking files. Currently nothing uses `lib-fs` locking. ### `fs_posix_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode to use for creating files. ### `fs_posix_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Directory prefix where files are read from/written to. INFO The trailing `/` is not automatically added, so using e.g. `/tmp/foo` as prefix will cause `/tmp/foofilename` to be created. ## Dictionary Filesystem This is a wrapper for `lib-dict` for using [dictionary](/main/core/config/dict.html) drivers as `fs` drivers. ### Settings ### `fs_dict_value_encoding` **Default:** `raw` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `raw``hex``base64` How to encode file contents into the dict value. ## Metawrap Filesystem This is a wrapper for other `fs` drivers that don't support metadata. The metadata is implemented by placing them into the beginning of the file content. # Health Check Scripting If you need dovecot to offer health-check functionality, use the health-check service by extending it's configuration with a listener like this: doveconf ``` service health-check { # this is the default configuration using the simple PING->PONG # example health-check. executable = script -p health-check.sh inet_listener health-check { port = 5001 } } ``` ## Options ### `-e` Parameter Define a list of environment variables which can be set by a request before calling the health-check script. Example: `script -e foo bar health-check.sh` sets `FOO=BAR` environment variable. ### `-p` Parameter Enables passthrough mode which allows to directly call a script without any protocol and directly receive the output. Be extremely careful when modifying the `health-check.sh` script or implementing your own. ## Script-Protocol If the passthrough mode is not enabled, the request must implement the following protocol: ``` VERSION .. [alarm= ] [env_= ] [env_= ] ... "noreply" | "-" (or anything really) arg 1 arg 2 ... DATA ``` If "alarm" is specified, it MUST be before "noreply". If "noreply" isn't given, a "-" must be given. ### `arg` Arguments that can be passed to the script. ### `env_` Environment variables that have been marked as allowed by `-e`. ### `noreply` Disable the success/fail answer by script executable itself. ### `VERSION` The VERSION of script (AToW 4.0 eg "VERSION\\tscript\\t4\\t0\\n"). ### `DATA` Input to be passed to the script to be called. # IMAP Configuration Dovecot was optimized since the beginning to work as an efficient IMAP server. ## Namespaces See [Namespaces](/main/core/config/namespaces.html). ## IMAP Extensions Dovecot supports many [IMAP extensions](https://imapwiki.org/Specs). ### COMPRESS Dovecot supports the IMAP COMPRESS ([RFC 4978](https://datatracker.ietf.org/doc/html/rfc4978)) extension. It allows an IMAP client to dynamically enable stream compression for an IMAP session. The extension is enabled by default and configured with the default compression level for the available mechanism. When the client connection comes through a Dovecot proxy, the compression is by default handled by the proxy instead of the backend's imap process. See `[imap_compress_on_proxy](/main/core/summaries/settings.html#imap_compress_on_proxy)`. ### SEARCH=FUZZY IMAP provides SEARCH as part of the core protocol, so it is useful to activate a Full Text Search indexing driver to handle these searches. See [fts plugin](/main/core/plugins/fts.html). ### METADATA Dovecot supports the IMAP METADATA extension ([RFC 5464](https://datatracker.ietf.org/doc/html/rfc5464)), which allows per-mailbox, per-user data to be stored and accessed via IMAP commands. See `[imap_metadata](/main/core/summaries/settings.html#imap_metadata)` for configuration instructions. #### Storing Metadata in SQL Dictionary You can store metadata into a database. This works best with a dedicated table for storing the entries. SQL Schema/etc/dovecot/dovecot.conf sql ``` -- Since username is a primary key, it is required to have some value. -- When empty, it means that the value applies to keys with 'shared/' prefix. -- Keys with 'priv/' prefix are expected to have a non-empty username. CREATE TABLE metadata ( username VARCHAR(255) NOT NULL DEFAULT '', attr_name VARCHAR(255) NOT NULL, attr_value TEXT, PRIMARY KEY(username, attr_name) ); ``` ``` dict_server { dict metadata { driver = sql sql_driver = mysql dict_map $key { sql_table = attr_priv username_field = username key_field attr_name { value = $key } value_field attr_value { } } } } mail_attribute { dict proxy { name = metadata } } ``` ### SPECIAL-USE No special-use mailboxes are configured by default. However, you can use `[@mailbox_defaults](/main/core/summaries/settings.html#@mailbox_defaults)` group to include the recommended default mailboxes with special-use flags configured. Alternatively, you can configure the `[mailbox](/main/core/summaries/settings.html#mailbox)` settings explicitly with `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)`. ### PREVIEW Dovecot supports the PREVIEW extension ([RFC 8970](https://datatracker.ietf.org/doc/html/rfc8970)), retrieved via the IMAP FETCH command. The extension is enabled by default. Preview text is generated during message delivery and is stored in the Dovecot index files. ### NOTIFY Set `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)`. ### URLAUTH: Set `[imap_urlauth_host](/main/core/summaries/settings.html#imap_urlauth_host)` and `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)`. ## IMAP Hibernation WARNING This is not supported on kqueue based systems currently, such as FreeBSD. Dovecot supports moving connections that have issued IDLE to a special holding process, called `imap-hibernate`. This process is responsible for holding the idling connections until they issue some command that requires them to be thawed back into a (new) imap process. This way, memory and CPU resources are saved, since there is only one hibernation process. ### Configuration `[imap_hibernate_timeout](/main/core/summaries/settings.html#imap_hibernate_timeout)` specifies the delay before moving users to `imap-hibernate` process. This requires inter-process communication between `imap` and `imap-hibernate` process. doveconf ``` imap_hibernate_timeout = 5s service imap { # Note that this change will allow any process running as # $SET:default_internal_user (dovecot) to access mails as any other user. # This may be insecure in some installations, which is why this isn't # done by default. unix_listener imap-master { user = $SET:default_internal_user } } # The following is the default already service imap { extra_groups = $default_internal_group } service imap-hibernate { unix_listener imap-hibernate { mode = 0660 group = $default_internal_group } } ``` # Login Processes The main purpose of login processes is to handle the [IMAP server](/main/core/config/imap.html), [POP3 server](/main/core/config/pop3.html), [submission server](/main/core/config/submission.html), and [ManageSieve server](/main/core/config/sieve/managesieve.html) connections before the user has logged in. The login processes don't need to be able to do anything else than let the user log in, so they can run in highly restricted environment. By default they are run as a non-privileged `dovenull` user chrooted into a non-writable directory containing only authentication UNIX sockets. Login processes also handle proxying the SSL and TLS connections even after the user has logged in. This way all the SSL code runs in the same restricted environment, which means that a security hole in the SSL library gives the attacker access only to the restricted chroot, rather than possibly all the users' mails. The default login settings should be good enough for small sites. There are two ways to run the login processes: the high-security mode and the high-performance mode. Both are discussed separately below. For explanation on the various settings for services, see [services configuration](/main/core/config/service.html). ## High-Security Mode (default) You can enable high-security mode with: doveconf ``` service imap-login { restart_request_count = 1 #process_min_avail = 0 } service pop3-login { restart_request_count = 1 } ``` It works by using a new imap-login or pop3-login process for each incoming connection. Since the processes run in a highly restricted chroot, running each connection in a separate process means that in case there is a security hole in Dovecot's pre-authentication code or in the SSL library, the attacker can't see other users' connections and can't really do anything destructive. The only way out of it is to find and exploit a kernel security hole. Since one login process can handle only one connection, the service's `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` setting limits the number of users that can be logging in at the same time (defaults to `[default_process_limit](/main/core/summaries/settings.html#default_process_limit)`). SSL/TLS proxying processes are also counted here, so if you're using SSL/TLS you'll need to make sure this count is higher than the maximum number of users that can be logged in simultaneously. With TLS/SSL connections, the login process will not terminate, and remains to perform proxying between imap backend process and the client. - If the maximum login process count is reached, the oldest process in logging-in state (ie. non-proxying) is destroyed. - To avoid startup latency for new client connections, set `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` to higher than zero. That many idling processes are always kept around waiting for new connections. - `[service_vsz_limit](/main/core/summaries/settings.html#service_vsz_limit)` should be fine at its default value. ## High-Performance Mode You can enable high-performance mode with: doveconf ``` service imap-login { restart_request_count = unlimited process_min_avail = %{system:cpu_count} vsz_limit = 1G } service pop3-login { restart_request_count = unlimited } ``` It works by using a number of long running login processes, each handling a number of connections. This loses much of the security benefits of the login process design, because in case of a security hole (in Dovecot or SSL library) the attacker is now able to see other users logging in and steal their passwords, read their mails, etc. - `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` should be set to be at least the number of CPU cores in the system, so that all of them will be used. You can use `%{system:cpu_count}`, which expands to this automatically. - Otherwise new processes are created only once an existing one's connection count reaches `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`. - Default `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` \* `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` = 1000 \* 100 = 100k connections. - `[service_vsz_limit](/main/core/summaries/settings.html#service_vsz_limit)` should be increased to avoid out of memory errors, especially if you're using SSL/TLS. ## Configuring Socket Paths for Login Processes The authentication UNIX socket is "login" by default. `[login_socket_path](/main/core/summaries/settings.html#login_socket_path)` allows to configure this path for all login processes. For individual processes this can be overridden by supplying a parameter to the appropriate service's executable. This example sets up the global socket "general-login-socket" but overrides this for the imap-login process individually (in `dovecot.conf`): doveconf ``` login_socket_path = general-login-socket service imap-login { executable = imap-login specific-login-socket } ``` # Mail Cache Configuration Dovecot caches the mail headers and other fields to `dovecot.index.cache` files automatically based on what the IMAP client uses. This is a per-folder decision. This works generally well for newly created folders, but not so well during migration, because Dovecot doesn't yet known which fields need to be cached. So Dovecot needs to be told what to initially add to `dovecot.index.cache` while mails are being saved. This can be useful even after migration. For example a user might normally use their mobile app IMAP client, but once a few months they would login to the webmail. Dovecot would normally preserve the fields used by the mobile IMAP client, but drop the extra fields used only by webmail after a month. This might not be wanted. This behavior can be configured with: - `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)`: List of fields that are initially cached for newly created users. Afterwards the caching decisions will live on based on the user's IMAP access patterns. Note that the INBOX's caching decisions are copied to newly created folders. - `[mail_always_cache_fields](/main/core/summaries/settings.html#mail_always_cache_fields)`: List of fields that are always cached for everyone. These fields won't get dropped automatically even if user never accesses them. - `[mail_never_cache_fields](/main/core/summaries/settings.html#mail_never_cache_fields)`: List of fields that should never be cached. This should probably never include anything other than imap.envelope, which isn't needed because it can be generated from the cached header fields. ## Settings For Clients The list of cached fields depends on which IMAP clients are expected to be used. These are commonly used: ### Common IMAP Clients IMAP ENVELOPE is used by many clients, which includes: `hdr.date hdr.subject hdr.from hdr.sender hdr.reply-to hdr.to hdr.cc hdr.bcc hdr.in-reply-to hdr.message-id` ### Open-Xchange App Suite - IMAP ENVELOPE - `flags date.received imap.bodystructure mime.parts hdr.importance hdr.x-priority hdr.references body.snippet hdr.x-open-xchange-share-url` ### POP3 Clients `pop3.uidl pop3.order` ### iOS 9.3.1 - Some (but not all) of the IMAP ENVELOPE headers: `hdr.date hdr.subject hdr.from hdr.to hdr.cc hdr.bcc hdr.message-id hdr.in-reply-to` - `hdr.content-type hdr.references` ### K-9 Mail `date subject from content-type to cc reply-to message-id references in-reply-to X-K9mail-Identity` ## Cache Fields | Field | Description | | --- | --- | | `flags` | Tracks various boolean flags for the mail: Does the header/body have CRLF linefeeds? Does it have NUL characters? | | `date.sent` | The Date: header parsed to timestamp | | `date.received` | Mail delivery date (IMAP INTERNALDATE) | | `date.save` | Mail save/copy date (mdbox stores this always in dovecot.index) | | `size.physical` | Physical message size (line feeds exactly as they are stored in the stored mail). Note that the size is of the mail as plaintext, i.e. after decryption/compression. This is typically used by [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs) or [Quota Driver: Maildir](/main/core/plugins/quota.html#driver-maildir) quota. | | `imap.bodystructure` | IMAP BODYSTRUCTURE response, which describes what the message's MIME structure looks like. | | `imap.body` | IMAP BODY response. This is the short version of imap.bodystructure, not the message body itself. If imap.bodystructure is cached, this field isn't cached because it can be generated from the imap.bodystructure. | | `imap.envelope` | IMAP ENVELOPE response, which contains the From, To, Cc, Bcc, Sender, Reply-To, Date, Subject, Message-ID and In-Reply-To headers in parsed forms. This is typically in the `[mail_never_cache_fields](/main/core/summaries/settings.html#mail_never_cache_fields)` because the raw headers are more useful in the cache and the ENVELOPE can be generated from them. | | `pop3.uidl` | POP3 UIDL responses. This is useful especially if some of the UIDLs have been migrated from an old system or if `[pop3_reuse_xuidl](/main/core/summaries/settings.html#pop3_reuse_xuidl)` is used. Otherwise Dovecot generates the UIDL in a way that usually doesn't require cache. | | `pop3.order` | POP3 messages' order. This is used after migration from another system where the IMAP and POP3 messages' order differs. | | `guid` | Internal Dovecot GUID for messages. | | `mime.parts` | MIME parts' sizes. | | `binary.parts` | MIME parts' sizes after decoding Content-Transfer-Encoding to binary. Used by IMAP BINARY extension. | | `body.snippet` | A short snippet of the message body. | | `hdr.*` | Message headers listed individually, e.g. `hdr.date`, `hdr.from` | ## Cache Decision Algorithm Nowadays there are three types of IMAP clients: - Clients that download all the (new) messages' headers and other metadata into local cache. Afterwards they use only the local cache for accessing the metadata. They may download the message bodies later on though. These clients don't benefit from Dovecot's caching after they have downloaded the metadata. For example Outlook, Thunderbird, Mac OS X Mail. - Clients that work otherwise like the local clients described above, but don't download all mails locally (even their metadata). They may also use server-side search for mails. For example iOS Mail. - Clients that don't have a local cache at all, or only a short-lived cache. These clients would benefit from having all mails cached by Dovecot. For example webmails. IMAP clients don't advertise how they work, so Dovecot attempts to figure it out dynamically. The behavior is now: - For a newly created INBOX Dovecot gets the caching decisions from the `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)` and `[mail_always_cache_fields](/main/core/summaries/settings.html#mail_always_cache_fields)` settings. - For a newly created non-INBOX folder the caching decisions are copied from the INBOX. - Whenever a new non-cached field is accessed, its caching decision is set to TEMP, which means only the last 1 week's mails have the field cached. - Whenever a field is accessed for a mail older than 1 week the caching decision changes from TEMP to YES, which means the field is cached for all mails. - Whenever a single IMAP session accesses the mails in non-ascending order (e.g. mail UIDs 100 -> 99 instead of 99 -> 100) the caching decision changes from TEMP to YES. Especially the SORT/THREAD commands trigger this. - Whenever cache file is recreated (purged) it can cause some fields' decisions to change: - Changes YES -> TEMP if the YES decision hasn't been reconfirmed for the last 30 days (`[mail_cache_unaccessed_field_drop](/main/core/summaries/settings.html#mail_cache_unaccessed_field_drop)`). - Changes TEMP -> NO and drops the field if it hasn't been accessed for the last 60 days (2 \* `[mail_cache_unaccessed_field_drop](/main/core/summaries/settings.html#mail_cache_unaccessed_field_drop)`). # Mail Location ## Settings ### `mail_alt_check` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_alt_path](/main/core/summaries/settings.html#mail_alt_path)` Whether to perform a sanity check and warn if `[mail_alt_path](/main/core/summaries/settings.html#mail_alt_path)` changes from the last access. This can catch accidentally broken configurations before users start reporting missing mails. The downside to this check is some additional disk IO. ### `mail_alt_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [dbox alternate storage](/main/core/config/mailbox_formats/dbox.html#alternate-storage) Specifies the [dbox alternate storage](/main/core/config/mailbox_formats/dbox.html#alternate-storage) path. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_cache_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) Place `dovecot.index.cache` files to this directory instead of among the other index files. This may be used as an optimization to split most index files to the fastest (smallest) storage while keeping cache files in a slightly slower (larger) storage. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_control_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Location for (mailbox-format specific) control files. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats) - [Mailbox Root Autodetection](/main/core/config/mail_location.html#mailbox-root-autodetection) - `[mail_path](/main/core/summaries/settings.html#mail_path)` One of the mailbox formats described at [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats). For an empty value or `auto`, Dovecot attempts to find the mailboxes automatically. ### `mail_inbox_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_home](/main/core/summaries/settings.html#mail_home)` - `[mail_path](/main/core/summaries/settings.html#mail_path)` Path to the INBOX mailbox. The path doesn't have to be absolute - it is relative to the `[mail_path](/main/core/summaries/settings.html#mail_path)`. WARNING If your inbox _does not_ reside in some weird location, you should _not_ set this setting to any value. Setting it to `.` or `$MAIL_HOME/` will just cause problems. You almost certainly want to change `mail_home` or `mail_path` settings instead. This is often used with mbox format where INBOX is in `/var/mail/` while the rest of the folders are under the user's home directory. This can also be used to specify a different INBOX path with Maildir: ``` mail_driver = maildir mail_path = ~/Maildir mail_inbox_path = ~/Maildir/.INBOX ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_index_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mail Location: Index Files](/main/core/config/mail_location.html#index-files) Location of [index files](/main/core/config/mail_location.html#index-files). `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_index_private_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [public shared mailboxes](/main/core/config/shared_mailboxes.html#public-shared-mailboxes) The private index files are used with shared mailboxes to provide private (per-user) message flags. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mail User Variables](/main/core/settings/variables.html#mail-user-variables) - [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) - `[mail_driver](/main/core/summaries/settings.html#mail_driver)` Path to a directory where the mail is stored. [Mail User Variables](/main/core/settings/variables.html#mail-user-variables) are commonly used here. Usually the mails should be stored in a sub-directory under the home directory, but not the home directory itself (see [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories)). The path must be absolute, not a relative path. Even if relative paths appear to work, this usage is deprecated and will likely stop working at some point. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_volatile_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Changed: 2.4.5 Fixed behavior to avoid collisions across multiple namespaces. Specifies the location of volatile files. This includes lock files and potentially other files that don't need to exist permanently, so it can point to an in-memory filesystem (`tmpfs`). This is especially useful to avoid creating lock files to NFS or other remote filesystems. This setting can be shared between multiple namespaces. Dovecot makes the filenames unique for each namespace to avoid collisions. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mailbox_directory_name` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_driver](/main/core/summaries/settings.html#mail_driver)` - `[mailbox_directory_name_legacy](/main/core/summaries/settings.html#mailbox_directory_name_legacy)` Specifies the directory name used for mailbox, index, and control directory paths. See the individual mailbox format pages for further information. For example `dbox-Mails` with [dbox](/main/core/config/mailbox_formats/dbox.html). With mbox format this works differently. The user-provided mailbox name is the directory name, while `mailbox_directory_name` is the mbox file. For example with `mailbox_directory_name=mbox`, creating `foo/bar` mailbox name ends up creating `.../foo/bar/mbox` file. ### `mailbox_directory_name_legacy` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` **Changes:** - Changed: 2.4.2 The default value changed from `yes` to `no`. - Deprecated: 2.4.2 This legacy setting is obsolete and destined to be removed. Changing this setting requires migration, see [dbox: Migrating away from \`mailbox\_directory\_name\_legacy\`](/main/core/config/mailbox_formats/dbox.html#migrating-away-from-mailbox-directory-name-legacy) If `no`, `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` applies also to index and control directories. The only reason to set this to `yes` is if you already have an existing Dovecot installation with the legacy `DIRNAME` (rather than `FULLDIRNAME`) parameter and don't want to migrate the data. ### `mailbox_list_drop_noselect` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Specifies whether to automatically delete `NoSelect` mailboxes that have no children. These mailboxes are sometimes confusing to users. Also if a `NoSelect` mailbox is attempted to be created with `CREATE box/`, it's created as selectable mailbox instead. INFO [Maildir](/main/core/config/mailbox_formats/maildir.html) layout does not support `NoSelect` mailboxes, so this setting has no effect with it. ### `mailbox_list_index_prefix` **Default:** `dovecot.list.index` **Value:** [string](/main/core/settings/types.html#string) Prefix for the mailbox list index filename. It may also optionally include a path (relative to `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)`) to place it in a different directory. ### `mailbox_list_iter_from_index_dir` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Perform mailbox listing using the `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` directories instead of the `[mail_path](/main/core/summaries/settings.html#mail_path)` directories. Mainly useful when the index file storage is on a faster storage. ### `mailbox_list_layout` **Default:** `fs` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `fs``index``Maildir++` **See Also:** - [dbox](/main/core/config/mailbox_formats/dbox.html) - [Maildir](/main/core/config/mailbox_formats/maildir.html) - [mbox](/main/core/config/mailbox_formats/mbox.html) Directory layout to use. | Value | Description | | --- | --- | | `Maildir++` | The default used by [Maildir](/main/core/config/mailbox_formats/maildir.html). | | `fs` | The default used by [mbox](/main/core/config/mailbox_formats/mbox.html) and [dbox](/main/core/config/mailbox_formats/dbox.html). | | `index` | Uses mailbox GUIDs as the directory names. The mapping between mailbox names and GUIDs exists in `dovecot.list.index*` files. | INFO The `[mail_driver](/main/core/summaries/settings.html#mail_driver)` setting provides the default value, that the `mailbox_list_layout` setting can override. ### `mailbox_list_storage_escape_char` **Default:** The default for [imapc](/main/core/config/mailbox_formats/imapc.html) storage is `%`. **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mailbox_list_visible_escape_char](/main/core/summaries/settings.html#mailbox_list_visible_escape_char)` Specifies an escape character that it used for encoding special characters in the mailbox names in storage. This allows users to use characters in mailboxes names that would otherwise be illegal. For example: - [Maildir](/main/core/config/mailbox_formats/maildir.html) layout disallows using the `.` character, since it's used internally as the folder hierarchy separator. - The `~` character at the beginning of the mailbox name is disallowed, because of the possibility that it gets expanded to user's home directory. - The `/` character can't be used on POSIX filesystems, since it's the directory separator. The characters are escaped to the mailbox name as ``. INFO It's possible to use the same character here as for `[mailbox_list_visible_escape_char](/main/core/summaries/settings.html#mailbox_list_visible_escape_char)`. ### `mailbox_list_utf8` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Store mailbox names on disk using UTF-8 instead of modified UTF-7 (mUTF-7). This applies also to the mailbox list index with `[mailbox_list_layout = index](/main/core/summaries/settings.html#mailbox_list_layout)`. WARNING Changing this breaks already existing non-ASCII mailbox names. ### `mailbox_list_validate_fs_names` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_full_filesystem_access](/main/core/summaries/settings.html#mail_full_filesystem_access)` Specifies whether to disallow mailbox names that might be unsafe to use in filesystems or potentially allow bypassing ACL checks: - `/` character anywhere in the name (except as a hierarchy separator), unless mailbox list isn't on a filesystem (e.g. index, imapc). - `/` as the first character. - `~` as the first character (so it's not confused as home directory). - No adjacent `/` characters. - No `.` or `..` names between `/` characters. - No `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` between `/` characters. - No mailbox format-specific internal directories between `/` characters, - unless `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` is non-empty. This mainly means the Maildir `new`, `cur` and `tmp` directories in some configurations. Enabling `[mail_full_filesystem_access](/main/core/summaries/settings.html#mail_full_filesystem_access)` enables also this setting. ### `mailbox_list_visible_escape_char` **Default:** The default for [imapc](/main/core/config/mailbox_formats/imapc.html) storage is `~`. **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mailbox_list_storage_escape_char](/main/core/summaries/settings.html#mailbox_list_storage_escape_char)` Specifies an escape character that is used for broken or otherwise inaccessible mailbox names. If mailbox name can't be changed reversibly to UTF-8 and back, encode the problematic parts using ``in the user-visible UTF-8 name. The `mailbox_list_visible_escape_char` itself also has to be encoded the same way. This can be useful with [imapc](/main/core/config/mailbox_formats/imapc.html) to access mailbox names that aren't valid mUTF-7 charset from remote servers, or if the remote server uses a different hierarchy separator and has folder names containing the local separator. INFO It's possible to use the same character here as for `[mailbox_list_storage_escape_char](/main/core/summaries/settings.html#mailbox_list_storage_escape_char)`. ### `mailbox_root_directory_name` **Default:** `specific to the mail_driver setting` **Value:** [string](/main/core/settings/types.html#string) Specifies directory name under which all mailbox directories are stored. For example `mailboxes` with [dbox](/main/core/config/mailbox_formats/dbox.html). ### `mailbox_subscriptions_filename` | Key 1 | | --- | | Default | `subscriptions` specific [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats) have different defaults | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Specifies the filename used for storing mailbox subscriptions. ## Variables You can use several variables in the mail location settings. See `[Settings variables](/main/core/settings/variables.html)` for a full list, but the most commonly used ones are: | Variable | Description | | --- | --- | | `%{user}` | Full username. | | `%{user | username}` | User part in `user@domain`; same as `%{user}` if there's no domain. | | `%{user | domain}` | Domain part in `user@domain`; empty if there's no domain. | ### Directory Hashing Examples on how to do it: - `%{ user | sha1 % 256 | hex(2)}` would give maximum 256 different hashes in range of `00` to `ff`. See also [Directory hashing](/main/installation/upgrade/2.3-to-2.4.html#directory-hashing) ## Index Files Index files are by default stored under the same directory as mails. You may want to change the index file location if you're using [NFS](/main/core/config/nfs.html) or if you're setting up [shared mailboxes](/main/core/config/shared_mailboxes.html). You can change the index file location with the `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` setting. For example: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mail_index_path = /var/indexes/%{user} ``` The index directories are created automatically, but note that it requires that Dovecot has actually access to create the directories. Either make sure that the index root directory (`/var/indexes` in the above example) is writable to the logged in user, or create the user's directory with proper permissions before the user logs in. Index files can be disabled completely with `mail_index_path=MEMORY`. This is not recommended for production use, as the index files will need to be generated on every access. ## Mailbox Root Autodetection By default the `[mail_driver](/main/core/summaries/settings.html#mail_driver)` and `[mail_path](/main/core/summaries/settings.html#mail_path)` settings are empty, which means that Dovecot attempts to locate automatically where your mails are. This is done by looking, in order, at: - `~/mdbox/` - `~/sdbox/` - `~/Maildir/` - `~/mail/.imap/` - `~/mail/inbox` - `~/mail/mbox` - `~/Mail/.imap/` - `~/Mail/inbox` - `~/Mail/mbox` TIP `.imap` is a directory, and `inbox` and `mbox` are files. For autodetection to work, one of the above locations has to be populated; when autodetection is active, Dovecot will not attempt to create a mail folder. It's usually a good idea to explicitly specify where the mails are, even if the autodetection happens to work, in particular to benefit from auto-creation of the folder for new users. ### Custom Autodetection If you need something besides the default autodetection, you can use [post-login scripting](/main/core/config/post_login_scripting.html). Example Script sh ``` #!/bin/sh if [ -d $HOME/.maildir ]; then export MAIL_DRIVER=maildir export MAIL_PATH=$HOME/.maildir else export MAIL_DRIVER=mbox export MAIL_PATH=$HOME/mail export MAIL_INBOX_PATH=/var/mail/$USER fi export USERDB_KEYS="$USERDB_KEYS mail_driver mail_path mail_inbox_path" exec "$@" ``` ## Mail Storage Autocreation If `[mail_path](/main/core/summaries/settings.html#mail_path)` is set, the path is automatically created if any directories are missing. You'll see something like this if you enable `[log_debug](/main/core/summaries/settings.html#log_debug)`. Example for mbox: ``` Debug: Namespace : /home/user/Mail doesn't exist yet, using default permissions Debug: Namespace : Using permissions from /home/user/Mail: mode=0700 gid=default ``` and a `Mail/.imap` directory will be present once that process has concluded. This is the easiest way to ensure a freshly created user is correctly set up for access via Dovecot. ## Home-less Users Having a home directory for users is highly recommended. At a minimum, [Sieve](/main/core/config/sieve/overview.html) requires a home directory to work. See [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) for more reasons why it's a good idea, and how to give Dovecot a home directory even if you don't have a "real home directory". If you really don't want to set any home directory, you can use something like: doveconf ``` mail_driver = maildir mail_path = /home/%{user}/Maildir ``` ## Per-User Mail Locations It's possible to override the default mail location for specific users by making the [userdb](/main/core/config/auth/userdb.html) return the settings as extra field. TIP Note that `%{home}` doesn't work in the userdb queries or templates. `~/` gets expanded later, so use it instead. If you have explicit settings inside [namespace { .. }](/main/core/config/namespaces.html) they need to be overridden in userdb with `namespace//` prefix. For example `namespace/inbox/mail_path` instead of simply `mail_path`. ### SQL doveconf ``` userdb sql { query = SELECT home, uid, gid, mail_path FROM users WHERE user = '%{user}' } ``` ### LDAP dovecot.conf doveconf ``` userdb ldap { ... fields { home = %{ldap:homeDirectory} uid = %{ldap:uidNumber} gid = %{ldap:gidNumber} mail_path = %{ldap:mailLocation} } } ``` ### Passwd-file ``` user:{PLAIN}password:1000:1000::/home/user::userdb_mail_driver=mbox userdb_mail_path=~/mail ``` ## Mixing Multiple Mailbox Formats It's possible to use different mailbox formats same user by configuring multiple namespaces. See [Namespaces](/main/core/config/namespaces.html). Each mailbox format has to live in a different namespace. Mixing mailbox formats within the same namespace is not supported. ## Custom Namespace Location If you need to override namespace's mail location settings, first give it a name (`inbox` in this example): doveconf ``` namespace inbox { [...] } ``` Then in the executable script use: sh ``` #!/bin/sh # do the lookup here mail_driver=mbox mail_path=$HOME/mail export USERDB_KEYS="$USERDB_KEYS namespace/inbox/mail_driver namespace/inbox/mail_path" exec env "NAMESPACE/INBOX/MAIL_DRIVER=$mail_driver" "NAMESPACE/INBOX/MAIL_PATH=$mail_path" "$@" ``` ## Finding Your Mail Before configuring Dovecot, you'll need to know where your mails are located. You should already have an SMTP server installed and configured to deliver mails somewhere, so the easiest way to make Dovecot work is to just use the same location. Otherwise you could create a `~/Maildir` directory and configure your SMTP server to use the Maildir format. First send a test mail to yourself (as your own non-root user): sh ``` echo "Hello me" | mail -s "Dovecot test" $USER ``` Now, find where the mail went. Here's a simple script which checks the most common locations: sh ``` for mbox in /var/mail/$USER /var/spool/mail/$USER ~/mbox ~/mail/* ~/*; do grep -q "Dovecot test" "$mbox" && echo "mbox: $mbox" done grep -q "Dovecot test" ~/Maildir/new/* 2>/dev/null && echo "Maildir: ~/Maildir" ``` ### mbox In most installations your mail went to `/var/mail/username` file. This file is called **INBOX** in IMAP world. Since IMAP supports multiple mailboxes, you'll also have to have a directory for them as well. Usually `~/mail` is a good choice for this. For installation such as this, the mail location settings are specified with, where `%{user}` is replaced with the username that logs in: - `[mail_driver = mbox](/main/core/summaries/settings.html#mail_driver)`, - `[mail_path = ~/mail](/main/core/summaries/settings.html#mail_path)`, and - `[mail_inbox_path = /var/mail/%{user}](/main/core/summaries/settings.html#mail_inbox_path)`. Similarly if your INBOX is in `~/mbox`, use: - `[mail_inbox_path = ~/mbox](/main/core/summaries/settings.html#mail_inbox_path)`. ### Maildir Maildir exists almost always in `~/Maildir` directory. The mail location is specified with: - `[mail_driver = maildir](/main/core/summaries/settings.html#mail_driver)`, and - `[mail_path = ~/Maildir](/main/core/summaries/settings.html#mail_path)`. ### Troubleshooting If you can't find the mail, you should check your SMTP server logs and configuration to see where it went or what went wrong. # dbox Mailbox Format dbox is Dovecot's own high-performance mailbox format. The original version was introduced in v1.0 alpha4, but since then it has been completely redesigned in v1.1 series and improved even further in v2.0. One of the main reasons for dbox's high performance is that it uses Dovecot's index files as the only storage for message flags and keywords, so the indexes don't have to be "synchronized". Dovecot trusts that they're always up-to-date (unless it sees that something is clearly broken). DANGER **You must not lose the dbox index files, as they can't be regenerated without data loss!** dbox has a feature for transparently moving message data to an [alternate storage](#alternate-storage) area. dbox storage is extensible. Single instance attachment storage was already implemented as such extension. ## dbox Modes ### single-dbox (`sdbox`) One message per file, similar to [Maildir](/main/core/config/mailbox_formats/maildir.html). For backwards compatibility, `dbox` is an alias to `sdbox` in `[mail_driver](/main/core/summaries/settings.html#mail_driver)`. ### multi-dbox (`mdbox`) Multiple messages per file,but unlike [mbox](/main/core/config/mailbox_formats/mbox.html) stores multiple files per mailbox. ## Layout By default, the dbox filesystem layout is as follows. Data which isn't the actual message content is stored in a layout common to both `sdbox` and `mdbox`. In these tables `` is shorthand for the mail location root directory on the filesystem. Index files can be stored in a different location by using the `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` setting. If specified, it will override the mail location root for index files and mdbox's "map index" file. | Location | Description | | --- | --- | | `/mailboxes/INBOX/dbox-Mails/dovecot.index*` | Index files for INBOX | | `/mailboxes/foo/dbox-Mails/dovecot.index*` | Index files for mailbox "foo" | | `/mailboxes/foo/bar/dbox-Mails/dovecot.index*` | Index files for mailbox "foo/bar" | | `/dovecot.mailbox.log*` | Mailbox changelog | | `/subscriptions` | Subscribed mailboxes list | | `/dovecot-uidvalidity*` | IMAP UID validity | Note that with dbox the Index files contain significant data which is held nowhere else. Index files for both `sdbox` and `mdbox` contain message flags and keywords. For `mdbox`, the index file also contains the map\_uids which link (via the "map index") to the actual message data. This data cannot be automatically recreated, so it is important that Index files are treated with the same care as message data files. Actual message content is stored differently depending on whether it is `sdbox` or `mdbox`. ### Message Storage: sdbox | Location | Description | | --- | --- | | `/mailboxes/INBOX/dbox-Mails/u.*` | Numbered files (`u.1`, `u.2`, ...) each containing one message of INBOX | | `/mailboxes/foo/dbox-Mails/u.*` | Files each containing one message for mailbox "foo" | | `/mailboxes/foo/bar/dbox-Mails/u.*` | Files each containing one message for mailbox "foo/bar" | ### Message Storage: mdbox | Location | Description | | --- | --- | | `/storage/dovecot.map.index*` | "Map index" containing a record for each message stored | | `/storage/m.*` | Numbered files (`u.1`, `u.2`, ...) each containing one or multiple messages | The directory layout (under `~/mdbox/`) is: | Location | Description | | --- | --- | | `~/mdbox/storage/` | The mail data for all mailboxes | | `~/mdbox/mailboxes/` | Directories for mailboxes and their index files | The `storage` directory has files: | File | Description | | --- | --- | | `dovecot.map.index*` | The "map index" | | `m.*` | Mail data. Each m.\* file contains one or more messages. `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)` can be used to configure how large the files can grow. | The "map index" contains a record for each message: | Key | Description | | --- | --- | | `map_uid` | Unique growing 32 bit number for the message. | | `refcount` | 16 bit reference counter for this message. Each time the message is copied the refcount is increased. | | `file_id` | File number containing the message. For example if file\_id=5, the message is in file `m.5`. | | `offset` | Offset to message within the file. | | `size` | Space used by the message in the file, including all metadata. | Mailbox indexes refer to messages only using map\_uids. This allows messages to be moved to different files by updating only the map index. Copying is done simply by appending a new record to mailbox index containing the existing map\_uid and increasing its refcount. If refcount grows over 32768, currently Dovecot gives an error message. It's unlikely anyone really wants to copy the same message that many times. Expunging a message only decreases the message's refcount. The space is later freed in "purge" step. This is typically done in a nightly cronjob when there's less disk I/O activity. The purging first finds all files that have refcount=0 mails. Then it goes through each file and copies the refcount>0 mails to other mdbox files (to the same files as where newly saved messages would also go), updates the map index and finally deletes the original file. So there is never any overwriting or file truncation. The purging can be invoked explicitly running `[doveadm purge](/main/core/summaries/doveadm.html#purge)`. There are several safety features built into dbox to avoid losing messages or their state if map index or mailbox index gets corrupted: - Each message has a 128 bit globally unique identifier (GUID). The GUID is saved to message metadata in `m.*` files and also to mailbox indexes. This allows Dovecot to find messages even if map index gets corrupted. - Whenever index file is rewritten, the old index is renamed to `dovecot.index.backup`. If the main index becomes corrupted, this backup index is used to restore flags and figure out what messages belong to the mailbox. - Initial mailbox where message was saved to is stored in the message metadata in `m.*` files. So if all indexes get lost, the messages are put to their initial mailboxes. This is better than placing everything into a single mailbox. ## Alternate Storage Unlike Maildir, with dbox the message file names don't change. This makes it possible to support storing files in multiple directories or mount points. dbox supports looking up files from "altpath" if they're not found from the primary path. This means that it's possible to move older mails that are rarely accessed to cheaper (slower) storage. When messages are moved from primary storage to alternate storage, only the actual message data (stored in files `u.*` under `sdbox` and `m.*` under `mdbox`) is moved to alternate storage; everything else remains in the primary storage. Message data can be moved from primary storage to alternate storage using `[doveadm altmove](/main/core/summaries/doveadm.html#altmove)`. The granularity at which data is moved to alternate storage is individual messages. This is true even for `mdbox` when multiple messages are stored in a single `m.*` storage file. If individual messages from an `m.*` storage file need to be moved to alternate storage, the message data is written out to a different `m.*` storage file (either new or existing) in the alternate storage area and the "map index" updated accordingly. Alternate storage is completely transparent at the IMAP/POP level. Users accessing mail through IMAP or POP cannot normally tell if any given message is stored in primary storage or alternate storage. Conceivably users might be able to measure a performance difference; the point is that there is no IMAP/POP command which could be used to expose this information. It is entirely possible to have a mail folder which contains a mix of messages stored in primary storage and alternate storage. ### Configuration To enable this functionality, use the `[mail_alt_path](/main/core/summaries/settings.html#mail_alt_path)` setting. For example: doveconf ``` mail_driver = mdbox mail_path = /var/vmail/%{user | domain}/%{user | username} mail_alt_path = /altstorage/vmail/%{user | domain}/%{user | username} ``` will make Dovecot look for message data first under `/var/vmail/%{user | domain}/%{user | username}` ("primary storage"), and if it is not found there it will look under `/altstorage/vmail/%{user | domain}/%{user | username}` ("alternate storage") instead. There's no problem having the same (identical) file in both storages. Keep the unmounted `/altstorage` directory permissions such that Dovecot mail processes can't create directories under it (e.g. `root:root 0755`). This way if the alt storage isn't mounted for some reason, Dovecot won't think that all the messages in alt storage were deleted and lose their flags. ## dbox and Mail Header Metadata Unlike when using [mbox](/main/core/config/mailbox_formats/mbox.html), where mail headers (for example `Status`, `X-UID`, etc.) are used to determine and store metadata, the mail headers within dbox files are (usually) **not** used for this purpose by Dovecot; neither when mails are created/moved/etc. via IMAP nor when dboxes are placed (e.g. copied or moved in the filesystem) in a mail location (and then "imported" by Dovecot). Therefore, it is (usually) **not** necessary, to strip any such mail headers at the [MTA](/main/core/config/delivery/mta.html), [MDA](/main/core/config/delivery/mda.html), or [LDA](/main/core/config/delivery/lda.html) (as it is recommended with [mbox](/main/core/config/mailbox_formats/mbox.html)). There is one exception, though, namely when `[pop3_reuse_xuidl = yes](/main/core/summaries/settings.html#pop3_reuse_xuidl)`: in this case `X-UIDL` is used for the POP3 UIDLs. Therefore, in this case, is recommended to strip the `X-UIDL` mail headers _case-insensitively_ at the mail delivery layer. ## Accessing Expunged Mails with mdbox `mdbox_deleted` storage can be used to access mdbox's all mails that are completely deleted (reference count = 0). The `mdbox_deleted` parameters should otherwise be exactly the same as `mdbox`'s. Then you can use e.g. `[doveadm fetch](/main/core/summaries/doveadm.html#fetch)` or `[doveadm import](/main/core/summaries/doveadm.html#import)` commands to access the mails. For example, if you have: - `[mail_driver = mdbox](/main/core/summaries/settings.html#mail_driver)`, - `[mail_path = ~/mdbox](/main/core/summaries/settings.html#mail_path)`, - `[mail_index_path = /var/index/%{user}](/main/core/summaries/settings.html#mail_index_path)`, use: `[doveadm import -p mail_index_path=/var/index/%{user} mdbox_deleted:~/mdbox "" subject oops](/main/core/summaries/doveadm.html#import)`. This finds a deleted mail with subject "oops" and imports it into INBOX. ## Mail Delivery Some MTA configurations have the MTA directly dropping mail into Maildirs or mboxes. Since most MTAs don't understand the dbox format, this option is not available. Instead, the MTA should use [LMTP Server](/main/core/config/delivery/lmtp.html) or [LDA](/main/core/config/delivery/lda.html). ## dbox Configuration ### Settings #### `mdbox_preallocate_space` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)` - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: If enabled, preallocate space for newly created files. In creation of new mdbox files, their size is immediately preallocated as `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)`. This setting currently works only in Linux with certain filesystems (ext4 and xfs). #### `mdbox_rotate_interval` **Default:** _\[None\]_ **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: The maximum age the dbox file may reach before it's rotated. `0` means there is no age-based rotation. #### `mdbox_rotate_size` **Default:** `10M` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: The maximum size the dbox file may reach before it is rotated. ### Mail Location #### sdbox To use **single-dbox**, use the tag `sdbox` in `[mail_driver](/main/core/summaries/settings.html#mail_driver)`: doveconf ``` # single-dbox mail_driver = sdbox mail_path = ~/dbox ``` For backwards compatibility, `dbox` is an alias to `sdbox` in the mail location. (This usage is deprecated.) #### mdbox To use **multi-dbox**, use the tag `mdbox` in `[mail_driver](/main/core/summaries/settings.html#mail_driver)`: doveconf ``` # multi-dbox mail_driver = mdbox mail_path = ~/mdbox ``` #### Default mail settings - `[mail_path = %{home}/sdbox](/main/core/summaries/settings.html#mail_path)` for sdbox, `[mail_path = %{home}/mdbox](/main/core/summaries/settings.html#mail_path)` for mdbox, - `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)`, - `[mailbox_directory_name = dbox-Mails](/main/core/summaries/settings.html#mailbox_directory_name)`, and - `[mailbox_root_directory_name = mailboxes](/main/core/summaries/settings.html#mailbox_root_directory_name)`. ### Migrating away from `mailbox_directory_name_legacy` Deprecated: 2.4.2`[mailbox_directory_name_legacy](/main/core/summaries/settings.html#mailbox_directory_name_legacy)` setting is deprecated and its default value is changed from `true` to `false`. This change requires migrations, though: - Start with `[mailbox_directory_name_legacy = yes](/main/core/summaries/settings.html#mailbox_directory_name_legacy)`, - for each user run: `doveadm -o mail_home=/var/newmails/$user -o mailbox_directory_name_legacy=no sync -u $user`, - shutdown all mail access, - for each user run `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html)` again, - switch the `[mail directory](/main/core/summaries/settings.html#mail_path)`, - set `[mailbox_directory_name_legacy = no](/main/core/summaries/settings.html#mailbox_directory_name_legacy)`, - start dovecot. # Imapc Mailbox Format The imapc storage accesses a remote IMAP server as if it were a regular (local) Dovecot mailbox format. Dovecot can treat it as a dummy storage or optionally a more capable storage. ## Settings ### `imapc_cmd_timeout` **Default:** `5 mins` **Value:** [time](/main/core/settings/types.html#time) How long to wait for a reply to an IMAP command sent to the remote IMAP server before disconnecting and retrying. ### `imapc_connection_retry_count` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) How many times to retry connection against a remote IMAP server? ### `imapc_connection_retry_interval` **Default:** `1 secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait between retries against a remote IMAP server? ### `imapc_connection_timeout_interval` **Default:** `30secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait before considering a connection attempt as timed out. ### `imapc_features` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Changed: 2.4.0 Several features are now automatically enabled and the respective flags dropped. In their place new flags to disable these features were added. List of features, optimizations, and workarounds that can be enabled. Changed: 2.4.3 Any remote IMAP server extension can now be disabled by using `no-` prefixed with the capability name. For example `no-sort`, `no-sort=display` or `no-quota`. **Features** `no-acl` If the [imap-acl plugin](/main/core/plugins/imap_acl.html) is loaded, the imapc acl feature is automatically enabled. With it IMAP ACL commands (MYRIGHTS, GETACL, SETACL, DELETEACL) are proxied to the imapc remote location. Note that currently these commands are attempted to be used even if the remote IMAP server doesn't advertise the ACL capability. To disable this feature either unload the [imap-acl plugin](/main/core/plugins/imap_acl.html) or provide this feature. Changed: 2.4.0 Earlier versions had an "acl" feature, which is now enabled by default. `no-delay-login` Immediately connect to the remote server. By default this is delayed until a command requires a connection. Changed: 2.4.0 Earlier versions had a "delay-login" feature, which is now enabled by default. `gmail-migration` Enable GMail-specific migration. Use IMAP `X-GM-MSGID` as POP3 UIDL. Add `$GMailHaveLabels` keyword to mails that have `X-GM-LABELS` except for `Muted` keyword (to be used for migrating only archived emails in `All Mails`). Add `[pop3_deleted_flag](/main/core/summaries/settings.html#pop3_deleted_flag)` to mails that don't exist in POP3 server. `no-modseq` Disable access to `MODSEQ` and `HIGHESTMODSEQ` fields. By default these fields are available if the remote server advertises the CONDSTORE or the QRESYNC capability. If modseqs are disabled, or not supported by the new server, they can still be used if imapc is configured to have local index files. Changed: 2.4.0 Earlier versions had a "modseq" feature, which is now enabled by default. `proxyauth` Use Sun/Oracle IMAP-server specific `PROXYAUTH` command to do master user authentication. Normally this would be done using the SASL PLAIN authentication. `send-id` Send session ID with the IMAP ID `x-session-ext-id` parameter. If `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` on the target host is configured to trust the connecting imapc IP, the session ID is preserved as the new imapc connection's session ID prefix. `throttle:::` When receiving \[THROTTLED\] response (from GMail), throttling is applied. **INIT** = initial throttling msecs (default: 50 ms), afterwards each subsequent \[THROTTLED\] doubles the throttling until **MAX** is reached (default: 16000 ms). When \[THROTTLED\] is not received for a while, it's shrunk again. The initial shrinking is done after **SHRINK** (default: 500 ms). If \[THROTTLED\] is received again within this timeout, it's doubled, otherwise both throttling and the next shrinking timeout is shrank to 3/4 the previous value. **Optimizations** `no-fetch-bodystructure` Disable fetching of IMAP `BODY` and `BODYSTRUCTURE` from the remote server. Instead, the whole message body is fetched to regenerate them. Changed: 2.4.0 Earlier versions had a "fetch-bodystructure" feature, which is now enabled by default. `no-fetch-headers` Disable fetching of specific message headers from the remote server using the IMAP `FETCH BODY.PEEK[HEADER.FIELDS(...)]` command. Instead, the whole header is fetched and the wanted headers are parsed from it. Changed: 2.4.0 Earlier versions had a "fetch-headers" feature, which is now enabled by default. `no-fetch-size` Disable fetching of message sizes from the remote server using the IMAP `FETCH RFC822.SIZE` command. Instead, the whole message body is fetched to calculate the size. Changed: 2.4.0 Earlier versions had a "rfc822.size" feature, which is now enabled by default. `no-metadata` Disable the detection of the `METADATA` capability from the remote server. The client will receive a `NO [UNAVAILABLE]` response for any request that requires access to metadata on the remote server (the same happens if the server does not announce the capability at all). `no-search` Disable searching messages using the IMAP `SEARCH` command. Instead, all the message headers/bodies are fetched to perform the search locally. Changed: 2.4.0 Earlier versions had a "search" feature, which is now enabled by default. **Workarounds** `fetch-fix-broken-mails` If a `FETCH` returns `NO` (but not `NO [LIMIT]` or `NO [SERVERBUG]`), assume the mail is broken in server and just treat it as if it were an empty email. DANGER This is often a dangerous option! It's not safe to assume that `NO` means a permanent error rather than a temporary error. This feature should be enabled only for specific users who have been determined to be broken. `fetch-msn-workarounds` Try to ignore wrong message sequence numbers in `FETCH` replies whenever possible, preferring to use the returned UID number instead. `no-examine` Use `SELECT` instead of `EXAMINE` even when we don't want to modify anything in the mailbox. This is a Courier-workaround where it didn't permanently assign `UIDVALIDITY` to an `EXAMINE`d mailbox, but assigned it for `SELECT`ed mailbox. `no-qresync` Added: 2.4.0 This can be used to work around a Zimbra bug where it doesn't send untagged "OK \[CLOSED\]" imap-resp-code when selecting a folder. `zimbra-workarounds` Fetch full message using `BODY.PEEK[HEADER] BODY.PEEK[TEXT]` instead of just `BODY.PEEK[]` because the header differs between these two when there are illegal control chars or 8bit chars. This mainly caused problems with dsync, but this should no longer be a problem and there's probably no need to enable this workaround. ### `imapc_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The remote IMAP host to connect to. ### `imapc_list_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Access only mailboxes under this prefix. Example, for a source IMAP server that uses an INBOX namespace prefix: ``` imapc_list_prefix = INBOX ``` ### `imapc_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_password](/main/core/summaries/settings.html#imapc_password)` - `[imapc_user](/main/core/summaries/settings.html#imapc_user)` The master username to authenticate as on the remote IMAP host. To authenticate as a master user but use a separate login user, the following configuration should be employed, where the credentials are represented by masteruser and masteruser-secret: ``` imapc_user = %{user} imapc_master_user = masteruser imapc_password = masteruser-secret ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `imapc_max_idle_time` **Default:** `29 mins` **Value:** [time](/main/core/settings/types.html#time) Send a command to the source IMAP server as a keepalive after no other command has been sent for this amount of time. Dovecot will send either `NOOP` or `DONE` to the source IMAP server. ### `imapc_max_line_length` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) The maximum line length to accept from the remote IMAP server. This setting is used to limit maximum memory usage. ### `imapc_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_master_user](/main/core/summaries/settings.html#imapc_master_user)` - `[imapc_user](/main/core/summaries/settings.html#imapc_user)` - `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` The authentication password for the remote IMAP server. If using master users, this setting will be the password of the master user. ### `imapc_port` **Default:** `143` **Value:** [Port Number](/main/core/settings/types.html#port-number) The port on the remote IMAP host to connect to. ### `imapc_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Log all IMAP traffic input/output to this directory. ### `imapc_sasl_mechanisms` **Default:** `plain` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The [SASL](/main/core/admin/sasl.html) mechanisms to use for authentication when connection to a remote IMAP server. The first one advertised by the remote IMAP sever is used. ``` imapc_sasl_mechanisms { external = yes plain = yes login = yes } ``` Supported mechanisms are: - ANONYMOUS - EXTERNAL - LOGIN - OAUTHBEARER - PLAIN - SCRAM-SHA-1 - SCRAM-SHA-1-PLUS - SCRAM-SHA-256 - SCRAM-SHA-256-PLUS - XOAUTH2 Note that `[imapc_password](/main/core/summaries/settings.html#imapc_password)` is ignored for `ANONYMOUS` and `EXTERNAL` mechanisms. For `OAUTHBEARER` and `XOAUTH2` `[imapc_password](/main/core/summaries/settings.html#imapc_password)` should be bearer token. ### `imapc_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``imaps``starttls` Use TLS to connect to the remote IMAP server. | Value | Description | | --- | --- | | `no` | No TLS | | `imaps` | Explicitly connect to remote IMAP port using TLS | | `starttls` | Use IMAP STARTTLS command to switch to TLS connection | ### `imapc_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[imapc_ssl](/main/core/summaries/settings.html#imapc_ssl)` **Changes:** - Removed: 2.4.0 Dropped in favor of using the global `[ssl_client_require_valid_cert](/main/core/summaries/settings.html#ssl_client_require_valid_cert)`. Verify remote IMAP TLS certificate? Verification may be disabled during testing, but should be enabled during production use. Only used if `[imapc_ssl](/main/core/summaries/settings.html#imapc_ssl)` is enabled. ### `imapc_user` **Default:** `%{owner_user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_master_user](/main/core/summaries/settings.html#imapc_master_user)` - `[imapc_password](/main/core/summaries/settings.html#imapc_password)` - `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` The user identity to be used for performing a regular IMAP LOGIN to the source IMAP server. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ## Configuration Example Do a regular IMAP LOGIN, using STARTTLS, to imap.example.com: doveconf ``` # In-memory index files: mail_driver = imapc mail_path = # OR, Store index files locally: #mail_path = ~/imapc imapc_host = imap.example.com imapc_password = secret imapc_port = 143 imapc_ssl = starttls imapc_user = user@example.com ``` ## Quota Using the `imapc` quota driver allows asking for the quota from remote IMAP server. By default it uses `GETQUOTAROOT INBOX` to retrieve the quota. There are two parameters that can be used to control how the quota is looked up: ### `quota_imapc_mailbox_name` **Default:** `INBOX` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_root_name](/main/core/summaries/settings.html#quota_imapc_root_name)` If non-empty, use `GETQUOTAROOT ` to get the imapc quota root. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### `quota_imapc_root_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` If `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` is empty, use `GETQUOTA ` to get the imapc quota. Some servers may have an empty quota root name. This is why if this setting and `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` both have empty values, this setting is used. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### Example doveconf ``` quota "User Quota" { driver = imapc imapc_root_name = Remote Quota } ``` # Maildir Mailbox Format The Maildir format debuted with the qmail server in the mid-1990s. Each mailbox folder is a directory and each message a file. This improves efficiency because individual emails can be modified, deleted and added without affecting the mailbox or other emails, and makes it safer to use on networked file systems such as NFS. WARNING The Maildir mailbox format is mainly viable for smaller installations. It will be maintained on a best-effort basis for [Dovecot Community Edition](https://repo.dovecot.org/), without any prioritization of new features or optimizations. ## Dovecot Extensions Since the [Maildir standard](https://cr.yp.to/proto/maildir.html) doesn't provide everything needed to fully support the IMAP protocol, Dovecot had to create some of its own non-standard extensions. The extensions still keep the Maildir standards compliant, so MUAs not supporting the extensions can still safely use it as a normal Maildir. ### IMAP UID mapping IMAP requires each message to have a permanent unique ID number. Dovecot uses the `dovecot-uidlist` file to keep UID <-> filename mapping. The file is basically in the same format as Courier IMAP's `courierimapuiddb` file, except for one difference (see below). The file begins with a header: ``` 3 V1275660208 N25022 G3085f01b7f11094c501100008c4a11c1 ``` - 3 is the file format version number used by Dovecot v1.1+ - 1275660208 is the IMAP UIDVALIDITY - 25022 is the UID that will be given to the next added message - 3085f01b7f11094c501100008c4a11c1 is the 128 bit mailbox global UID in hex - There may be other fields, and the order of these fields isn't important Version 1 file format is compatible with Courier. Version 2 was used by a few Dovecot non-release versions. After the header comes the list of UID <-> filename mappings: ``` 25006 :1276528487.M364837P9451.kurkku,S=1355,W=1394:2, 25017 W2481 :1276533073.M242911P3632.kurkku:2,F ``` - 25006, 25017 are message UIDs - 2481 is the second message's virtual size. First message contains it in the filename itself, so it's not duplicated. - There may be more fields before ':' character - Rest of the line after ':' is the last known filename. This filename doesn't necessarily exist currently, because the filename changes every time a message's flags change. Dovecot doesn't waste disk I/O by rewriting uidlist file every time flags change, but whenever it is rewritten the latest filenames are used. This allows Dovecot to try to guess what the message's current filename is and if successful, avoid having to scan the directory's contents. The `dovecot-uidlist` file doesn't need to be locked for reading. When writing, `dovecot-uidlist.lock` file needs to be created. New lines can be appended to the end of file, but existing data must never be directly modified; it can only be replaced with `rename()` system call. `dovecot-uidlist` is updated lazily to optimize for disk I/O. If a message is expunged, it may not be removed from `dovecot-uidlist` until sometimes later. This means that if you create a new file using the same file name as what already exists in `dovecot-uidlist`, Dovecot thinks you "unexpunged" message by restoring a message from backup. This causes a warning to be logged and the file to be renamed. Note that messages must not be modified once they've been delivered. IMAP (and Dovecot) requires that messages are immutable. If you wish to modify them in any way, create a new message instead and expunge the old one. ### IMAP Keywords All the non-standard message flags are called keywords in IMAP. Some clients use these automatically for marking spam (eg. `$Junk`, `$!NonJunk`, `$Spam`, `$!NonSpam` keywords). Thunderbird uses labels which map to keywords `$Label1`, `$Label2`, etc. Dovecot stores keywords in the Maildir filename's flags field using letters `a..z`. This means that only 26 keywords are possible to store in the Maildir. If more are used, they're still stored in Dovecot's index files. The mapping from single letters to keyword names is stored in `dovecot-keywords` file. The file is in format: ``` 0 $Junk 1 $NonJunk ``` 0 means letter `a` in the Maildir filename, 1 means `b`, and so on. The file doesn't need to be locked for reading, but when writing `dovecot-uidlist` file must be locked. The file must not be directly modified; it can only be replaced with `rename()` system call. For example, a file named ``` 1234567890.M20046P2137.mailserver,S=4542,W=4642:2,Sb ``` would be flagged as `$NonJunk` with the above keywords. ### Maildir Filename Extensions The standard filename definition is: `:2,`. Dovecot has extended the `` field to be `[,]`. This means that if Dovecot sees a comma in the `` field while updating flags in the filename, it doesn't touch anything after the comma. However other Maildir MUAs may mess them up, so it's still not such a good idea to do that. Basic `` are described in the Maildir standard. The `` isn't used by Dovecot for anything currently. Dovecot supports reading a few fields from the ``: - `,S=`: `` contains the file size. Getting the size from the filename avoids doing a system `stat()` call, which may improve the performance. This is especially useful with [Quota Driver: Maildir](/main/core/plugins/quota.html#driver-maildir). - `,W=`: `` contains the file's RFC822.SIZE, i.e., the file size with linefeeds being CR+LF characters. If the message was stored with CR+LF linefeeds, `` and `` are the same. Setting this may give a small speedup because now Dovecot doesn't need to calculate the size itself. A Maildir filename with those fields would look something like: ``` 1035478339.27041_118.foo.org,S=1000,W=1030:2,S ``` ### Usage of Timestamps Timestamps of message files: - `mtime` is used as IMAP INTERNALDATE [RFC 3501 (section 2.3.3)](https://datatracker.ietf.org/doc/html/rfc3501#section-2.3.3), and must never change (see [RFC 3501 (section 2.3.1.1)](https://datatracker.ietf.org/doc/html/rfc3501#section-2.3.1.1)). - `ctime` is used as Dovecot's internal "save/copy date", unless the correct value is found from `dovecot.index.cache`. This is used only by external commands, e.g. `[doveadm expunge savedbefore](/main/core/summaries/doveadm.html#expunge)`. - `atime` is not used. Timestamps of `cur` and `new` directories: - `mtime` is used to detect changes of the mailbox and may force regeneration of index files - `atime` and `ctime` not used. ### Filename Examples For a filename `1491941793.M41850P8566V0000000000000015I0000000004F3030E_0.mx1.example.com,S=10956:2,STln`: `1491941793` UNIX timestamp of arrival. `S=10956` Size of the e-mail. `STln` - **S** = seen (marked as read) - **T** = trashed - **l** = IMAP tag #12 (0=a, 1=b, 2=c, etc.) as defined in that folder's `dovecot-keywords` file. - **n** = IMAP tag #14 (0=a, 1=b, 2=c, etc.) as defined in that folder's `dovecot-keywords` file. ## Issues with the Maildir Specification ### Locking Although Maildir was designed to be lockless, Dovecot locks the Maildir while doing modifications to it or while looking for new messages in it. This is required because otherwise Dovecot might temporarily see mails incorrectly deleted, which would cause trouble. Basically the problem is that if one process modifies the Maildir (eg. a `rename()` to change a message's flag), another process in the middle of listing files at the same time could skip a file. The skipping happens because `readdir()` system call doesn't guarantee that all the files are returned if the directory is modified between the calls to it. This problem exists with all the commonly used filesystems. Because Dovecot uses its own non-standard locking (`dovecot-uidlist.lock` dotlock file), other MUAs accessing the Maildir don't support it. This means that if another MUA is updating message flags or expunging messages, Dovecot might temporarily lose some message(s). After the next sync when it finds it again, an error message may be written to log and the message will receive a new UID. Delivering mails to `new/` directory doesn't have any problems, so there's no need for LDAs to support any type of locking. ### Mail Delivery [Qmail's how a message is delivered page](http://qmail.org/man/man5/maildir.html) suggests to deliver the mail like this: 1. Create a unique filename (only `time.pid.host` here, later Maildir spec has been updated to allow more uniqueness identifiers) 2. Do `stat(tmp/)`. If the `stat()` found a file, wait 2 seconds and go back to step 1. 3. Create and write the message to `tmp/`. 4. `link()` it into `new/` directory. Although not mentioned here, the `link()` could again fail if the mail existed in `new/` dir. In that case you should probably go back to step 1. All this trouble is rather pointless. Only the first step is what really guarantees that the mails won't get overwritten, the rest just sounds nice. Even though they might catch a problem once in a while, they give no guaranteed protection and will just as easily pass duplicate filenames through and overwrite existing mails. Step 2 is pointless because there's a race condition between steps 2 and 3. PID/host combination by itself should already guarantee that it never finds such a file. If it does, something's broken and the `stat()` check won't help since another process might be doing the same thing at the same time, and you end up writing to the same file in `tmp/`, causing the mail to get corrupted. In step 4 the `link()` would fail if an identical file already existed in the Maildir, right? Wrong. The file may already have been moved to `cur/` directory, and since it may contain any number of flags by then you can't check with a simple `stat()` anymore if it exists or not. Step 2 was pointed out to be useful if clock had moved backwards. However, this doesn't give any actual safety guarantees because an identical base filename could already exist in `cur/`. Besides if the system was just rebooted, the file in `tmp/` could probably be even overwritten safely (assuming it wasn't already `link()`\\ ed to `new/`). So really, all that's important in not getting mails overwritten in your Maildir is step 1: Always create filenames that are guaranteed to be unique. Forget about the 2 second waits and such that the Qmail's man page talks about. ## Maildir and Mail Header Metadata Unlike when using [mbox](/main/core/config/mailbox_formats/mbox.html), where mail headers (for example `Status`, `X-UID`, etc.) are used to determine and store metadata, the mail headers within Maildir files are (usually) **not** used for this purpose by Dovecot; neither when mails are created/moved/etc. via IMAP nor when Maildirs are placed (e.g., copied or moved in the filesystem) in a mail location (and then "imported" by dovecot). Therefore, it is (usually) **not** necessary, to strip any such mail headers at the [MTA](/main/core/config/delivery/mta.html), [MDA](/main/core/config/delivery/mda.html), or [LDA](/main/core/config/delivery/lda.html) (as is recommended with [mbox](/main/core/config/mailbox_formats/mbox.html)). There is one exception, though, namely when `[pop3_reuse_xuidl = yes](/main/core/summaries/settings.html#pop3_reuse_xuidl)` is used: in this case `X-UIDL` is used for the POP3 UIDLs. Therefore, in this case, is recommended to strip the `X-UIDL` mail headers _case-insensitively_ at the mail delivery layer. ## Procmail Problems Maildir format is somewhat compatible with MH format. This is sometimes a problem when people configure their procmail to deliver mails to `Maildir/new`. This makes procmail create the messages in MH format, which basically means that the file is called `msg.inode_number`. While this appears to work first, after expunging messages from the Maildir the inodes are freed and will be reused later. This means that another file with the same name may come to the Maildir, which makes Dovecot think that an expunged file reappeared into the mailbox and an error is logged. The proper way to configure procmail to deliver to a Maildir is to use `Maildir/` as the destination. ## Settings ### `maildir_broken_filename_sizes` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, do not obtain a mail message's physical size from the `S=` data in the Maildir filename except when recalculating the Maildir++ quota. ### `maildir_copy_with_hardlinks` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, copying of a message is done with hard links whenever possible. This makes the performance much better, and it's unlikely to have any side effects. The only reason to disable this is if you're using a filesystem where hard links are slow (e.g. HFS+). ### `maildir_empty_new` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) Should mail messages always be moved from the `new/` directory to `cur/`, even when the `Recent` flags aren't being reset? ### `maildir_stat_dirs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, don't include non-directory files in a LIST response that begin with a dot. Thus, if disabled, Dovecot assumes that all the files beginning with a dot in the Maildir are Maildirs. You shouldn't have any non-directory files beginning with a dot in the Maildirs, but if you do you may need to set this to `yes`, in which case Dovecot needs to `stat()` each directory entry, which degrades the performance. Some filesystems (e.g. ext4) provide the directory/non-directory status for free without having to `stat()`. In those filesystems this setting is ignored. ### `maildir_very_dirty_syncs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled (`yes`), Dovecot is assumed to be the only MUA that accesses Maildir directly, so the `cur/` directory is scanned only when its mtime changes unexpectedly or when the mail cannot otherwise be found. If enabled and another process (or a Dovecot process which doesn't update index files) does changes to `cur/` while the mailbox is simultaneously being modified by Dovecot, Dovecot may not notice those external changes. It is still safe to deliver new mails to `new/` using non-Dovecot software (except with `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)`, changes aren't noticed outside INBOX). ### `pop3_save_uidl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) [Maildir](/main/core/config/mailbox_formats/maildir.html) only: If enabled, allow permanent saving of UIDLs sent to POP3 clients so that changes to `[pop3_uidl_format](/main/core/summaries/settings.html#pop3_uidl_format)` don't cause future changes to the corresponding UIDLs. ## Configuration ### Mail Location Maildir exists almost always in `~/Maildir` directory. The mail location is specified with: doveconf ``` mail_driver = maildir mail_path = ~/Maildir ``` #### Directory Layout By default, Dovecot uses Maildir++ directory layout. This means that all mailboxes are stored in a single directory and prefixed with a dot. For example: - `Maildir/.folder/` - `Maildir/.folder.subfolder/` If you want Maildirs to use hierarchical directories, such as: - `Maildir/folder/` - `Maildir/folder/subfolder/` you'll need to enable fs layout: doveconf ``` mailbox_list_layout = fs ``` #### Default mail settings - `[mail_path = %{home}/Maildir](/main/core/summaries/settings.html#mail_path)`, - `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)`, - `[mail_inbox_path = .](/main/core/summaries/settings.html#mail_inbox_path)` with `fs` and `maildir++` layouts. This is used to store INBOX into the `~/Maildir/` directory root instead of `~/Maildir/.INBOX`. ### Control Files Dovecot stores some Maildir metadata into two control files: - `dovecot-uidlist` file contains IMAP UID <-> Maildir filename mapping - `dovecot-keywords` file contains Maildir filename flag (a..z = 0..25) <-> keyword name mapping They shouldn't be treated the same way as index files. Index files can be deleted and rebuilt without any side effects, but if you delete control files you'll cause messages to get new UIDs and possibly lose keyword names. If the messages get new UIDs, the IMAP clients will invalidate their local cache and download the messages all over again. If you do this for all the users, you could cause huge disk I/O bursts to your server. Dovecot cannot currently handle not being able to write the control files, so it will cause problems with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). To avoid problems with this, you should place control files into a partition where quota isn't checked. You can specify this with the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mail_control_path = /var/no-quota/%{user} ``` ### Index Files By default, index files are stored in the actual Maildirs. See [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) for an explanation of how to change the index path. Example: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mail_index_path = /var/indexes/%{user} ``` ### Optimizations - `[maildir_copy_with_hardlinks = yes](/main/core/summaries/settings.html#maildir_copy_with_hardlinks)` - `[maildir_stat_dirs = no](/main/core/summaries/settings.html#maildir_stat_dirs)` - `[maildir_very_dirty_syncs = yes](/main/core/summaries/settings.html#maildir_very_dirty_syncs)` ### Mailbox Directory Name When using `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)`, there is a potential for naming collisions between Maildir's `new/`, `cur/`, and `tmp/` subdirectories, and mail folders of the same names. For example, consider a mail folder `foo/bar`. Under `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)`, data for this mail folder will be stored under Maildir's usual three directories `~/Maildir/foo/bar/{new,cur,tmp}/`. If the user then tries to create a mail folder `foo/bar/new`, this would then imply that data should be stored in Maildir's three directories `~/Maildir/foo/bar/new/{new,cur,tmp}/`. But this would overlap Maildir's `new/` subdirectory of mail folder `foo/bar`. This may not be a problem in many installations, but if a risk of collisions with Maildir's three subdirectory names is perceived, then the `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` setting can be used. For example, if we specify the mail location as: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mailbox_list_layout = fs mailbox_directory_name = mAildir ``` then this will push Maildir's `new/`, `cur/`, and `tmp/` subdirectories down into a subdirectory `mAildir/`, so a mail folder `foo/bar` would be stored at `~/Maildir/foo/bar/mAildir/{new,cur,tmp}/`. A mail folder `foo/bar/new` would be stored at `~/Maildir/foo/bar/new/mAildir/{new,cur,tmp}/`, which would then have no overlap with the mail folder `foo/bar`. `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` affects INBOX slightly differently. If unset, INBOX will be stored at `~/Maildir/{new,cur,tmp}`, but when `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` is specified, we get an extra path component `INBOX/` immediately prior to the `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` value. In the example above INBOX would be stored at `~/Maildir/INBOX/mAildir/{new,cur,tmp}/`. The value for `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` should be chosen carefully so as to minimise the chances of clashing with mail folder names. In the example here, unusual upper/lower casing has been used. ### Multiple Namespaces pointing to INBOX When there are multiple namespaces that point to the same INBOX namespace, `dovecot.list.index` can potentially keep fighting over whether INBOX exists or not. For example: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mailbox_list_layout = fs namespace inbox { inbox = yes prefix = INBOX/ separator = / subscriptions = no } namespace empty { prefix = separator = / alias_for = inbox subscriptions = yes } ``` The solution is to disable `dovecot.list.index` for the alias namespace. In the above example, this is done by adding: doveconf ``` namespace empty { mailbox_list_index_prefix = } ``` # Mbox Mailbox Format DANGER Mbox format is deprecated and should not be used in production setups at all. Mbox is no longer being maintained for write fixes, for any new or advanced features, nor for optimization improvements. E.g. mbox is incapable of writing very large payloads approaching 4GB in size. It still exists solely to read old mail storages, and for backwards utility purposes (specifically for archival purposes, as mbox allows multiple messages to be natively stored in a single file). In a production system, a more modern mailbox format should be used, e.g., [dbox](/main/core/config/mailbox_formats/dbox.html) (or [Maildir](/main/core/config/mailbox_formats/maildir.html)). Usually UNIX systems are configured by default to deliver mails to `/var/mail/username` or `/var/spool/mail/username` mboxes. In the IMAP world, these files are called INBOX mailboxes. IMAP protocol supports multiple mailboxes , so there needs to be a place for them as well. Typically they're stored in `~/mail/` or `~/Mail/` directories. The mbox file contains all the messages of a single mailbox. Because of this, the mbox format is typically thought of as a slow format. However with Dovecot's indexing this isn't true. Only expunging messages from the beginning of a large mbox file is slow with Dovecot, most other operations should be fast. Also because all the mails are in a single file, searching is much faster (if FTS is not used) than with Maildir. Modifications to mbox may require moving data around within the file, so interruptions (eg. power failures) can cause the mbox to break more or less badly. Although Dovecot tries to minimize the damage by moving the data in a way that data should never get lost (only duplicated), mboxes still aren't recommended to be used for important data. ## History The history of mbox format, and a discussion of its historical use and generally agreed-upon conventions, can be found in [RFC 4155](https://datatracker.ietf.org/doc/html/rfc4155). Additionally, see the [mbox Wikipedia page](https://en.wikipedia.org/wiki/Mbox). ## Locking Locking is a mess with mboxes. There are multiple different ways to lock a mbox, and software often uses incompatible locking. The only standard way to lock an mbox is using a method called "dotlock". This means that a file named `.lock` is created in the same directory as the mailbox being locked. This works pretty well when the mbox is locked for writing, but for reading it's very inefficient. That's why other locking methods have been used. It's important that all software that's reading or writing to mboxes use the same locking settings. If they use different methods, they might read/write to an mbox while another process is modifying it, and see corrupted mails. If they use the same methods but in a different order, they can both end up in a deadlock. ### Locking Methods There are at least four different ways to lock a mbox: #### dotlock `mailboxname.lock` file created by almost all software when writing to mboxes. This grants the writer an exclusive lock over the mbox, so it's usually not used while reading the mbox so that other processes can also read it at the same time. So while using a dotlock typically prevents actual mailbox corruption, it doesn't protect against read errors if mailbox is modified while a process is reading. Another problem with dotlocks is that if the mailboxes exist in `/var/mail/`, the user may not have write access to the directory, so the dotlock file can't be created. There are a couple of ways to work around this: - Give a mail group write access to the directory and then make sure that all software requiring access to the directory runs with the group's privileges. This may mean making the binary itself setgid-mail, or using a separate dotlock helper program which is setgid-mail. With Dovecot this can be done by setting `[mail_privileged_group = mail](/main/core/summaries/settings.html#mail_privileged_group)`. - Set sticky bit to the directory (`chmod +t /var/mail`). This makes it somewhat safe to use, because users can't delete each others mailboxes, but they can still create new files (the dotlock files). The downside to this is that users can create whatever files they wish in there, such as a mbox for newly created user who hadn't yet received mail. #### flock `flock()` system call is quite commonly used for both read and write locking. The read lock allows multiple processes to obtain a read lock for the mbox, so it works well for reading as well. The downside is that it doesn't work if mailboxes are stored in NFS. #### fcntl Very similar to flock, also commonly used by software. In some systems this `fcntl()` system call is compatible with `flock()`, but in other systems it's not, so you shouldn't rely on it. fcntl works with NFS if you're using lockd daemon in both NFS server and client. #### lockf POSIX `lockf()` locking. Because it allows creating only exclusive locks, it's somewhat useless so Dovecot doesn't support it. With Linux `lockf()` is internally compatible with `fcntl()` locks, but again you shouldn't rely on this. ### Deadlocks If multiple lock methods are used, which is usually the case since dotlocks aren't typically used for read locking, the order in which the locking is done is important. Consider if two programs were running at the same time, both use dotlock and fcntl locking but in different order: - Program A: fcntl locks the mbox - Program B at the same time: dotlocks the mbox - Program A continues: tries to dotlock the mbox, but since it's already dotlocked by B, it starts waiting - Program B continues: tries to fcntl lock the mbox, but since it's already fcntl locked by A, it starts waiting Now both of them are waiting for each others locks. Finally after a couple of minutes they time out and fail the operation. ### Lock Configuration For Dovecot you can configure locking using the `[mbox_read_locks](/main/core/summaries/settings.html#mbox_read_locks)` and `[mbox_write_locks](/main/core/summaries/settings.html#mbox_write_locks)` settings. The defaults are: doveconf ``` mbox_read_locks = fcntl mbox_write_locks = dotlock fcntl ``` Here's a list of how to find out the locking settings for other software: #### Debian Debian's policy specifies that all software should use "fcntl and then dotlock" locking. All software in Debian packages are supposed to be patched to use this order by default. #### Procmail sh ``` procmail -v 2>&1|grep Locking ``` ``` Locking strategies: dotlocking, fcntl() ``` #### Postfix Postfix has two different ways to deliver to mboxes. One is the "mailbox" transport and another one is the "virtual" transport. doveconf ``` # postconf mailbox_delivery_lock mailbox_delivery_lock = fcntl, dotlock # postconf virtual_mailbox_lock virtual_mailbox_lock = fcntl ``` In the above case, if you used the mailbox transport, you'd have to change Dovecot's configuration to `[mbox_write_locks = fcntl dotlock](/main/core/summaries/settings.html#mbox_write_locks)` or vice versa for Postfix. If you used the virtual transport, it doesn't really matter if the dotlock is missing, since the fcntl is common with Dovecot and Postfix. #### Exim Locks are controlled by the appendfile transport's `use_lockfile`, `use_fcntl_lock` and `use_flock_lock` settings. The default is to use lockfile (dotlock) and then fcntl lock. #### Sendmail Sendmail's mail.local delivery always uses dotlock followed by flock. #### mutt sh ``` mutt -v|grep -i lock ``` ## Directory Structure By default, when listing mailboxes, Dovecot simply assumes that all files it sees are mboxes and all directories mean that they contain sub-mailboxes. There are two special cases however which aren't listed: - `.subscriptions` file contains IMAP's mailbox subscriptions. - `.imap/` directory contains Dovecot's index files. Because it's not possible to have a file which is also a directory, it's not normally possible to create a mailbox and child mailboxes under it. However if you really want to be able to have mailboxes containing both messages and child mailboxes under mbox, then Dovecot can be configured to do this, subject to certain provisos; see [child folders](#child-folders). ## Dovecot's Metadata Dovecot uses c-Client (ie. UW-IMAP, Pine) compatible headers in mbox messages to store metadata. These headers are: | Header | Description | | --- | --- | | `X-IMAPbase` | Contains UIDVALIDITY, last used UID, and list of used keywords | | `X-IMAP` | Same as X-IMAPbase but also specifies that the message is a "pseudo-message" | | `X-UID` | Message's allocated UID | | `Status` | **R** (\\Seen) and **O** (non-\\Recent) flags | | `X-Status` | **A** (\\Answered), **F** (\\Flagged), **T** (\\Draft), and **D** (\\Deleted) flags | | `X-Keywords` | Message's keywords | | `Content-Length` | Length of the message body in bytes | Whenever any of these headers exist, Dovecot treats them as its own private metadata. It does sanity checks for them, so the headers may also be modified or removed completely. None of these headers are sent to IMAP/POP3 clients when they read the mail. WARNING **The [MTA](/main/core/config/delivery/mta.html), [MDA](/main/core/config/delivery/mda.html), or [LDA](/main/core/config/delivery/lda.html) should strip all these headers case-insensitively before writing the mail to the mbox.** Only the first message contains the X-IMAP or X-IMAPbase header. The difference is that when all the messages are deleted from mbox file, a pseudo message is written to the mbox which contains X-IMAP header. This is the "DON'T DELETE THIS MESSAGE -- FOLDER INTERNAL DATA" message which you hate seeing when using non-C-client and non-Dovecot software. This is however important to prevent abuse, otherwise the first mail which is received could contain faked X-IMAPbase header which could cause trouble. If message contains X-Keywords header, it contains a space-separated list of keywords for the mail. Since the same header can come from the mail's sender, only the keywords are listed in X-IMAP header are used. The UID for a new message is calculated from last used UID in X-IMAP header + 1. This is done always, so fake X-UID headers don't really matter. This is also why the pseudo-message is important. Otherwise the UIDs could easily grow over 231 which some clients start treating as negative numbers, which then cause all kinds of problems. Also when 232 is exceeded, Dovecot will also start having some problems. Content-Length is used as long as another valid mail starts after that many bytes. Because the byte count must be exact, it's quite unlikely that abusing it can cause messages to be skipped (or rather appended to the previous message's body). Status and X-Status headers are trusted completely, so it's pretty good idea to filter them in LDA if possible. ## Dovecot's Speed Optimizations Updating messages' flags and keywords can be a slow operation since you may have to insert a new header (Status, X-Status, X-Keywords) or at least insert data in the header's value. Some mbox MUAs do this simply by rewriting all of the mbox after the inserted data. If the mbox is large, this can be very slow. Dovecot optimizes this by always leaving some space characters after some of its internal headers. It can use this space to move only minimal amount of data necessary to get the necessary data inserted. Also if data is removed, it just grows these spaces areas. There are several configuration options that can be used that will affect optimization: - `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)` - `[mbox_lazy_writes](/main/core/summaries/settings.html#mbox_lazy_writes)` - `[mbox_very_dirty_syncs](/main/core/summaries/settings.html#mbox_very_dirty_syncs)` ## From Escaping In mboxes a new mail always begins with a "From " line, commonly referred to as `From_`\-line. To avoid confusion, lines beginning with "From " in message bodies are usually prefixed with '>' character while the message is being written to in mbox. Dovecot doesn't currently do this escaping however. Instead it prevents this confusion by adding Content-Length headers so it knows later where the next message begins. Dovecot also doesn't remove the '>' characters before sending the data to clients. ## Mbox Variants There are a few minor variants of this format: ### mboxo An original mbox format originated with Unix System V. Messages are stored in a single file, with each message beginning with a line containing "From SENDER DATE". If "From " (case-sensitive, with the space) occurs at the beginning of a line anywhere in the email, it is escaped with a greater-than sign (to ">From "). Lines already quoted as such, for example ">From " or ">>>From " are not quoted again, which leads to irrecoverable corruption of the message content. ### mboxrd Named for Raul Dhesi in June 1995, though several people came up with the same idea around the same time. An issue with the mboxo format was that if the text ">From " appeared in the body of an email (such as from a reply quote), it was not possible to distinguish this from the mailbox format's quoted ">From ". mboxrd fixes this by always quoting already quoted "From " lines (e.g. ">From ", ">>From ", ">>>From ", etc.) as well, so readers can just remove the first ">" character. This format is used by qmail and getmail (>=4.35.0). ### mboxcl Originated with Unix System V Release 4 mail tools. It adds a Content-Length field which indicates the number of bytes in the message. This is used to determine message boundaries. It still quotes "From " as the original mboxo format does (and not as mboxrd does it). ### mboxcl2 Like mboxcl but does away with the "From " quoting. Dovecot uses this format internally. ### MMDF (Multi-channel Memorandum Distribution Facility mailbox format) originated with the MMDF daemon. The format surrounds each message with lines containing four control-A's. This eliminates the need to escape From: lines. ## How a message is read stored in mbox extension - An email client reader scans throughout mbox file looking for `From_` lines. - Any `From_` line marks the beginning of a message. - Once the reader finds a message, it extracts a (possibly corrupted) envelope sender and delivery date out of the `From_` line. - It then reads until the next `From_` line or scans till the end of file, whenever `From_` comes first. - It removes the last blank line and deletes the quoting of `>From_` lines and `>>From_` lines and so on. ## Known Problems ### External modifications In general Dovecot doesn't mind if you modify the mbox file externally. It's fine if external software expunges messages or appends new ones. However moving around existing messages, inserting messages in the middle of the file or modifying existing messages isn't allowed. Especially modifying existing messages (eg. removing attachments) may cause all kinds of problems. If you do that, at the minimum go and delete `dovecot.index.cache` file from the mailbox, otherwise weird things may happen. However IMAP protocol guarantees that messages don't change at all, and deleting Dovecot's cache file doesn't clear clients' local caches, so it still may not work right. If you insert messages, or if you "undelete" messages (eg. replace mbox from a backup), you may see errors in Dovecot's logs: ``` mbox sync: UID inserted in the middle of mailbox /home/tss/mail/inbox (817 > 787, seq=18, idx_msgs=32) ``` This is normal. Dovecot just assigned new UIDs for the messages. See below for other reasons why UID insertions could happen. ### Debugging UID insertions The above error message can be read as: "18th message in the mbox file contained X-UID: 787 header, however the index file at that position told the message was supposed to have UID 817. There are 32 messages currently in the index file." There are four possibilities why the error message could happen: 1. Message with a X-UID: 787 header really was inserted in the mbox file. For example you replaced mbox from a backup. 2. Something changed the X-UID headers. Very unlikely. 3. The message was expunged from the index file, but for some reason it wasn't expunged from the mbox file. The index file is updated only after a successful mbox file modification, so this shouldn't really happen either. 4. If this problem happens constantly, it could mean that you're sharing the same index file for multiple different mboxes! - This could happen if you let Dovecot do mailbox autodetection and it sometimes uses `/var/mail/%{user}` (when it exists) and other times `~/mail/inbox`. Use explicit [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) settings to make sure the same INBOX is used. - Another possibility is that you're sharing index files between multiple users. Each user must have their own home directory. It's possible that broken X-UID headers in mails and `[mbox_lazy_writes = yes](/main/core/summaries/settings.html#mbox_lazy_writes)` combination has some bugs. If you're able to reproduce such an error, please let us know how. ### UIDVALIDITY changes UIDVALIDITY is stored in X-IMAPbase: or X-IMAP: header of the first message in mbox file. This is done by both Dovecot and UW-IMAP (and Pine). It's also stored in `dovecot.index` file. It shouldn't normally change, because if it does it means that client has to download all the messages for the mailbox again. If the UIDVALIDITY in mbox file doesn't match the one in `dovecot.index` file, Dovecot logs an error: ``` UIDVALIDITY changed (1100532544 -> 1178155834) in mbox file /home/user/mail/mailbox ``` This can happen when the following happens: 1. Dovecot accesses the mailbox saving the current UIDVALIDITY to `dovecot.index` file. 2. The UIDVALIDITY gets lost from the mbox file - X-IMAP: or X-IMAPbase: header gets lost because something else than Dovecot or UW-IMAP deletes the first message - The whole file gets truncated - Something else than Dovecot deletes or renames the mbox 3. The mailbox is accessed (or created if necessary) by UW-IMAP or Pine. It notices that the mailbox is missing UIDVALIDITY, so it assigns a new UIDVALIDITY and writes the X-IMAPbase: or X-IMAP: header. - Also Dovecot that's configured to not use index files behaves the same. 4. Dovecot accesses again the mailbox. UIDVALIDITY in the mbox file's header doesn't match the one in `dovecot.index` file. It logs an error and updates the UIDVALIDITY in the index file to the new one. ### Crashes Dovecot's mbox code is a bit fragile because of the way it works. However instead of just corrupting the mbox file, it usually assert-crashes whenever it notices an inconsistency. You may see crashes such as: ``` Panic: mbox /home/user/mail/mailbox: seq=2 uid=45 uid_broken=0 originally needed 12 bytes, now needs 27 bytes ``` This is a bit difficult problem to fix. Usually this crash has been related to Dovecot rewriting some headers that were broken. If you see these crashes, it would really help if you were able to reproduce the crash. If you have such a mailbox which crashes every time when it's tried to be opened, please put the mbox through [mbox anonymizer](https://github.com/dovecot/tools/blob/main/mbox-anonymize.pl) and send it, the mailbox's `dovecot.index` and `dovecot.index.log` files to [dovecot@dovecot.org](mailto:dovecot@dovecot.org). None of those files contain any actual message contents so it's safe to send them. ### Avoiding Crashes and Errors Since the problems usually have been related to broken headers, you should be able to avoid them by filtering out all the Dovecot's internal metadata headers. This is a good idea to do in any case. If you use [LDA](/main/core/config/delivery/lda.html) it does this filtering automatically. Otherwise you could do this in your SMTP server. The headers that you should filter out are: - Content-Length - Status - X-IMAP - X-IMAPbase - X-Keywords - X-Status - X-UID - X-UIDL (if you're using `[pop3_reuse_xuidl = yes](/main/core/summaries/settings.html#pop3_reuse_xuidl)`) ## Configuration ### Settings #### `mbox_dirty_syncs` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mbox_very_dirty_syncs](/main/core/summaries/settings.html#mbox_very_dirty_syncs)` - [mbox](/main/core/config/mailbox_formats/mbox.html) Enable optimized mbox syncing? For larger mbox files, it can take a long time to determine what has changed when the file is altered unexpectedly. Since the change in most cases consists solely of newly appended mail, Dovecot can operate more quickly if it starts off by simply reading the new messages, then falls back to reading the entire mbox file if something elsewhere in it isn't as expected. Dovecot assumes that external mbox file changes only mean that new messages were appended to it. Without this setting Dovecot re-reads the whole mbox file whenever it changes. There are various safeguards in place to make this setting safe even when other changes than appends were done to the mbox. The downside to this setting is that external message flag modifications may not be visible immediately. When this setting is enabled, Dovecot tries to avoid re-reading the mbox every time something changes. Whenever the mbox changes (i.e. timestamp or size), Dovecot first checks if the mailbox's size changed. If it didn't, it most likely meant that only message flags were changed so it does a full mbox read to find it. If the mailbox shrunk, it means that mails were expunged and again Dovecot does a full sync. Usually however the only thing besides Dovecot that modifies the mbox is the LDA which appends new mails to the mbox. So if the mbox size was grown, Dovecot first checks if the last known message is still where it was last time. If it is, Dovecot reads only the newly added messages and goes into "dirty mode". As long as Dovecot is in dirty mode, it can't be certain that mails are where it expects them to be, so whenever accessing some mail, it first verifies that it really is the correct mail by finding its X-UID header. If the X-UID header is different, it fallbacks to a full sync to find the mail's correct position. The dirty mode goes away after a full sync. If `[mbox_lazy_writes](/main/core/summaries/settings.html#mbox_lazy_writes)` was enabled and the mail didn't yet have an X-UID header, Dovecot uses the MD5 sum of a couple of headers to compare the mails. #### `mbox_dotlock_change_timeout` **Default:** `2 mins` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Override a lockfile after this amount of time if a dot-lock exists but the mailbox hasn't been modified in any way. #### `mbox_lazy_writes` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) If enabled, mbox headers (e.g., metadata updates, such as writing X-UID headers or flag changes) are not written until a full write sync is performed (triggered via IMAP EXPUNGE or CHECK commands and/or when the mailbox is closed). mbox rewrites can be costly, so this may avoid a lot of disk writes. Enabling this setting is especially useful with POP3, in which clients often delete all mail messages. One negative consequence of enabling this setting is that the changes aren't immediately visible to other MUAs. C-Client works the same way. The upside of this is that it reduces writes because multiple flag updates to same message can be grouped, and sometimes the writes don't have to be done at all if the whole message is expunged. The downside is that other processes don't notice the changes immediately (but other Dovecot processes do notice because the changes are in index files). #### `mbox_lock_timeout` **Default:** `5 mins` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) The maximum time to wait for all locks to be released before aborting. #### `mbox_md5` | Key 1 | | --- | | Default | `apop3d` | | Value | [string](/main/core/settings/types.html#string) | | Allowed Values | `apop3d``all` | | See Also | - `[pop3_uidl_format](/main/core/summaries/settings.html#pop3_uidl_format)` - [mbox](/main/core/config/mailbox_formats/mbox.html) | | Advanced Setting; this should not normally be changed. | The mail-header selection algorithm to use for MD5 POP3 UIDLs when the setting `[pop3_uidl_format = %{md5}](/main/core/summaries/settings.html#pop3_uidl_format)` is applied. #### `mbox_min_index_size` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [size](/main/core/settings/types.html#size) | | See Also | - [mbox](/main/core/config/mailbox_formats/mbox.html) | | Advanced Setting; this should not normally be changed. | For mboxes smaller than this size, index files are not written. If an index file already exists, it gets read but not updated. The default should not be changed for most installations. #### `mbox_read_locks` **Default:** `fcntl` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Allowed Values:** `dotlock``dotlock_try``fcntl``flock``lockf` **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Specify which locking method(s) to use for locking the mbox files during reading. Descriptions of the locking methods can be found at [mbox locking](/main/core/config/mailbox_formats/mbox.html#locking). #### `mbox_very_dirty_syncs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)` - [mbox](/main/core/config/mailbox_formats/mbox.html) If enabled, Dovecot performs the optimizations from `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)` also for the IMAP SELECT, EXAMINE, EXPUNGE, and CHECK commands. If set, this option overrides `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)`. #### `mbox_write_locks` **Default:** `dotlock fcntl` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Allowed Values:** `dotlock``dotlock_try``fcntl``flock``lockf` **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Specify which locking method(s) to use for locking the mbox files during writing. Descriptions of the locking methods can be found at [mbox locking](/main/core/config/mailbox_formats/mbox.html#locking). ### Mail Location Configuration In many systems, the user's mails are by default stored in `/var/mail/username` file. This file is called INBOX in IMAP world. Since IMAP supports multiple mailboxes, you'll need to have a directory for them as well. Usually `~/mail` is a good choice for this. For an installation such as this, the mail location is specified with: doveconf ``` # %{user} is replaced with the username that logs in mail_driver = mbox mail_path = ~/mail mail_inbox_path = /var/mail/%{user} ``` It's in no way a requirement to have the INBOX in `/var/mail/` directory. In fact, this often just brings problems because Dovecot might not be able to write dotlock files to the directory (see below). You can avoid this completely by just keeping everything in `~/mail/`: doveconf ``` # INBOX exists in ~/mail/inbox mail_driver = mbox mail_path = ~/mail ``` #### Default mail settings - `[mail_path = %{home}/mail](/main/core/summaries/settings.html#mail_path)`, - `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)`, - `[mailbox_subscriptions_filename = .subscriptions](/main/core/summaries/settings.html#mailbox_subscriptions_filename)`, and - `[mail_inbox_path = inbox](/main/core/summaries/settings.html#mail_inbox_path)` with fs layout (so INBOX is in `~/mail/inbox` rather than `~/mail/INBOX`). ### Index Files By default, index files are stored under an `.imap/` directory. See the [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) for an explanation of how to change the index path. Example: doveconf ``` mail_driver = mbox mail_path = ~/mail mail_inbox_path = /var/mail/%{user} mail_index_path = /var/indexes/%{user} ``` ### Locking Make sure that all software accessing the mboxes are using the same locking methods in the same order. The order is important to prevent deadlocking. From Dovecot's side you can change these from `[mbox_read_locks](/main/core/summaries/settings.html#mbox_read_locks)` and `[mbox_write_locks](/main/core/summaries/settings.html#mbox_write_locks)` settings. #### /var/mail/ Dotlocks Often mbox write locks include dotlock, which means that Dovecot needs to create a new `.lock` file to the directory where the mbox file exists. If your INBOXes are in `/var/mail/` directory, you may have to give Dovecot write access to the directory. There are two ways the `/var/mail/` directory's permissions have traditionally been set up: - World-writable with sticky bit set, allowing anyone to create new files but not overwrite or delete existing files owned by someone else (i.e. same as `/tmp`). You can do this with `chmod a+rwxt /var/mail`. - Directory owned by a mail group and the directory set to group-writable (mode=0770, group=mail) You can give Dovecot access to mail group by setting: doveconf ``` mail_privileged_group = mail ``` NOTE: With [LDA](/main/core/config/delivery/lda.html) the `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` setting unfortunately doesn't work, so you'll have to use the sticky bit, disable dotlocking completely, or use LMTP server instead. ### /var/mail/\* Permissions In some systems the `/var/mail/$USER` files have 0660 mode permissions. This causes Dovecot to try to preserve the file's group, and if it doesn't have permissions to do so, it'll fail with an error like: ``` imap(user): Error: chown(/home/user/mail/.imap/INBOX, -1, 12(mail)) failed: Operation not permitted (egid=1000(user), group based on /var/mail/user) ``` There is rarely any real need for the files to have 0660 mode, so the best solution for this problem is to just change the mode to 0600: sh ``` chmod 0600 /var/mail/* ``` ### Only /var/mail/ mboxes With POP3 it's been traditional that users have their mails only in the `/var/mail/` directory. IMAP however supports having multiple mailboxes, so each user has to have a private directory where the mailboxes are stored. Dovecot also needs a directory for its index files unless you disable them completely. If you **really** want to use Dovecot as a plain POP3 server without index files, you can work around not having a per-user directory: - Set users' home directory in an empty non-writable directory, e.g. `[mail_home = /var/empty](/main/core/summaries/settings.html#mail_home)`. - Set `[mail_path](/main/core/summaries/settings.html#mail_path)` to an empty non-writable directory, e.g. `[mail_path = /var/empty](/main/core/summaries/settings.html#mail_path)`. - Set `[mail_inbox_path](/main/core/summaries/settings.html#mail_inbox_path)`, e.g. `[mail_inbox_path = /var/mail/%{user}](/main/core/summaries/settings.html#mail_inbox_path)`. - Note that if you have IMAP users, they'll see `/var/empty` as the directory containing other mailboxes than INBOX. If the directory is writable, all the users will have their mailboxes shared. ### Directory Layout By default Dovecot uses `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)` layout under mbox. This means that mail is stored in mbox files under hierarchical directories, for example: | File | Description | | --- | --- | | `~/mail/inbox` | mbox file containing mail for INBOX | | `~/mail/foo` | mbox file containing mail for mailbox "foo" | | `~/mail/bar/baz` | mbox file containing mail for mailbox "bar/baz" | One upshot of this is that it is not normally possible to have mailboxes which are subfolders of mailboxes containing messages. As an alternative, it is possible to configure Dovecot to store all mailboxes in a single directory with hierarchical levels separated by a dot. This can be configured by adding `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` to the mail location. There are, however, some further considerations when doing this; see [child folders](#child-folders) for some examples. ### Control Files Under mbox format, Dovecot maintains the subscribed mailboxes list in a file `.subscriptions` which by default is stored in the mail location root. So in the example configuration this would be at `~/mail/.subscriptions`. If you want to put this somewhere else, you can change the directory in which the `.subscriptions` file is kept by using the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting. For example: doveconf ``` mail_driver = mbox mail_path = ~/mail mail_control_path = ~/mail-control ``` would store the subscribed mailboxes list at `~/mail-control/.subscriptions`. One practical application of the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting is described at [child folders](#child-folders). ### Message Filename By default, Dovecot stores messages for INBOX in an mbox file called "inbox", and messages for all other mailboxes in an mbox file whose relative path is equivalent to the name of the mailbox. Under this scheme, it is not possible to have mailboxes which contain both messages and child mailboxes. However, the behaviour (for mailboxes other than INBOX) can be changed using the `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` setting. If it is specified, Dovecot stores messages in a mbox file with a name of that value, in a directory with a name equivalent to the mailbox name. There are, however, some further considerations when doing this; see [child folders](#child-folders) for an example. ### Child Folders Under mbox, it is not normally possible to have a mail folder which contains both messages and sub-folders. This is because there would be a filesystem name collision between the name of the mbox file containing the messages and the name of the directory containing the sub-folders. For example: - Mail folder "foo" containing messages would be stored in a file at `~/mail/foo`. - Mail folder "foo/bar" containing messages would be stored in a file at `~/mail/foo/bar`, but this cannot happen because this relies on the existence of a directory `~/mail/foo/` which can't exist because there is already a file with that name. Under mbox, Dovecot normally stores mail folders in "filesystem" layout. In this layout, mail folders are stored in mbox files (potentially under subdirectories) with the same relative path as the mail folder path. For example: | File | Description | | --- | --- | | `~/mail/foo` | mbox file containing mail for mail folder "foo"; cannot create any mail sub-folders of "foo" | | `~/mail/bar/baz` | mbox file containing mail for mail folder "bar/baz"; cannot create any mail sub-folders of "bar/baz" | | `~/mail/inbox` | mbox file containing mail for INBOX | If there is a requirement to be able to have a mail folder which contains both messages and sub-folders, then there are two ways to do it: 1. Maildir++ layout 2. Messages in named file These approaches are described in more detail below. #### Maildir++ Layout Dovecot can be configured to keep mbox mail in a Maildir++-like layout. This makes Dovecot keep mail in mbox files where all the mailbox folder naming levels are separated with dots (with a leading dot). For example: | File | Description | | --- | --- | | `~/mail/.foo` | mbox file containing mail for mail folder "foo" | | `~/mail/.foo.bar` | mbox file containing mail for mail folder "foo/bar". We can now do this. | | `~/mail/.bar.baz` | mbox file containing mail for mail folder "bar/baz" | | `~/mail/inbox` | mbox file containing mail for INBOX | This can be enabled by adding the `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` setting to the mail location: doveconf ``` # Incomplete example. Do not use! mail_driver = mbox mail_path = ~/mail mailbox_list_layout = maildir++ ``` However, there is a problem. Under mbox, the `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` setting alone leaves Dovecot unable to place index files, which would likely result in performance issues. So when using `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` with mbox, it is advisable to also configure `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)`. Now, mail files (other than INBOX) all have names beginning with a dot, so if we like we can store other things in the `~/mail` directory by using names which do not begin with a dot. So we could think to use `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` to store indexes at `~/mail/index/`. Example: doveconf ``` # Incomplete example. Do not use! mail_driver = mbox mail_path = ~/mail mail_index_path = ~/mail/index mailbox_list_layout = maildir++ ``` If we do this, then indexes will be kept at `~/mail/index/` and this will not clash with any names used for mail folders. There is one more thing we may want to consider though. By default Dovecot will maintain a list of subscribed folders in a file `.subscriptions` under the mail location root. In this case that means it would end up at `~/mail/.subscriptions`. This would then mean that it would be impossible to create a mail folder called "subscriptions". We can get around this by using the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting to move the `.subscriptions` file somewhere else, for example into the directory `~/mail/control` (again, choosing a name which doesn't begin with a dot so we don't collide with the names of mbox files storing mail folders). That gives us: doveconf ``` # Trick mbox configuration which allows a mail folder which contains both # messages and sub-folders mail_driver = mbox mail_path = ~/mail mailbox_list_layout = maildir++ mail_index_path = ~/mail/index mail_control_path = ~/mail/control ``` This then allows mail folders which contains both messages and sub-folders without possibility of naming collisions between mail folders and other data. There is one further wrinkle. Specifying `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` for mbox changes the default hierarchy separator from a slash to a dot. This should not be a problem for IMAP clients as the hierarchy separator is exposed through IMAP. However anything which expects to just "know" that the hierarchy separator is a slash may get confused. This can be worked around by configuring [Namespaces](/main/core/config/namespaces.html) to set the folder separator back to a slash. #### Messages in Named File In the default "filesystem" example from above, we can't create any sub-folders of "foo" because there is a file - `foo` - in the way. So we could think to get rid of that file and put a directory there instead. But if we do that then we need somewhere to put the messages for folder "foo". We could think to put them in a specially-named file in the directory: `foo/`. Then if we wanted to create a sub-folder of "foo" we would be fine because we could then do that. The rule would then be that messages go into the specially-named file in the directory corresponding to the mail folder name. We want to choose a special name which would be unlikely to collide with a folder name. We could think to use something like `mBoX-MeSsAgEs`. Now, it turns out that you can configure Dovecot to do this using the `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` setting: doveconf ``` # Incomplete example. Do not use! mail_driver = mbox mail_path = ~/mail mailbox_directory_name = mBoX-MeSsAgEs ``` With that config, we would get a layout like this: | File | Description | | --- | --- | | `~/mail/inbox` | mbox file containing mail for INBOX | | `~/mail/foo/mBoX-MeSsAgEs` | mbox file containing mail for mail folder "foo" | | `~/mail/foo/bar/mBoX-MeSsAgEs` | mbox file containing mail for mail folder "foo/bar" | However there is a problem. Under mbox, setting `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` alone leaves Dovecot unable to place index files, which would likely result in performance issues, or worse, if the index directory gets created first, this will obstruct the creation of the mbox file. So when using `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` with mbox, it is also necessary to configure `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)`. The question then arises where to put index files. Any directory under the `~/mail` directory could be considered as a mail folder. We could think to use a name beginning with a dot, for example `~/mail/.index` but that would then mean that it would not be possible to create a mail folder called ".index"; unlikely, but it would be nice to have as few implementation-specific restrictions as possible. In addition, by default, Dovecot will create a file `.subscriptions` at the mail location root to hold a list of mailbox subscriptions. This would make it impossible to create a mail folder called ".subscriptions". But we can move the `.subscriptions` file to another directory by using the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting. To get around these issues, we can add another directory layer which separates these purposes. For example: doveconf ``` # Trick mbox configuration which allows a mail folder which contains both # messages and sub-folders mail_driver = mbox mail_path = ~/mail/mailboxes mailbox_directory_name = = mBoX-MeSsAgEs mail_index_path = ~/mail/index mail_control_path = ~/mail/control ``` would result in the following layout: | File | Description | | --- | --- | | `~/mail/mailboxes/foo/mBoX-MeSsAgEs` | mbox file containing messages for mail folder "foo" | | `~/mail/mailboxes/foo/bar/mBoX-MeSsAgEs` | mbox file containing messages for mail folder "foo/bar" | | `~/mail/mailboxes/inbox` | mbox file containing messages for INBOX | | `~/mail/control/.subscriptions` | File containing list of subscribed mailboxes | | `~/mail/index/INBOX/dovecot.index.*` | Index files for INBOX | | `~/mail/index/foo/dovecot.index.*` | Index files for mail folder "foo" | | `~/mail/index/foo/bar/dovecot.index.*` | Index files for mail folder "foo/bar" | | `~/mail/index/dovecot.mailbox.log` | Other index files | Restrictions on mail folder names are then minimised; we can't have mail folders with the names "mBoX-MeSsAgEs", "dovecot.index.\*, or "dovecot.mailbox.log". Unlike the Maildir++ layout approach above, because we are still using "filesystem" layout, the hierarchy separator remains as a slash. # Mailbox Formats Mailbox formats control the way that mail data is stored. ## Available Formats Mailbox formats supported by Dovecot: ### mbox See [mbox](/main/core/config/mailbox_formats/mbox.html). Traditional UNIX mailbox format. Users' INBOX mailboxes are commonly stored in `/var/spool/mail` or `/var/mail` directory. Single file contains multiple messages. ### Maildir See [Maildir](/main/core/config/mailbox_formats/maildir.html). One file contains one message. A reliable choice since files are never modified and all operations are atomic. The top-level Maildir directory contains the `Maildir/cur`, `Maildir/new`, and `Maildir/tmp` subdirectories. ### dbox See [dbox](/main/core/config/mailbox_formats/dbox.html). Dovecot's own high performance mailbox format. Messages are stored in one or more files, each containing one or more messages. There are two flavors of dbox: - `sdbox`: "single-dbox" - one message per file - `mdbox`: "multi-dbox" - multiple messages per file ### imapc See [imapc](/main/core/config/mailbox_formats/imapc.html). Use remote IMAP server as mail storage. ### pop3c See [pop3c](/main/core/config/mailbox_formats/pop3c.html). Use remote POP3 server as mail storage. ## Configuration See [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting) for configuration information. ## Physical Storage The mailbox formats define how Dovecot stores mail data, but it does not address where that data will physically live - that is a decision for the administrator to make. There are two general categories of storage: local and shared. ### Local Storage #### Filesystems - See [Maildir](/main/core/config/mailbox_formats/maildir.html) for Maildir-specific filesystem optimizations - Dovecot doesn't rely on atime updates, so you can mount the filesystem with `noatime` #### Index Files Keeping index files on a different disk than the mail spool gives you better performance. The indexes have a lot of write activity so it is recommended to use RAID-10 instead of RAID-5 for them. #### Fsyncing By default, Dovecot calls `fsync()` and `fdatasync()` whenever it's useful to prevent potential data loss. The main reason for this is so that Dovecot won't lie that the message was saved to the disk, if in fact a power failure a second later would lose the message. With IMAP clients this is perhaps a less serious problem, because the lost message was most likely either a mail in Draft mailbox or a message in "Sent Messages" mailbox; in other words, a message that the user had already seen. However if [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) loses a message, the user never even knew that the message existed, unless the sender decides to resend it. Since power failures and kernel panics are quite rare, many people are tempted to disable fsyncing because it may increase the performance quite a lot. Dovecot allows this by setting `[mail_fsync = never](/main/core/summaries/settings.html#mail_fsync)`. However, this is dangerous, especially with IMAP, LDA, and LMTP. If you do want to set to `never`, you should only explicitly do this for services that you are comfortable with data loss. Example: doveconf ``` # Default mail_fsync = optimized protocol pop3 { # Enable fsyncing for POP3 mail_fsync = never } ``` ## Shared Storage The recommended storage solution for large installations that require high-availability and scalable performance is object storage. [Dovecot Pro](https://www.dovecotpro.com/) provides the obox mailbox format to efficiently interact with object storage systems. Dovecot allows keeping mails and index files in clustered filesystems. Dovecot does not specifically support any specific clustered solution - it is the responsibility of the admin to perform functional and load testing to guarantee the storage solution provides adequate performance. Dovecot also supports keeping mails and index files on NFS. Everything described in this page applies to NFS as well, but see [NFS](/main/core/config/nfs.html) for additional NFS-specific problems and optimizations. Dovecot CE only supports mailbox access on a single server: a user can only be accessed by a single Dovecot server at a time. ### Memory Mapping By default, Dovecot `mmap()s` the index files. This may not work with all clustered filesystems, and it most certainly won't work with NFS. Setting `[mmap_disable = yes](/main/core/summaries/settings.html#mmap_disable)` disables `mmap()` and Dovecot does its own internal caching. If `mmap()` is supported by your filesystem, it's still not certain that it gives better performance. Try benchmarking to make sure. ### Locking Dovecot supports locking index files with fcntl (default), flock or dotlocks. Some clustered filesystems may not support fcntl, so you can change it to use flock instead. Fcntl locks may also cause problems with some NFS configurations, in which case you can try if switching to dotlocks helps. Note that dotlocks are the slowest locking method. You can change the locking method from `[lock_method](/main/core/summaries/settings.html#lock_method)` setting. Regardless of the `lock_method` setting, Dovecot always uses dotlocks for some locks. ### Clock Synchronization Run ntpd on each node to make sure clocks are synchronized. If the clocks are more than one second apart from each others and multiple computers access the same mailbox simultaneously, you may get errors from Dovecot. ### Caching Your cluster will probably perform better if users are usually redirected to the same server. This is because the mailbox may already be cached in the memory and it may also reduce the traffic between the clusterfs nodes. At the very least, make sure that your load balancer redirects connections from the same IP address to the same server, if possible. ### FUSE / GlusterFS FUSE caches dentries and file attributes internally. If you're using multiple GlusterFS clients to access the same mailboxes, you're going to have problems. Worst of these problems can be avoided by using NFS cache flushes, which just happen to work with FUSE as well: doveconf ``` mail_nfs_index = yes mail_nfs_storage = yes ``` These probably don't work perfectly. ### Samba / CIFS Dovecot's temporary files may include a colon character `:` in their filename, which is not a permitted character when using CIFS. Dovecot also renames the temporary files whilst holding a lock in them, which generates the error "Text file is busy". In short, CIFS/smbfs is unlikely to work as a remote filesystem. # Pop3c Mailbox Format The pop3c storage accesses a remote POP3 server as if it were a regular (local) Dovecot mailbox format. The remote POP3 mailbox is visible as the INBOX folder on the Dovecot side. ## Settings ### `pop3c_features` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of features, optimizations, and workarounds that can be enabled. Workarounds: `no-pipelining` Prevents use of the PIPELINING extension even when it is advertised. ### `pop3c_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The remote POP3 host to connect to. ### `pop3c_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_password](/main/core/summaries/settings.html#pop3c_password)` - `[pop3c_user](/main/core/summaries/settings.html#pop3c_user)` The master username to authenticate as on the remote POP3 host. To authenticate as a master user but use a separate login user, the following configuration should be employed, where the credentials are represented by masteruser and masteruser-secret: ``` pop3c_user = %{user} pop3c_master_user = masteruser pop3c_password = masteruser-secret ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `pop3c_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_master_user](/main/core/summaries/settings.html#pop3c_master_user)` - `[pop3c_user](/main/core/summaries/settings.html#pop3c_user)` The authentication password for the remote POP3 server. If using master users, this setting will be the password of the master user. ### `pop3c_port` **Default:** `110` **Value:** [Port Number](/main/core/settings/types.html#port-number) The port on the remote POP3 host to connect to. ### `pop3c_quick_received_date` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, pop3c doesn't require calling TOP for each message in order to get the metadata. ### `pop3c_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Log all POP3 traffic input/output to this directory. ### `pop3c_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``pop3s``starttls` Use TLS to connect to the remote POP3 server. | Value | Description | | --- | --- | | `no` | No TLS | | `pop3s` | Explicitly connect to remote POP3 port using TLS | | `starttls` | Use POP3 STARTTLS command to switch to TLS connection | ### `pop3c_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[pop3c_ssl](/main/core/summaries/settings.html#pop3c_ssl)` Verify remote POP3 TLS certificate? Verification may be disabled during testing, but should be enabled during production use. Only used if `[pop3c_ssl](/main/core/summaries/settings.html#pop3c_ssl)` is enabled. ### `pop3c_user` **Default:** `%{user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_master_user](/main/core/summaries/settings.html#pop3c_master_user)` - `[pop3c_password](/main/core/summaries/settings.html#pop3c_password)` The user identity to be used for performing authentication to the source POP3 server. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ## Configuration Example Connect using STARTTLS to pop3.example.com: doveconf ``` # In-memory index files: mail_driver = pop3c mail_path = # OR, Store index files locally: #mail_path = ~/pop3c pop3c_host = pop3.example.com pop3c_password = secret pop3c_port = 110 pop3c_ssl = starttls pop3c_user = user@example.com ``` # Namespaces Dovecot supports fully configurable, hierarchical namespaces, which can use different storage drivers. Their original and primary purpose is to provide Namespace IMAP extension ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) support, which allows giving IMAP clients hints about where to locate mailboxes and whether they're private, shared or public. Dovecot namespaces can be used for several other purposes too: - Changing the [Hierarchy Separators](/main/core/config/namespaces.html#hierarchy-separators) - Providing backwards compatibility when switching from another IMAP server - Provides support for [public shared mailboxes](/main/core/config/shared_mailboxes.html#public-shared-mailboxes) and [user shared mailboxes](/main/core/config/shared_mailboxes.html#user-shared-mailboxes) mailboxes - Allows having mails in multiple different locations with possibly different formats These different locations and drivers are presented to the client as a single tree. Each namespace has: - prefix (usually empty or "INBOX.") - namespace separator (usually '/' or '.') - 0 or more folders There must be one namespace where the case-insensitive folder named INBOX exists. All visible namespaces must have the same separator. Inside each namespace there is a list of folders, which form a sub-hierarchy. INFO Enable default INBOX namespace and English language mailbox names with `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)` flags with `[@mailbox_defaults = english](/main/core/summaries/settings.html#@mailbox_defaults)` Using this setting will set these settings by default: Click to show settings doveconf ``` namespace inbox { inbox = yes mailbox Drafts { auto = subscribe special_use = \Drafts } mailbox Junk { auto = subscribe special_use = \Junk } mailbox Sent { auto = subscribe special_use = \Sent } mailbox Trash { auto = subscribe special_use = \Trash } } ``` ## Folder Names Each folder has a name. In configuration files and log files Dovecot almost always uses the "virtual name", which uses the configured namespace's hierarchy separator as well as the namespace prefix. Depending on the used `[mailbox_list_layout](/main/core/summaries/settings.html#mailbox_list_layout)` setting the internal folder name may be different. The internal name is stored in databases (e.g. mailbox subscriptions), which allows changing the namespace prefix or separator without having to change the databases. The folder names use UTF-8 character set internally. All folder names must be valid UTF-8. With `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)` and `[mailbox_list_layout = Maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` the folder names are stored in filesystem paths as mUTF-7 (see IMAP [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)) mainly for legacy reasons. This can be changed with the `[mailbox_list_utf8](/main/core/summaries/settings.html#mailbox_list_utf8)` setting. ### Folder Name Lengths Folder name length restrictions: - Maximum length of an individual folder name within a hierarchy is 255 bytes. For example with "a/b/c" hierarchy each of the a, b and c can be a maximum of 255 bytes. - The maximum folder path length is 4096 bytes. The maximum folder name lengths work correctly when folder names aren't stored in filesystem, i.e. `[mailbox_list_layout = index](/main/core/summaries/settings.html#mailbox_list_layout)` is used. Otherwise the OS adds its own limitations to path name lengths and the full 4096 bytes can't be used. With `[mailbox_list_layout = Maildir++](/main/core/summaries/settings.html#mailbox_list_layout)` the path must fit to 254 bytes (due to OS limitations). ### Parent Folders A folder can have one or more parent folders that do not physically exist. These are presented with `\NoSelect` or `\Nonexistent` attribute. It's possible to try to avoid creating these by using the `[mailbox_list_drop_noselect](/main/core/summaries/settings.html#mailbox_list_drop_noselect)` setting (enabled by default). ## Configuration If the Dovecot configuration doesn't explicitly specify a namespace, the inbox namespace is created automatically. Namespace configuration is defined within a dovecot configuration block with the format: doveconf ``` namespace { [... namespace settings ...] } ``` The namespace name is only used internally within configurations. It allows you to update an existing namespace - by repeating the namespace block and adding additional configuration settings - or allows userdb to override namespace settings for specific users, e.g.: ``` namespace/name/prefix=foo/ ``` Example configuration for default namespace: doveconf ``` namespace inbox { separator = . prefix = inbox = yes } ``` ## Settings ### `namespace` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) Creates a new namespace to the list of namespaces. The filter name refers to the `[namespace_name](/main/core/summaries/settings.html#namespace_name)` setting. Example: ``` namespace foo { [...] } ``` ### `namespace_alias_for` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Refers to an alias namespace's `[namespace_name](/main/core/summaries/settings.html#namespace_name)`. If multiple namespaces point to the same location, they should be marked as aliases against one primary namespace. This avoids duplicating work for some commands (listing the same mailbox multiple times). `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. Note: Alias namespaces often have `hidden=yes` and `list=no` so they are not visible unless clients have specifically configured them, and they're typically used when migrating to a different namespace prefix for existing users. Example: ``` namespace inbox { prefix = # ... } namespace alias { prefix = INBOX/ alias_for = inbox } ``` ### `namespace_disabled` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, namespace is disabled and cannot be accessed by user in any way. Useful when returned by a userdb lookup to easily configure per-user namespaces. ### `namespace_hidden` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, namespace will be hidden from IMAP NAMESPACE ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) command. ### `namespace_ignore_on_failure` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If namespace's storage initialization fails, by default the entire session will fail to start. If this is set, this namespace will be ignored instead. ### `namespace_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, this namespace will be considered the one holding the INBOX folder. There can be only one namespace defined like this. ### `namespace_list` **Default:** `yes` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `yes``no``children` **See Also:** - `[namespace_hidden](/main/core/summaries/settings.html#namespace_hidden)` Include this namespace in LIST output when listing its parent's folders. Options: | Value | Description | | --- | --- | | `children` | Namespace prefix list listed only if it has child mailboxes. | | `no` | Namespace and mailboxes not listed unless listing requests explicitly mailboxes under the namespace prefix. | | `yes` | Namespace and mailboxes are always listed. | It is still possible to list the namespace's folders by explicitly asking for them. For example, if this setting is `no`, using `LIST "" *` with namespace prefix "lazy-expunge/" won't list it, but using `LIST "" lazy-expunge/*` lists all folders under it. ### `namespace_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the namespace. This is used only in configurations - it's not visible to user. The `[namespace](/main/core/summaries/settings.html#namespace)` filter refers to this setting. ### `namespace_order` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Sets display order in IMAP NAMESPACE ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) command. Namespaces are automatically numbered if this setting does not exist. ### `namespace_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specifies prefix for namespace. INFO Must end with `[namespace_separator](/main/core/summaries/settings.html#namespace_separator)`. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. Example: ``` namespace shared { prefix = Shared/ separator = / } ``` ### `namespace_separator` **Default:** `"." for Maildir; "/" for other mbox formats` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Namespaces: Hierarchy Separators](/main/core/config/namespaces.html#hierarchy-separators) Specifies the hierarchy separator for the namespace. The separator is a single character, which can't then otherwise be used in folder names. The commonly used separators are `.` and `/`, but other separators can be used as well. For example `^` is less likely to be found in normal folder names. Recommended value is to leave it empty and accept the default value. ### `namespace_subscriptions` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether subscriptions are stored in this namespace. This is usually `no` for shared namespaces so that the shared folders' subscriptions are stored in the user's primary subscriptions file. If `no`, the subscriptions are stored in the first parent namespace (based on the prefix) that has this setting enabled. Example: If this setting is `no` for a namespace with `prefix=foo/bar/`, Dovecot first sees if there's a `prefix=foo/` namespace with `subscriptions=yes` and then a namespace with an empty prefix. If neither is found, an error is given. ### `namespace_type` **Default:** `private` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `private``shared``public` The namespace type. One of: | Type | Description | | --- | --- | | `public` | Contains [public shared mailboxes](/main/core/config/shared_mailboxes.html#public-shared-mailboxes). | | `private` | Typically contains only user's own private mailboxes. | | `shared` | Contains other users' [user shared mailboxes](/main/core/config/shared_mailboxes.html#user-shared-mailboxes). | ## Hierarchy Separators `[namespace_separator](/main/core/summaries/settings.html#namespace_separator)` specifies the character that is used to separate the parent mailbox from its child mailbox. For example if you have a mailbox "foo" with child mailbox "bar", the full path to the child mailbox would be "foo/bar" with `/` as the separator, and "foo.bar" with `.` as the separator. IMAP clients, Sieve scripts, and many parts of Dovecot configuration use the configured separator when referring to mailboxes. This means that if you change the separator, you may break things. However, changing the separator doesn't change the on-disk "layout separator". Example: | `mailbox_list_layout` | Layout Separator | Namespace Separator | Mailbox Name | Directory | | --- | --- | --- | --- | --- | | `Maildir++` (default) | `.` | `.` | `foo.bar` | `~/Maildir/.foo.bar/` | | `Maildir++` (default) | `.` | `/` | `foo/bar` | `~/Maildir/.foo.bar/` | | `fs` | `/` | `.` | `foo.bar` | `~/Maildir/foo/bar/` | | `fs` | `/` | `/` | `foo/bar` | `~/Maildir/foo/bar/` | TIP The "namespace separator" changes only the "mailbox name", but doesn't change the directory where the mails are stored. The "layout separator" can only be changed by changing `[mailbox_list_layout](/main/core/summaries/settings.html#mailbox_list_layout)`, which also affects the entire directory structure. The layout separator also restricts the mailbox names. For example if the layout separator is `.`, you can't just set separator to `/` and create a mailbox named `foo.bar`. A commonly used separator is `/`. It probably causes the least amount of trouble with different IMAP clients. The `^` separator is troublesome with Thunderbird. If `\` has to be used, it needs to be escaped in configuration: doveconf ``` namespace inbox { separator = "\\" } ``` You should use the same hierarchy separator for all namespaces. All `list=yes` namespaces must use the same separator, but if you find it necessary (e.g. for backwards compatibility namespaces) you may use different separators for `list=no` namespaces. ## Values From userdb To change namespace settings from userdb, you need to return `namespace//setting+=value`. To create a namespace, make sure you first return `namespace+=[,,...]` and settings after this. This adds a new given namespace. Without `+` all the namespaces are replaced with the given list. doveconf ``` userdb static { fields { namespace += special namespace/special/mail_path = /var/special/%{user} namespace/special/prefix = special/ } } ``` ## Shared Mailboxes See [shared mailboxes](/main/core/config/shared_mailboxes.html). ## Examples ### Mixed mbox and Maildir If you have your INBOX as mbox in `/var/mail/username` and the rest of the mailboxes in Maildir format under `~/Maildir`, you can do this by creating two namespaces: doveconf ``` namespace inbox { separator = / prefix = "#mbox/" mail_driver = mbox mail_path = ~/mail mail_inbox_path = /var/mail/%{user} inbox = yes hidden = yes list = no } namespace maildir { separator = / prefix = mail_driver = maildir mail_path = ~/Maildir } ``` Without the `list = no` setting in the first namespace, clients would see the "#mbox" namespace as a non-selectable mailbox named "#mbox" but with child mailboxes (the mbox files in the "~/mail" directory), i.e. like a directory. So specifically with `inbox = yes`, having `list = no` is often desirable. ### Backwards Compatibility: UW-IMAP When switching from UW-IMAP and you don't want to give users full access to filesystem, you can create hidden namespaces which allow users to access their mails using their existing namespace settings in clients. doveconf ``` # default namespace namespace inbox { separator = / prefix = inbox = yes } # for backwards compatibility: namespace compat1 { separator = / prefix = mail/ hidden = yes list = no alias_for = inbox } namespace compat2 { separator = / prefix = ~/mail/ hidden = yes list = no alias_for = inbox } namespace compat3 { separator = / prefix = ~%{user}/mail/ hidden = yes list = no alias_for = inbox } ``` ### Backwards Compatibility: Courier IMAP **Recommended:** You can continue using the same `INBOX.` namespace as Courier: doveconf ``` namespace inbox { separator = . prefix = INBOX. inbox = yes } ``` **Alternatively:** Create the `INBOX.` as a compatibility name, so old clients can continue using it while new clients will use the empty prefix namespace: doveconf ``` namespace inbox { separator = / prefix = inbox = yes } namespace compat { separator = . prefix = INBOX. inbox = no hidden = yes list = no alias_for = inbox } ``` The `separator=/` allows the INBOX to have child mailboxes. Otherwise with `separator=.` it wouldn't be possible to know if "INBOX.foo" means INBOX's "foo" child or the root "foo" mailbox in "INBOX." compatibility namespace. With `separator=/` the difference is clear with "INBOX/foo" vs. "INBOX.foo". The alternative configuration is not recommended, as it may introduce issues: - Although clients may do `LIST INBOX.*`, they may still do `LSUB *`, resulting in mixed results. - If clients used empty namespace with Courier, they now see the mailboxes with different names, resulting in redownloading of all mails (except INBOX). - Some clients may have random errors auto-detecting the proper default folders (Sent, Drafts etc) if the client settings refer to old paths while the server lists new paths. See also [migrating mailboxes](/main/core/admin/migration.html) ### Per-user Namespace Location From SQL You need to give the namespace a name, for example "docs" below: doveconf ``` namespace docs { type = public separator = / prefix = Public/ } ``` Then you have an SQL table like: sql ``` CREATE TABLE Namespaces ( [...] Location varchar(255) NOT NULL, [...] ) ``` Now if you want to set the namespace location from the Namespaces table, use something like: doveconf ``` userdb sql { query = SELECT Location as 'namespace/docs/mail_path' \ FROM Namespaces \ WHERE .. } ``` If you follow some advice to separate your "INBOX", "shared/" and "public/" namespaces by choosing "INBOX/" as your prefix for the inboxes you will see, that you run into troubles with subscriptions. Thats, because there is no parent namespace for "shared/" and "public/" if you set `subscriptions = no` for those namespaces. If you set `subscriptions = yes` for "shared/" and "public/" you will see yourself in the situation, that all users share the same subscription files under the location of those mailboxes. One good solution is, to create a so called "hidden subscription namespace" with subscriptions turned on and setting `subscriptions = no` for the other namespaces: doveconf ``` namespace subscriptions { subscriptions = yes prefix = "" list = no hidden = yes } namespace inbox { inbox = yes subscriptions = no prefix = INBOX/ separator = / mailbox Drafts { auto = subscribe special_use = \Drafts } mailbox Sent { auto = subscribe special_use = \Sent } mailbox "Sent Messages" { special_use = \Sent } mailbox Spam { auto = subscribe special_use = \Junk } mailbox Trash { auto = subscribe special_use = \Trash } } namespace shared { type = shared prefix = shared/$user/ mail_driver = mdbox mail_path = %{owner_home}/mdbox mail_index_private_path = %{owner_home}/mdbox/shared list = children subscriptions = no } namespace public { type = public separator = / prefix = public/ mail_driver = mdbox mail_path = /usr/local/mail/public/mdbox mail_index_private_path = ~/mdbox/public subscriptions = no list = children } ``` ## Mailbox Settings Mailbox configuration is typically defined inside a `[namespace](/main/core/summaries/settings.html#namespace)` block so it only applies to the specific namespace. ### Settings #### `mailbox` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) Create a new mailbox to the list of mailboxes. The filter name refers to the `[mailbox_name](/main/core/summaries/settings.html#mailbox_name)` setting. The mailbox name can contain `?` and `*` wildcards. Settings are applied for all matching mailbox filters. TIP If the mailbox name has spaces, you can put it into quotes: ``` mailbox "Test Mailbox" { # ... } ``` TIP It's possible to rename the mailbox in userdb. For example: ``` mailbox junk { name = Junk special_use = \Junk } ``` The userdb can then return `mailbox/junk/name=Spam` to rename the mailbox for a specific user. #### `mailbox_auto` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `create``no``subscribe` Autocreate and/or subscribe to the mailbox? | Value | Description | | --- | --- | | `create` | Autocreate but don't autosubscribe | | `no` | Don't autocreate or autosubscribe | | `subscribe` | Autocreate and autosubscribe | Autocreated mailboxes are created lazily to disk only when accessed for the first time. The autosubscribed mailboxes aren't written to subscriptions file, unless SUBSCRIBE command is explicitly used for them. #### `mailbox_autoexpunge` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) **See Also:** - `[mailbox_autoexpunge_max_mails](/main/core/summaries/settings.html#mailbox_autoexpunge_max_mails)` Expunge all mails in this mailbox whose saved-timestamp is older than this value. `0` means autoexpunging is disabled. For IMAP and POP3 this happens after the client is already disconnected. For LMTP this happens when the user's mail delivery is finished. Note that in case there are multiple recipients, autoexpunging is done only for some of the recipients to prevent delays with the mail delivery: The last recipient user is autoexpunged first. Next, the first recipient user is autoexpunged (because the first user's mail was kept open in case it could be directly copied to the other users). None of the middle recipient users are autoexpunged. `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)` is highly recommended when using this setting, as it avoids actually opening the mailbox to see if anything needs to be expunged. `[mail_always_cache_fields = date.save](/main/core/summaries/settings.html#mail_always_cache_fields)` is also recommended when using this setting with [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) or [Maildir](/main/core/config/mailbox_formats/maildir.html), as it avoids using `stat()` to find out the mail's saved-timestamp. With [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) format this isn't necessary, since the saved-timestamp is always available. #### `mailbox_autoexpunge_max_mails` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Mails are autoexpunged until mail count is at or below this number of messages. Once this threshold has been reached, `[mailbox_autoexpunge](/main/core/summaries/settings.html#mailbox_autoexpunge)` processing is done. #### `mailbox_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the mailbox being configured. The `[mailbox](/main/core/summaries/settings.html#mailbox)` filter name refers to this setting. #### `mailbox_special_use` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Namespaces](/main/core/config/namespaces.html) **Changes:** - Changed: 2.4.0 Using non-standard special-use flags will result in a warning message at startup. List of SPECIAL-USE ([RFC 6154](https://datatracker.ietf.org/doc/html/rfc6154)) flags to broadcast for the mailbox. There are no validity checks, so you could specify anything you want here, but it's not a good idea to use other than the standard ones specified in the RFC. ### Example In general it's recommended to use `[@mailbox_defaults = english](/main/core/summaries/settings.html#@mailbox_defaults)`, which will create and autosubscribe to a standard default set of mailboxes with appropriate `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)` settings. In case you need separate or additional folders with special use flags, consider the following example: doveconf ``` namespace inbox { # the namespace prefix isn't added again to the mailbox names. #prefix = INBOX. inbox = yes # ... mailbox Trash { auto = no special_use = \Trash } mailbox Drafts { auto = no special_use = \Drafts } mailbox Sent { auto = subscribe # autocreate and autosubscribe the Sent mailbox special_use = \Sent } mailbox "Sent Messages" { auto = no special_use = \Sent } mailbox Spam { auto = create # autocreate Spam, but don't autosubscribe special_use = \Junk } mailbox virtual/All { # if you have a virtual "All messages" mailbox auto = no special_use = \All } } ``` # NFS Dovecot is commonly used with NFS. However, Dovecot does **not** support accessing the same user simultaneously by different servers. That will result in more or less severe mailbox corruption. Note that this applies to all mailbox access, including mail delivery. - Users must be assigned to specific backends (i.e. in the proxy's passdb lookups). - Use [LMTP Server](/main/core/config/delivery/lmtp.html) for mail deliveries. - Set `[mmap_disable = yes](/main/core/summaries/settings.html#mmap_disable)` - Set `[mail_fsync = always](/main/core/summaries/settings.html#mail_fsync)` - Do **not** set `[mail_nfs_index](/main/core/summaries/settings.html#mail_nfs_index)` or `[mail_nfs_storage](/main/core/summaries/settings.html#mail_nfs_storage)` (i.e. keep them as `no`) - Do **not** use the `quota-status` service. - Unmounted NFS mount point directory should not be writable to Dovecot mail processes (i.e. often the `vmail` user). Otherwise if the NFS isn't mounted for some reason and user access mails, a new empty user mail directory is created, which breaks things. ## NFS Mount Options - `actimeo`: This or the more specific settings can be used to control NFS caching. Increasing this can reduce NFS traffic. It should be at least 60 seconds (`actimeo=60`). - `nordirplus`: Disable readdirplus operations, which aren't needed by Dovecot. They can also slow down some NFS servers. - `noatime`: Disable updating atime. Dovecot doesn't need this and it may slow down NFS servers. - `root_squash`: Dovecot doesn't care about this. Typically Dovecot doesn't store any root-owned files in NFS. - `nolock` / `local_lock=all`: This is possible to use as a slightly unsafe optimization. All file locking is handled only locally instead of via NFS server. Assuming users are never accessed simultaneously by multiple backends, there is no need to use locking across NFS. Each user only locks their own files, and the user should only be accessed by a single server at a time. In some rare situations the same user can become accessed by multiple servers simultaneously. In those situations the mails are more likely to become corrupted if `nolock` is used. However, if indexes and emails are on different mountpoints, email corruption shouldn't be possible if the `nolock` is enabled only for the index mountpoint. This can still increase the likelihood of index corruption (which can lose message flags), but locking won't prevent index corruption completely anyway. ## Optimizations Potential optimizations to use: - mdbox format is likely more efficient to use than the sdbox format. The downside is that it requires running periodic `[doveadm purge](/main/core/summaries/doveadm.html#purge)` for each user. Theses commands should be run via a doveadm proxy so they are run in the proper backends. - Use `[mail_volatile_path = /dev/shm/dovecot/%{user | sha1 % 256 | hex(2)}/%{user}](/main/core/summaries/settings.html#mail_volatile_path)` to store some temporary files (e.g. lock files) in tmpfs rather than NFS. - Use `[mailbox_list_index_prefix = /fast/%{user | sha1 % 256 | hex(2)}/%{user}](/main/core/summaries/settings.html#mailbox_list_index_prefix)` to use "smaller fast storage" for index files and "larger slow storage" for mail files. Also use `[mailbox_list_iter_from_index_dir = yes](/main/core/summaries/settings.html#mailbox_list_iter_from_index_dir)` to list mailboxes via the fast index storage rather than the slow mail storage. - Use `[mailbox_list_iter_from_index_dir = /slow/%{user | sha1 % 256 | hex(2)}/%{user}](/main/core/summaries/settings.html#mailbox_list_iter_from_index_dir)` to use "smaller fast storage" for new mails and "larger slow storage" for old mails. The `[doveadm altmove](/main/core/summaries/doveadm.html#altmove)` command needs to be run periodically. Also use `[mail_alt_check = no](/main/core/summaries/settings.html#mail_alt_check)` to disable a sanity check to make sure alt storage path doesn't unexpectedly change. - See the NFS mount options above. ## Clock Synchronization Run ntpd in the NFS server and all the NFS clients to make sure their clocks are synchronized. If the clocks are more than one second apart from each others and multiple computers access the same mailbox simultaneously, you may get errors. # Dovecot Optimizations ## TZ (timezone) Environment Variable The use of the TZ environment variable can dramatically reduce the number of system calls and kernel context switches performed by the application. The `localtime()` function in glibc checks whether the `TZ` environment variable is set. If it is not set, then glibc will use the `stat()` system call every time `localtime()` is called, even with vDSO in place. Setting the `TZ` environment variable to `:/etc/localtime` (or some other timezone file of your choice) for a process will save glibc from making those extra unnecessary system calls (Notice the column `:` prefix before the file path). The variable can be set either from the shell: sh ``` export TZ=:/etc/localtime ``` or directly from inside the Dovecot configuration file: doveconf ``` import_environment { TZ = :/etc/localtime } ``` Note that a reload is not sufficient for the change to take effect. A restart is required. TIP See Also: - [GNU man page for the TZ environment variable](https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html), and - [Article explaining in detail the issue](https://blog.packagecloud.io/set-environment-variable-save-thousands-of-system-calls/). ## Disk I/O Usually heavily loaded IMAP and POP3 servers don't use much CPU, but they use all the disk I/O they can get. So reducing disk I/O is probably the most useful optimization you can do. - See [mbox](/main/core/config/mailbox_formats/mbox.html) for mbox-specific optimizations. - See [Maildir](/main/core/config/mailbox_formats/maildir.html) for Maildir-specific optimizations. - See [dbox](/main/core/config/mailbox_formats/dbox.html) for Dovecot's own high-performance mailbox format. It usually gives much better performance than mbox/Maildir. - See [fts plugin](/main/core/plugins/fts.html) for optimizing IMAP SEARCH command. - See [POP3](/main/core/config/pop3.html) for POP3 optimizations, especially `[pop3_no_flag_updates = yes](/main/core/summaries/settings.html#pop3_no_flag_updates)`. - `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)` can help a lot by replying to IMAP STATUS (and similar) lookups from a single index without having to open each mailbox index separately. This is the default. - Also `[mailbox_list_index_very_dirty_syncs = yes](/main/core/summaries/settings.html#mailbox_list_index_very_dirty_syncs)` makes Dovecot assume that the list index is up-to-date. - `[mail_prefetch_count](/main/core/summaries/settings.html#mail_prefetch_count)` setting may be helpful with some mailbox formats. - `[mail_volatile_path = /tmp/dovecot-volatile/%{user | sha1 % 256 | hex(2)}/%{user}](/main/core/summaries/settings.html#mail_volatile_path)` moves, e.g., lock files to the volatile directory. This is helpful especially if the [mail location settings](/main/core/config/mail_location.html#mail-location-setting) otherwise point to a remote filesystem like NFS. - If the [acl plugin](/main/core/plugins/acl.html) is used, but only global ACLs are needed, set `[acl_globals_only = yes](/main/core/summaries/settings.html#acl_globals_only)`. ## CPU Usage - See [login processes](/main/core/config/login_processes.html) for optimizing CPU usage caused by logins. - See `[auth_cache_size](/main/core/summaries/settings.html#auth_cache_size)` setting for caching passdb and userdb lookups. - To distribute password hash calculations to multiple CPU cores (via auth-worker processes), set `[auth_cache_verify_password_with_worker = yes](/main/core/summaries/settings.html#auth_cache_verify_password_with_worker)`. - Services having [service configuration (client\_limit)](/main/core/config/service.html#client-limit) > 1 and [service configuration (process\_limit)](/main/core/config/service.html#process-limit) > 1, set [service configuration (process\_min\_avail)](/main/core/config/service.html#protocol) to the number of CPU cores. - To reduce forks by reusing existing processes for new requests, increase `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` from `1` to higher (e.g. `100`) for imap and pop3 services. It's better not to set it too high or unlimited, because different users use different amounts of memory, and it's wasteful when a lot of processes end up having a lot of "free" memory. ## Memory Usage There aren't many settings which affect Dovecot's memory usage. In general Dovecot uses as much memory as it needs, which is usually quite little. - `[auth_cache_size](/main/core/summaries/settings.html#auth_cache_size)` controls maximum memory size for caching passdb/userdb lookups - [high performance login](/main/core/config/login_processes.html#high-performance-mode) for login processes - `[imap_hibernate_timeout](/main/core/summaries/settings.html#imap_hibernate_timeout)`\` controls when to move IDLEing IMAP connections to wait for changes in a shared imap-hibernate process. This frees up the imap process. Note that these settings do NOT directly affect the memory usage: - [service configuration (vsz\_limit)](/main/core/config/service.html#vsz-limit): These are simply safe guards against potential memory leaks. If the process's virtual size reaches the limit, the process is killed by the kernel. - [service configuration (process\_limit)](/main/core/config/service.html#process-limit) and [service configuration (client\_limit)](/main/core/config/service.html#client-limit): These are mostly to avoid DoS attacks using up all your memory. ### glibc For Linux/glibc it's recommended to set: doveconf ``` import_environment { MALLOC_MMAP_THRESHOLD_ = 131072 } ``` This allocates all memory larger than 128 kB using mmap(), which allows the OS to free the memory afterwards. This is important for backends which have a lot of long-running imap and pop3 processes. ## Operating Systems WARNING Dovecot is developed for Linux. Although Dovecot may work on other OS platforms, the main developer focus (and the focus of this page) is exclusively Linux. The default Linux configurations are usually quite good. The only things needed to check are: - `/proc/sys/fs/inotify/max_user_watches` and `max_user_instances` need to be large enough to handle all the IDLEing IMAP processes. ``` fs.inotify.max_user_instances = 65535 fs.inotify.max_user_watches = 65535 ``` - In order to reduce I/O on the backends, it is recommended to disable the ext4 journal: sh ``` tune2fs -O ^has_journal /dev/vdb e2fsck -f /dev/vdb ``` - Dovecot doesn't require atimes, so you can mount the filesystem with noatime: sh ``` mount -o defaults,discard,noatime /dev/vdb /storage ``` - All the servers' hostnames must be unique. This is relied on in many different places. - Make sure the servers are running ntpd or some other method of synchronizing clocks. The clocks shouldn't differ more than 1 second. The time must never go backwards - this is especially important in Dovecot backends when using Cassandra, because otherwise `DELETEs` or `UPDATEs` may be ignored when the query timestamp is older than the previous `INSERT/UPDATE`. - With busy servers Dovecot might run out of TCP ports. It may be useful to increase `net.ipv4.ip_local_port_range`. ``` net.ipv4.ip_local_port_range = 1024 65500 ``` ## TIME-WAIT Connections - `net.ipv4.tcp_tw_reuse=1` can help to avoid "Cannot assign requested address" errors for outgoing connections and is rather safe to set. It only affects outgoing connections. See: [https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux](https://vincent.bernat.ch/en/blog/2014-tcp-time-wait-state-linux) ### NOT Recommended Adjusting TCP buffer sizes is also usually a bad idea, unless your kernel is very old and you have good knowledge of the types of TCP traffic (number of connections, bandwidth consumed, activity patterns etc) you will have. # Configuration Overview - [Dovecot Config File Syntax](/main/core/settings/syntax.html) - [All Dovecot Settings](/main/core/summaries/settings.html#all-dovecot-settings) - [Quick configuration](/main/core/config/quick.html) # Dovecot Backend Dovecot can be configured for use in "backend" mode on a single server. In this mode, Dovecot is responsible for reading and writing mails to storage and handling all of the email protocols. # Dovecot Proxy Dovecot can be configured for use in "proxy" mode on a single server. In this mode, Dovecot is responsible for proxying incoming email protocols to remote hosts. See [proxy passdb](/main/core/config/proxy/overview.html). # POP3 Dovecot was primarily designed to be an IMAP server, so although it works fine as a POP3 server, it's not really optimized for that. ## Maildir Performance The main problem with Dovecot's POP3 implementation with [Maildir](/main/core/config/mailbox_formats/maildir.html) is how to get messages' size fast enough. The POP3 specification requires that the sizes are reported exactly, not just approximately. This means that linefeeds must be counted as `CR+LF` characters. Normally with Maildir the linefeeds are stored as plain LF characters, which means that simply getting the file size would produce the wrong POP3 message size. Some Maildir POP3 servers do this anyway and violate the POP3 specification. Dovecot returns correct message sizes by reading the entire message and counting the linefeeds correctly. After this is done, the `virtual size` is stored into `dovecot-uidlist` file and future calculations can be avoided by simply looking up the cached value. You can also avoid the initial message size calculation by storing the size directly into the filename. You can do this by appending ,`W=` at the end of the base filename. For example `1199932653.M583975P6568.host,W=2211:2`, is a file whose virtual size is 2211 bytes (and real size somewhat smaller). Note that this must not be done for existing files, only to newly delivered mails. If Dovecot's [LDA](/main/core/config/delivery/lda.html) is used, `dovecot-uidlist` and the index files are updated upon message arrival, therefore there will be no message-size performance issues. `[pop3_fast_size_lookups = yes](/main/core/summaries/settings.html#pop3_fast_size_lookups)` setting uses the virtual message sizes when they're already available, but fallbacks to using the physical message sizes (violating POP3 specifications, but then again a lot of POP3 servers do that). ## mbox Performance Index files are quite useless if your users don't keep mails in the server. They get first updated when the POP3 session starts to include all the messages, and after the user has deleted all the mails, they again get updated to contain zero mails. With this kind of a session the index reads and writes could have been avoided if the index files had just been completely disabled. You may want to try how performance changes if you disable indexes for POP3 users. You can also try preserving indexes but try different values for `[mbox_min_index_size](/main/core/summaries/settings.html#mbox_min_index_size)`. Do not disable indexing if there are users that do not delete messages after downloading them. Also, if you use Dovecot LDA, indexes may be helpful to have fast access to the message sizes. ## Session Locking By default Dovecot allows multiple POP3 connections to the same mailbox. This is (was?) especially useful for dialup connections which die in the middle of the download, because the half-dead connections won't keep the mailbox locked. Setting `[pop3_lock_session = yes](/main/core/summaries/settings.html#pop3_lock_session)` makes Dovecot lock the mailbox for the whole session. This is also what the POP3 [RFC 1939](https://datatracker.ietf.org/doc/html/rfc1939) specifies should be done. If another connection comes while the mailbox is locked, Dovecot waits until the locking times out (2 minutes with Maildir, `[mbox_lock_timeout](/main/core/summaries/settings.html#mbox_lock_timeout)` with mbox). ## Flag Changes By default when a message is RETRed, \\Seen flag is added to it. POP3 itself doesn't support flags, but if the mailbox is opened with IMAP it's shown as seen. You can disable this (to get better performance) with `[pop3_no_flag_updates = yes](/main/core/summaries/settings.html#pop3_no_flag_updates)`. ## POP3 Client Workarounds pop3\_client\_workarounds setting allows you to set some workarounds to avoid POP3 clients breaking with some broken mails. See `[pop3_client_workarounds](/main/core/summaries/settings.html#pop3_client_workarounds)`. ## UIDL format UIDLs are used by POP3 clients to keep track of what messages they've downloaded, typically only if you've enabled keep messages in server option. If the UIDL changes, the existing messages are re-downloaded as new messages, which the users don't really appreciate. Dovecot supports multiple different ways to set the UIDL format, mostly to make migrations from other POP3 servers transparent by preserving the old UIDL values. See [migrating mailboxes](/main/core/admin/migration.html) for how to set the UIDLs to be compatible with your previous POP3 server. For new POP3 servers, the easiest way to set up UIDLs is to use IMAP's UIDVALIDITY and UID values. The default is: doveconf ``` pop3_uidl_format = %{uid | hex(8)}%{uidvalidity | hex(8)} ``` Another good default is to use the message's global UID: doveconf ``` pop3_uidl_format = %{guid} ``` However, note that GUIDs may not be unique, as the GUID does not change when a message is copied. (While copying is not possible using only POP3, it can be done using IMAP, Sieve, or doveadm.) ### MD5 UIDL format (mbox-only) doveconf ``` pop3_uidl_format = %{md5} ``` This works by getting the MD5 sum of a couple of message headers that uniquely identify the message. The one good thing about MD5 format is that it doesn't rely on the IMAP UID or UIDVALIDITY value. This allows you to modify the mbox files in ways that Dovecot doesn't like, without causing the UIDLs to change. For example: - Inserting messages in the middle of mbox files (eg. restoring mbox files from backups can cause "Expunged message reappeared" errors) - Reordering messages inside mbox - [Other random problems](/main/core/config/mailbox_formats/mbox.html) causing UID renumbering (although you should figure out why they're happening) The MD5 summing method however doesn't work well if you receive two identical messages. Usually the MD5 sum is taken from these headers: - The first Received: header - Delivered-To: header Normally there won't be a problem, because the MTA adds a unique identifier to the first Received: header. If the same message is sent to multiple users in one delivery, the Delivered-To: header is still different, making the MD5 sum different. Except the MTA can be configured to support aliases, so for example sending the mail to both root@ and webmail@ aliases causes the message to be delivered to the same user, with identical Received: and Delivered-To: headers. The messages really are identical, so their MD5 sums are also identical, and that can cause some POP3 clients to keep downloading the messages over and over again, never deleting them. To avoid this, there's also a 3rd header that is included in the MD5 sum calculation: - X-Delivery-ID: header If you use [LDA](/main/core/config/delivery/lda.html) or IMAP APPEND and `[pop3_uidl_format = %{md5}](/main/core/summaries/settings.html#pop3_uidl_format)` it always appends the X-Delivery-ID: header to saved mailbox. Any existing X-Delivery-ID: headers in the saved mails are dropped. ## Optimization ### Don't Update \\Seen Flag Improve performance by not updating the IMAP \\Seen flag whenever downloading mails via POP3: `[pop3_no_flag_updates = yes](/main/core/summaries/settings.html#pop3_no_flag_updates)`. # Post-login Scripting If you want to do something special after authentication, but before beginning the IMAP or POP3 session, you can do this by telling imap/pop3 executable to use post-login service by editing `dovecot.conf`: doveconf ``` service imap { # tell imap to do post-login lookup using a socket called "imap-postlogin" executable = imap imap-postlogin } # The service name below doesn't actually matter. service imap-postlogin { # all post-login scripts are executed via script-login binary executable = script-login /usr/local/bin/postlogin.sh # the script process runs as the user specified here (v2.0.14+): user = $SET:default_internal_user # this UNIX socket listener must use the same name as given to imap executable unix_listener imap-postlogin { } } ``` You can run multiple post-login scripts by just giving multiple scripts as parameters to `script-login`, for example: doveconf ``` executable = script-login rawlog /usr/local/bin/postlogin.sh /usr/local/bin/postlogin2.sh ``` The scripts are run in the specified order. Remember that the post-login script runs with the privileges of the `user` setting given to the service (root by default). If you need the script to access user's mail files, change it to whatever user owns the mails (e.g. vmail). If you're using multiple UNIX UIDs (e.g. system users), use `script-login -d` to drop to the `UID` or `GID` specified by the userdb lookup (ignoring user/group/chroot service settings). It's not currently possible to run post-login scripts in [proxy passdb](/main/core/config/proxy/overview.html) because they're not actually logging into the local Dovecot. ## Running Environment Standard input and output file descriptors are redirected to the client's network socket, so you can send data to client by simply writing to stdout. Standard error fd is redirected to Dovecot's error log, you can write errors there as well. The script can use environment variables: - `USER`: Username - `IP`: Remote IP address - `LOCAL_IP`: Local IP address - Fields returned by [userdb](/main/core/config/auth/userdb.html) lookup with their keys uppercased (e.g. if userdb returned home, it's stored in `HOME`). It's possible to add/modify userdb fields by adding them to environment and adding the field to `USERDB_KEYS`. For example to change user's mail location: sh ``` #!/bin/sh export MAIL_DRIVER=maildir export MAIL_PATH=/tmp/test export USERDB_KEYS="$USERDB_KEYS mail_driver mail_path" exec "$@" ``` You can change any Dovecot settings using the above method. ## Custom Mailbox Location Autodetection See [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting). ## Example Actions TIP Generally, many of these tasks can today be done much easier (and entirely within Dovecot configuration) by using [Lua authentication database](/main/core/config/auth/databases/lua.html). ### Alerts If you want to give the user's client some warning notification, you can do it just by writing it to stdout. But note: - Not all clients show the alerts, even though IMAP RFC requires it. - IMAP protocol requires CRLF (\\r\\n) line feeds. Some clients will break if you only send LF. sh ``` #!/bin/sh if [ -f ~/.out-of-office ]; then printf "* OK [ALERT] You're still marked as being out of office.\r\n" fi exec "$@" ``` ### Use UNIX Groups for ACL Authorization sh ``` #!/bin/sh ACL_GROUPS=`groups $USER | tr ' ' ','` export ACL_GROUPS export USERDB_KEYS="$USERDB_KEYS acl_groups" exec "$@" ``` ### Denying Connection from Some IP/User You can use the IP and USER shell variables that are setup by Dovecot in a bash script in order to deny connection (after a successful login), like this: bash ``` #!/bin/bash if [ "$USER" = "myuser" ] ; then printf "* NO [ALERT] The user '$USER' cannot login\r\n" exit 0 fi if [ ! "$IP" = "192.168.1.1" ] ; then printf "* NO [ALERT] Access not allowed from the Internet\r\n" exit 0 fi exec "$@" ``` ### Dynamically Adding Shared Mailboxes According to Filesystem Permissions Details shared\_mailboxes.plEnvironment Variables perl ``` #!/usr/bin/perl use strict; my $SHAREDDIR= '/var/spool/mail/Shared'; if (! @ARGV) { exit 1; } # for testing... #if ($ENV{USER} eq 'lemur') { # # print "* OK [ALERT] Hello $ENV{'USER'}!\n"; # &set_namespaces(); # system("env >> /tmp/dovecot-env-$$"); #} &set_namespaces(); exec(@ARGV) or die "Unable to exec @ARGV: $!"; sub set_namespaces { my $mailbox; local *D; if (opendir(D, $SHAREDDIR)) { my $dir; my @namespaces = (); while ($mailbox= readdir(D)) { next if ($mailbox =~ /^\./); if (-r "${SHAREDDIR}/${mailbox}") { my $nsname = 'S-'.uc($mailbox); push(@namespaces, lc($nsname)); &log("adding NAMESPACE/${nsname}/PREFIX ${SHAREDDIR}/${mailbox}"); $ENV{"NAMESPACE/${nsname}/MAIL_PATH"} = "$SHAREDDIR/$mailbox" $ENV{"NAMESPACE/${nsname}/MAIL_INDEX_PATH"} = "~/Maildir/index/Shared/$mailbox"; $ENV{"NAMESPACE/${nsname}/PREFIX"} = "Shared/$mailbox/"; $ENV{"NAMESPACE/${nsname}/TYPE"}= "public"; $ENV{"NAMESPACE/${nsname}/SEPARATOR"}= "/"; $ENV{"NAMESPACE/${nsname}/LIST"}= "yes"; # $ENV{"NAMESPACE/${nsname}/SUBSCRIPTIONS"} = "no" } } closedir D; if (@namespaces) { $ENV{"NAMESPACE"} = join(' ', @namespaces); my @userdb_keys; if ($ENV{'USERDB_KEYS'}) { push(@userdb_keys, $ENV{'USERDB_KEYS'}); } push(@userdb_keys, grep(/^NAMESPACE/, keys(%ENV))); $ENV{'USERDB_KEYS'} = join(' ', @userdb_keys); } } } sub log { print STDERR "@_\n"; } ``` ``` NAMESPACE/S-SPAMREP/LIST=yes NAMESPACE/S-SPAMREP/MAIL_PATH=/var/spool/mail/Shared/spamrep NAMESPACE/S-SPAMREP/MAIL_INDEX_PATH=~/Maildir/index/Shared/spamrep NAMESPACE/S-SPAMREP/PREFIX=Shared/spamrep/ NAMESPACE/S-SPAMREP/SEPARATOR=/ NAMESPACE/S-SPAMREP/TYPE=public NAMESPACE/S-TESTSHARED/LIST=yes NAMESPACE/S-TESTSHARED/MAIL_PATH=/var/spool/mail/Shared/testshared NAMESPACE/S-TESTSHARED/MAIL_INDEX_PATH=~/Maildir/index/Shared/testshared NAMESPACE/S-TESTSHARED/PREFIX=Shared/testshared/ NAMESPACE/S-TESTSHARED/SEPARATOR=/ NAMESPACE/S-TESTSHARED/TYPE=public NAMESPACE=s-testshared s-spamrep USERDB_KEYS=SYSTEM_GROUPS_USER UID GID HOME NAMESPACE/S-SPAMREP/LIST NAMESPACE NAMESPACE/S-TESTSHARED/SEPARATOR NAMESPACE/S-TESTSHARED/TYPE NAMESPACE/S-TESTSHARED/PREFIX NAMESPACE/S-TESTSHARED/LIST NAMESPACE/S-TESTSHARED/MAIL_PATH NAMESPACE/S-TESTSHARED/MAIL_INDEX_PATH NAMESPACE/S-SPAMREP/SEPARATOR NAMESPACE/S-SPAMREP/TYPE NAMESPACE/S-SPAMREP/PREFIX NAMESPACE/S-SPAMREP/MAIL_PATH NAMESPACE/S-SPAMREP/MAIL_INDEX_PATH ``` # Forwarding parameters in IMAP/POP3/LMTP/SMTP proxying Dovecot supports proxying various pieces of information and even variables for various protocols when forwarding connection. It requires that the sender is listed under \[setting,login\_trusted\_networks\]\]. For IMAP, it uses the `ID` command, for other protocols, `XCLIENT` is used. ## IMAP For IMAP, forwarding is done by extending the ID ([RFC 2971](https://datatracker.ietf.org/doc/html/rfc2971)) command. - RFC Requirements - Maximum key length is 30 bytes. - Value strings MUST NOT be longer than 1024 octets. - Dovecot has exactly 1024 byte limit to values. Trying to send 1025 bytes results in "BYE Input buffer full, aborting" response. - Implementations MUST NOT send more than 30 field-value pairs. - That being said, there doesn't seem to be any limit to number of field-value pairs Dovecot can accept. In a test of thousands of pairs sent to Dovecot, there was not any increased memory usage (since each key-value pair was read separately and then discarded when not used). The parameters are forwarded as part of the ID command field-value list. ``` 5 ID ("x-originating-ip" "127.0.0.1" "x-originating-port" "143" ...) ``` ### Supported Fields | Field | Description | | --- | --- | | `x-originating-ip` | Client IP address | | `x-originating-port` | Client port | | `x-connected-ip` | Server IP address | | `x-connected-port` | Server port address | | `x-connected-name` | TLS SNI provided name. | | `x-proxy-ttl` | TTL which is reduced by each hop, loop prevention. When TTL drops to 0, the connection is dropped. | | `x-session-id`, `x-session-ext-id` | Session ID to be used. | | `x-forward-` | Forwarded variable, see `[Settings variables](/main/core/settings/variables.html)` | ## POP3 For POP3, forwarding is done with custom `XCLIENT` command which accepts a space separated list of field=value parameters. WARNING There is a 1024 byte line limit for the XCLIENT command when using POP3. Reaching this limit would cause the XCLIENT command to fail. This would be visible to the POP3 client as "-ERR Input buffer full, aborting" or some other AUTH error. ### Supported Fields | Field | Description | | --- | --- | | `ADDR` | Client IP | | `PORT` | Client port | | `DESTADDR` | Added: 2.4.3 Server IP | | `DESTPORT` | Added: 2.4.3 Server port | | `SESSION` | Session ID | | `TTL` | TTL which is reduced by each hop, loop prevention. When TTL drops to 0, the connection is dropped. | | `DESTNAME` | Name provided by TLS SNI. | | `FORWARD` | Base64-encoded, tab-separated list of `key=value` pairs to be forwarded to auth process. The keys and values are escaped using Dovecot's tab-escape format. | ## SMTP/LMTP See [XCLIENT information](https://www.postfix.org/XCLIENT_README.html). ### Supported Fields (SMTP/LMTP) | Field | Description | | --- | --- | | `ADDR` | Client IP; prefix (`IPV6:`) is required for IPv6. However, Dovecot currently forwards without the IPv6 prefix, which does not follow the correct Postfix XCLIENT syntax. | | `DESTADDR` | Added: 2.4.3 Server IP | | `DESTPORT` | Added: 2.4.3 Server port | | `PORT` | Client port | | `TTL` | TTL which is reduced by each hop, loop prevention. When TTL drops to 0, the connection is dropped. | | `HELO` | Original `HELO`/`EHLO` | | `LOGIN` | Original `LOGIN` value | | `TIMEOUT` | Original `TIMEOUT` | | `PROTO` | Forwarded protocol: `SMTP`, `ESTMP`, or `LMTP`. | | `DESTNAME` | Name provided by TLS SNI. | ### Supported Fields (_SMTP/Submission_ ONLY) | Field | Description | | --- | --- | | `FORWARD` | Base64-encoded, tab-separated list of `key=value` pairs to be forwarded to auth process. The keys and values are escaped using Dovecot's tab-escape format. This value is effectively limited to around 1000 bytes. | | `SESSION` | Session ID | | `DESTNAME` | Name provided by TLS SNI. | ### Supported Fields LMTP Additional fields supported for the LMTP `RCPT TO` command: | Field | Description | | --- | --- | | `XRCPTFORWARD` | Base64-encoded, tab-separated list of `key=value` pairs to be forwarded. The keys and values are escaped using Dovecot's tab-escape format. This value is effectively limited to around 900 bytes. | # HAProxy [HAProxy (High Availability Proxy)](https://www.haproxy.org/) is a popular open-source software TCP and HTTP Load Balancer and proxying solution. It is available as a package on almost all Linux distros. WARNING Dovecot CE supports single-server operation only, so load balancing is not applicable. This page exists to document HAProxy-related features that exist in the software. ## HAProxy PROXY Protocol When HAProxy forwards connections to Dovecot, it passes the original client IP using the PROXY protocol. Dovecot accepts both PROXYv1 (text) and PROXYv2 (binary) headers; v2 is preferred because it carries optional TLS metadata (TLV) and parses with less ambiguity. The examples below use PROXYv2. Dovecot uses the PROXY header to log the real client address and apply IP-based restrictions correctly. HAProxy enables PROXYv2 forwarding with one of the following directives on the `server` line: - `send-proxy-v2` — sends client IP in the PROXYv2 header - `send-proxy-v2-ssl` — additionally includes TLS metadata from the HAProxy-terminated session (cipher suite, SNI, etc.); used when HAProxy terminates client TLS - `send-proxy-v2-ssl-cn` — like `send-proxy-v2-ssl`, but also forwards the client certificate Common Name as a PROXYv2 TLV. Dovecot uses the forwarded CN exactly as if it had terminated TLS itself: it becomes the `cert_username` passed to authentication, so SASL `EXTERNAL` (or any username-from-cert flow that relies on `[ssl_server_cert_username_field](/main/core/summaries/settings.html#ssl_server_cert_username_field)`) works through HAProxy. Use only when HAProxy is doing client-certificate authentication for you. Dovecot must be configured to accept PROXY protocol headers: - Set `[inet_listener_haproxy = yes](/main/core/summaries/settings.html#inet_listener_haproxy)` on the `[inet_listener](/main/core/summaries/settings.html#inet_listener)`. - Set `[haproxy_trusted_networks](/main/core/summaries/settings.html#haproxy_trusted_networks)` to the IP address(es) or CIDR range(s) of HAProxy. The value is a space-separated list and accepts CIDR notation, e.g. `haproxy_trusted_networks = 127.0.0.1 10.0.0.0/24 2001:db8::/32`. If a client from an unlisted address connects to a `haproxy` listener, Dovecot logs a warning and disconnects the client. ## HAProxy TLS Termination This section describes how TLS can be handled when using HAProxy in front of Dovecot, and the implications of different configurations. INFO The examples below show IMAP (port 993/143). The same configuration pattern applies to POP3 (ports 995/110), Submission (ports 465/587), and ManageSieve (port 4190) — adjust port numbers accordingly. ### Recommended TLS Deployment The recommended configuration is: - Terminate client TLS at HAProxy - Use TLS between HAProxy and Dovecot when they run on different hosts or share an untrusted network segment - Forward TLS metadata to Dovecot via `send-proxy-v2-ssl` - Optionally use mTLS for backend authentication - Do not expose plaintext ports (143/110) This provides: - Consistent TLS handling at a single point - End-to-end encryption (client → HAProxy → Dovecot) - Reduced risk of configuration drift — no need to keep TLS certificates and policy in sync between HAProxy and Dovecot INFO Backend TLS only adds security when the HAProxy ↔ Dovecot path can be observed or tampered with. When both run on the same host and traffic stays on `127.0.0.1`, backend TLS adds CPU overhead with no threat-model benefit; a plaintext connection to the backend is acceptable in that case. See [HAProxy → Dovecot with TLS](#haproxy-dovecot-with-tls-recommended) for the full example. ### Backend Configurations The following subsections cover the concrete HAProxy ↔ Dovecot backend patterns. The first is the recommended deployment; the others are variants or fallbacks. #### HAProxy → Dovecot with TLS (Recommended) HAProxy terminates client TLS and establishes a new TLS connection to Dovecot. haproxy\_tls\_to\_dovecotdovecot\_tls\_listener haproxy ``` frontend imap_tls bind *:993 ssl crt /etc/haproxy/certs/example.pem mode tcp default_backend dovecot_imap_tls backend dovecot_imap_tls mode tcp server dovecot1 10.0.0.1:993 ssl verify required ca-file /etc/haproxy/ca.pem send-proxy-v2-ssl ``` doveconf ``` ssl = required service imap-login { inet_listener imaps { port = 993 haproxy = yes ssl = yes } } haproxy_trusted_networks = 10.0.0.0/24 ``` **Behavior:** - TLS is terminated at HAProxy and re-established to Dovecot (`ssl verify required`). - `send-proxy-v2-ssl` forwards the _client's_ TLS session metadata (cipher suite, SNI) inside the PROXYv2 header — this is independent of the backend TLS connection. - Dovecot sees the original client IP via PROXY protocol. - Dovecot logs the session as TLS: ``` imap-login: Login: user=, method=PLAIN, rip=203.0.113.5, lip=192.0.2.10, mpid=12345, TLS, session= ``` **Advantages:** - End-to-end encryption (client → HAProxy → Dovecot) - Centralized TLS policy at HAProxy - Certificate validation between HAProxy and Dovecot **Disadvantages:** - Two TLS handshakes per session (client→HAProxy and HAProxy→Dovecot); on a loopback interface this CPU cost buys no extra security (see note above). ##### Mutual TLS (mTLS) Between HAProxy and Dovecot For additional security, HAProxy and Dovecot can authenticate each other using client certificates. This ensures only trusted proxies can connect to Dovecot's backend port. WARNING `[ssl_server_request_client_cert = yes](/main/core/summaries/settings.html#ssl_server_request_client_cert)` requests a client certificate from **every** TCP connection on the listener. With HAProxy in front of Dovecot, only HAProxy connects to this port, so this is safe. Do not copy this listener block into a configuration exposed directly to end-user clients — those clients will be asked for a certificate they do not have. haproxy\_mtlsdovecot\_mtls haproxy ``` # Line continuations with `\` require HAProxy 2.4+. # On older versions, place all options on one line. backend dovecot_imap_tls mode tcp server dovecot1 10.0.0.1:993 ssl verify required \ ca-file /etc/haproxy/ca.pem \ crt /etc/haproxy/client.pem \ send-proxy-v2-ssl # Use send-proxy-v2-ssl-cn instead to also forward the client certificate CN ``` doveconf ``` ssl = required ssl_server_cert_file = /etc/dovecot/server.pem ssl_server_key_file = /etc/dovecot/server.key ssl_client_ca_file = /etc/dovecot/ca.pem ssl_server_request_client_cert = yes haproxy_trusted_networks = 10.0.0.0/24 service imap-login { inet_listener imaps { port = 993 haproxy = yes ssl = yes } } ``` HAProxy presents a client certificate to Dovecot; Dovecot verifies it before accepting the connection. #### HAProxy → Dovecot Without TLS HAProxy may forward plaintext connections to Dovecot after terminating TLS from the client. haproxy\_plain\_backenddovecot\_plain\_listener haproxy ``` frontend imap_tls bind *:993 ssl crt /etc/haproxy/certs/example.pem mode tcp default_backend dovecot_imap backend dovecot_imap mode tcp server dovecot1 10.0.0.1:143 send-proxy-v2 ``` doveconf ``` ssl = yes haproxy_trusted_networks = 10.0.0.0/24 service imap-login { inet_listener imaps { port = 0 } inet_listener imap { port = 143 haproxy = yes } } ``` **Drawbacks:** - Connection between HAProxy and Dovecot is unencrypted. - Credentials and session data are exposed on the internal network. - Only acceptable on strictly controlled local systems where the HAProxy–Dovecot path is trusted. ### Plaintext Client Connections (Port 143) If a client connects on a plaintext port, HAProxy passes the connection through without TLS termination. Recommended Do not expose a plaintext port at all when HAProxy is terminating TLS on the TLS port. The configurations below apply only when a plaintext listener is required. The [No STARTTLS](#no-starttls) and [Dovecot Provides STARTTLS](#dovecot-provides-starttls) subsections below differ in whether Dovecot loads a TLS context. The HAProxy side is independent: each layout can be combined with the HAProxy-terminates-TLS frontend from the previous sections. #### No STARTTLS - Dovecot loads no TLS context (`[ssl = no](/main/core/summaries/settings.html#ssl)`). - The plaintext listener cannot advertise STARTTLS. - Suitable when HAProxy terminates TLS on 993 and the 143 path is internal only; avoids splitting TLS configuration between HAProxy and Dovecot. doveconf ``` ssl = no haproxy_trusted_networks = 10.0.0.0/24 service imap-login { inet_listener imap { port = 143 haproxy = yes } } ``` #### Dovecot Provides STARTTLS - Dovecot is configured with a TLS context. - STARTTLS is advertised on the plaintext listener. WARNING This configuration is functional but discouraged. TLS configuration is split between HAProxy and Dovecot, so certificates and policies (like accepted ciphers) may diverge. doveconf ``` ssl = yes ssl_server_cert_file = /etc/dovecot/server.pem ssl_server_key_file = /etc/dovecot/server.key haproxy_trusted_networks = 10.0.0.0/24 service imap-login { inet_listener imap { port = 143 haproxy = yes } } ``` See [SSL configuration](/main/core/config/ssl.html#configuration-overview) for STARTTLS configuration details. ### Alternative: TCP Passthrough to allow Dovecot to handle TLS If HAProxy should not terminate TLS, it can pass the raw TCP stream directly to Dovecot. Dovecot handles TLS entirely; HAProxy cannot inspect traffic, perform SNI-based routing, or do TLS offloading. The PROXYv2 header is the only way for Dovecot to learn the original client IP. Add `send-proxy-v2` on the `server` line — without it the connection appears to come from HAProxy's source address, and Dovecot loses client-IP visibility entirely. haproxy\_passthroughdovecot\_passthrough haproxy ``` frontend imap_tls bind *:993 mode tcp default_backend dovecot_imap_tls backend dovecot_imap_tls mode tcp server dovecot1 10.0.0.1:993 send-proxy-v2 ``` doveconf ``` ssl = required ssl_server_cert_file = /etc/dovecot/server.pem ssl_server_key_file = /etc/dovecot/server.key haproxy_trusted_networks = 10.0.0.0/24 service imap-login { inet_listener imaps { port = 993 haproxy = yes ssl = yes } } ``` ## Operational Notes ### Timeouts and IMAP IDLE HAProxy `timeout client` / `timeout server` defaults (often 1 minute) are too short for IMAP. Long-lived IDLE connections will be torn down by HAProxy long before Dovecot's `[imap_idle_notify_interval](/main/core/summaries/settings.html#imap_idle_notify_interval)` fires, which is the most common HAProxy misconfiguration for mail deployments. Set HAProxy timeouts larger than Dovecot's idle notify interval (default 2 minutes), e.g.: haproxy ``` defaults mode tcp timeout client 31m timeout server 31m timeout connect 5s ``` ### Health Checks The default HAProxy `check` directive performs a plain TCP connect, which is fine for plaintext backends but fails immediately against an `imaps` backend that expects a TLS ClientHello. For a TLS backend, use `option ssl-hello-chk`: haproxy ``` backend dovecot_imap_tls mode tcp option ssl-hello-chk server dovecot1 10.0.0.1:993 check ssl verify required \ ca-file /etc/haproxy/ca.pem send-proxy-v2-ssl ``` For a plaintext backend, a protocol-aware `tcp-check` matches the IMAP greeting: haproxy ``` backend dovecot_imap mode tcp option tcp-check tcp-check expect string * OK server dovecot1 10.0.0.1:143 check send-proxy-v2 ``` See also: [secured connections](/main/core/config/ssl.html#secured-connections), `[haproxy_trusted_networks](/main/core/summaries/settings.html#haproxy_trusted_networks)`. # Dovecot Proxying Dovecot supports proxying IMAP, POP3, [submission server](/main/core/config/submission.html), [LMTP Server](/main/core/config/delivery/lmtp.html), [ManageSieve](/main/core/config/sieve/managesieve.html) and doveadm connections to other hosts. The proxying can be done for all users, or only for some specific users. There are two ways to do the authentication on the remote server: 1. Forward the user-given password (or OAUTH token) to the remote server. This is done by returning `pass=%{password}` and `proxy_mech=%{mechanism}` extra fields. - This doesn't work if any non-cleartext, non-token-based [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) are used, because they prevent such password forwarding by design. - `proxy_mech` is needed only if both OAUTH and cleartext mechanisms are enabled. 2. Login to the remote server using a [master password](/main/core/config/auth/master_users.html#master-passwords). This is done by returning `pass=master_secret` extra field. This allows client to use also non-cleartext authentication. ## Configuration Proxying is enabled by the `proxy` or `proxy_maybe` extra field. Additionally, `host` is also a required field. See below for details on them and other optional extra fields. In backends, set `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` to point to the proxies' IP addresses. This way you'll get the clients' actual IP addresses logged instead of the proxy's. The destination servers don't need to be running Dovecot, but you should make sure that the Dovecot proxy doesn't advertise more capabilities than the destination server can handle. For IMAP you can do this by changing `[imap_capability](/main/core/summaries/settings.html#imap_capability)`. For POP3 you'll have to modify Dovecot's sources for now (`src/pop3/capability.h`). Dovecot IMAP proxy also automatically sends updated untagged CAPABILITY reply if it detects that the remote server has different capabilities than what it already advertised to the client, but some clients simply ignore the updated CAPABILITY reply. ## Fields ### `proxy` Enables the proxying. Either this or `proxy_maybe` is required to enable proxying. ### `proxy_maybe` Enables optional proxying. Either this or `proxy` is required to enable proxying. `proxy_maybe` can be used to implement "automatic proxying" to implement a mixed mode of running proxies and backends in the same servers. If the proxy destination matches the current connection, the user gets logged in normally instead of being proxied. If the same happens with `proxy`, the login fails with `Proxying loops` error. `[auth_proxy_self](/main/core/summaries/settings.html#auth_proxy_self)` can be used to specify extra IPs that are also considered to be the proxy's own IPs. ### `host=` The destination server's IP address. This field is required. ### `source_ip=` The source IP address to use for outgoing connections. ### `port=` The destination server's port. The default is `143` with IMAP and `110` with POP3. ### `protocol=` The protocol to use for the connection to the destination server. This field is currently only relevant for LMTP: it can be used to select either `lmtp` or `smtp`. ### `destuser=s` Tell client to use a different username when logging in. ### `proxy_mech=` Tell client to use this SASL authentication mechanism when logging in. See `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` for supported mechanisms ### `proxy_timeout=` Abort connection after this much time has passed. This overrides the default `[login_proxy_timeout](/main/core/summaries/settings.html#login_proxy_timeout)`. This setting applies only to proxying via login processes, not to lmtp or doveadm processes. ### `proxy_no_multiplex` Added: 2.4.0 IMAP only: Do not enable multiplexing for the backend connection. By default the proxy requests a multiplexed connection from the backend by sending `x-multiplex` in the pre-login `ID` command. The multiplexed stream adds a side channel that the backend uses to hand off work to the proxy. Currently this is used only for handling the IMAP `COMPRESS` extension in the proxy, see `[imap_compress_on_proxy](/main/core/summaries/settings.html#imap_compress_on_proxy)`. WARNING This field should normally not be set: the multiplexed connection is the intended default between a Dovecot proxy and a backend. When the field is set, the proxy-backend connection is a plain stream and the side channel functionality is disabled: - `[imap_compress_on_proxy](/main/core/summaries/settings.html#imap_compress_on_proxy)` has no effect for the connection. The `COMPRESS` extension keeps working, but compression is handled by the backend imap process instead of the proxy, increasing CPU usage on the backends. This is a passdb extra field [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) only; there is no equivalent global setting. To disable multiplexing for all connections, return the field unconditionally from the proxy's passdb, e.g.: ``` passdb static { fields { proxy = yes host = 10.0.0.1 proxy_no_multiplex = yes nopassword = yes } } ``` ### `proxy_nopipelining` Don't pipeline IMAP commands. This is a workaround for broken IMAP servers that hang otherwise. ### `proxy_not_trusted` IMAP/POP3 proxying never sends the `ID/XCLIENT` command to remote. This can be used when proxying to remote servers that shouldn't see the clients' original IP addresses or other information. ## SSL You can use SSL/TLS connection to destination server by returning: - `ssl=yes`: Use SSL and require a valid verified remote certificate. - `ssl=any-cert`: Use SSL, but don't require a valid remote certificate. - `starttls=yes`: Use STARTTLS command instead of doing SSL handshake immediately after connected. - `starttls=any-cert`: Combine `starttls` and `ssl=any-cert`. INFO Login processes are chrooted, so `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting doesn't work. You can instead use `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)`. Additionally you can also tell Dovecot to send SSL client certificate to the remote server using `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` and `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` settings. ## Source IPs If your proxy handles a lot of connections `(~64k)` to the same destination IP, you may run out of TCP ports. The only way to work around this is to use either multiple destination IPs or ports, or multiple source IPs. Multiple source IPs can be easily used by adding them to `[login_source_ips](/main/core/summaries/settings.html#login_source_ips)`. You can also use hostnames which expand to multiple IPs. It's probably better not to include the server's default outgoing IP address in the setting, as explained here: [https://idea.popcount.org/2014-04-03-bind-before-connect/](https://idea.popcount.org/2014-04-03-bind-before-connect/). ## Disconnection Delay To avoid reconnection load spikes when a backend server dies, you can tell proxy to spread the client disconnections over a longer time period (after the server side of the connection is already disconnected). `[login_proxy_max_disconnect_delay](/main/core/summaries/settings.html#login_proxy_max_disconnect_delay)` controls this. ## Forwarding Fields You can forward arbitrary variables by returning them prefixed with `forward_`. Dovecot will use a protocol-dependent extension to forward these variables to the next hop. The next hop imports these to the auth request as passdb extra fields, so they are visible in, e.g., `%{passdb:forward_variable}`. If the proxying continues, all these fields are further forwarded to the next hop again. This feature requires that the sending host is in the destination's `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)`. See [forwarding parameters in proxying](/main/core/config/proxy/forwarding.html) for more details on how this is implemented for different protocols, which includes limits to the key and value lengths and counts. INFO Most importantly the IMAP ID command restricts the forward key length to just 20 bytes (excluding `forward_` prefix). Larger keys are silently dropped. ## Moving Users Between Backends/Clusters A safe way to move users from one cluster to another is to do it like: - Set `delay_until=` [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) where `` is the current timestamp plus some seconds into future (e.g. 31s). You may also want to append, e.g., +5 for some load balancing if a lot of users are moved at once. - Set `host=` [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). This update should be atomic together with the `delay_until` field. - Use `[doveadm kick](/main/core/summaries/doveadm.html#kick)` to kick the user's existing connections. - The processes may still continue running in the backend for a longer time. If you want to be absolutely sure, you could also run a script to `kill -9` all processes for the user in the backend. This of course has its own problems. The idea here is that while the user's connections are being kicked and the backend processes are finishing up and shutting down, new connections are being delayed in the proxy. This delay should be long enough that the user's existing processes are expected to die, but not so large that clients get connection timeouts. A bit over 30 seconds is likely a good value. Once the `delay_until` timestamp is reached, the connections continue to the new host. If you have a lot of users, it helps to group some of them together and do the `host/delay_until` updates on a per-group basis rather than per-user basis. ## ID Command Forwarding If you want to forward, for some reason, the IMAP ID command provided by the client, set `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. This will also enable `client_id` variable in variable expansions for auth requests, which will contain the ID command as IMAP arglist. ## Password Forwarding If you don't want proxy itself to do authentication, you can configure it to succeed with any given password. You can do this by returning an empty password and `nopassword` field. ## Master Users Note that this is different from master passwords. This way of forwarding requires the destination server to support master user feature. The users will be normally authenticated in the proxy and the common proxy fields are returned, but you'll need to return two fields specially: - `master=`: This contains the master username (e.g. `proxy`). It's used as SASL authentication ID. - Alternatively you could return `destuser=user*master` and set `[auth_master_user_separator = *](/main/core/summaries/settings.html#auth_master_user_separator)`. - `pass=`: This field contains the master user's password. See [Master Users](/main/core/config/auth/master_users.html) for more information how to configure this. ## Examples ### Password Forwarding with Static DB See [static authentication database](/main/core/config/auth/databases/static.html). ### Password Forwarding with SQL Create the SQL table: sql ``` CREATE TABLE proxy ( user varchar(255) NOT NULL, host varchar(16) default NULL, destuser varchar(255) NOT NULL default '', PRIMARY KEY (user) ); ``` Insert data to SQL corresponding your users. Working data could look like this: | user | host | destuser | | --- | --- | --- | | john | 192.168.0.1 | | | joe | 192.168.0.2 | [joe@example.com](mailto:joe@example.com) | dovecot.confSQL Tabledovecot.conf doveconf ``` # If you want to trade a bit of security for higher performance, change # these settings: service imap-login { restart_request_count = unlimited } service pop3-login { restart_request_count = unlimited } # If you are not moving mailboxes between hosts on a daily basis you can # use authentication cache pretty safely. auth_cache_size = 4096 auth_mechanisms = plain sql_driver = mysql mysql sqlhost1 { } mysql sqlhost2 { } mysql_dbname = mail mysql_user = dovecot mysql_password = secret passdb sql { query = SELECT NULL AS password, 'Y' as nopassword, host, destuser, 'Y' AS proxy \ FROM proxy \ WHERE user = '%{user}' } ``` ### `proxy_maybe` with SQL SQL Tabledovecot.conf sql ``` CREATE TABLE users ( user varchar(255) NOT NULL, domain varchar(255) NOT NULL, password varchar(100) NOT NULL, host varchar(16) NOT NULL, home varchar(100) NOT NULL, PRIMARY KEY (user) ); ``` doveconf ``` # user/group who owns the message files: mail_uid = vmail mail_gid = vmail auth_mechanisms = plain sql_driver = mysql mysql localhost { } passdb sql { query = SELECT concat(user, '@', domain) AS user, password, host, 'Y' AS proxy_maybe \ FROM users \ WHERE user = '%{user | username}' AND domain = '%{user | domain}' } userdb sql { query = SELECT user AS username, domain, home \ FROM users \ WHERE user = '%{user | username}' AND domain = '%{user | domain}' } ``` # Login Referrals Login referrals are an IMAP extension specified by [RFC 2221](https://datatracker.ietf.org/doc/html/rfc2221). It is practically not used by anyone, so this page exists mainly for historical reasons. Their purpose is to redirect clients to an different IMAP server in case of hardware failures or organizational changes. No client action is needed to invoke the LOGIN-REFERRALS capability: the redirection is triggered by the server and occurs transparently. DANGER As stated by [RFC 2221](https://datatracker.ietf.org/doc/html/rfc2221), a man in the middle attack may use a rogue 'password catching' server to collect login data and redirect your clients to their own rogue IMAP server. Although this would be avoided by enforcing SSL/TLS. Login referrals are not supported by many clients, so you probably don't want to use them anyway. Dovecot does NOT use login referrals by default. ## Configuration INFO The `host` field is also used by proxying. Login referrals are used only if the `proxy` field isn't set. Login referrals can be used in two ways: 1. Tell the client to log into another server without allowing to log in locally. 2. Suggest the client to log into another server, but log it in anyway. The following fields can be used to configure login referrals: | Field | Description | | --- | --- | | `host=` | The destination server's hostname. This field is required for login referrals to be used. | | `port=` | The destination server's port. The default is `143`. | | `destuser=` | Tell client to use a different username when logging in. | | `reason=` | Optional reason to use as the reply to the login command. The default is "Logged in, but you should use this server instead." | Using the above settings, you can suggest client to log in elsewhere. To require it, you'll also have to return: - `nologin`: User is not allowed to log in. - `reason=`: Optional reason. The default is "Try this server instead." ## Client Support The following clients are known to support login referrals: - Pine - Outlook (but not Outlook Express) ## Examples Forward user to another server after successful authentication (SQL): doveconf ``` passdb sql { query = SELECT password, host, 'Y' as nologin \ FROM users \ WHERE userid = '%{user}' } ``` Forward all users to another server without authentication: doveconf ``` passdb sql { query = SELECT NULL AS password, 'Y' AS nopassword 'imap2.example.com' AS host, 'This server is down, try another one.' AS reason, 'Y' AS nologin, 'Y' AS nodelay } ``` # Quick Configuration If you just want to get Dovecot running with typical configuration in a typical environment, this guide will help you. ## TLDR; I Just Want Dovecot Running Here is a very simple basic configuration with single vmail user to be placed in `dovecot.conf`. TIP Some distros split configuration under `/etc/dovecot/conf.d/`. This can be useful, but it is not required. For a simple setup, a single `dovecot.conf` file is recommended. You need to create group `vmail` and user `vmail`. dovecot.conf doveconf ``` # Start new configs with the latest Dovecot version numbers here: dovecot_config_version = 2.4.0 dovecot_storage_version = 2.4.0 # Enable wanted protocols: protocols { imap = yes lmtp = yes } mail_home = /srv/mail/%{user} mail_driver = sdbox mail_path = ~/mail mail_uid = vmail mail_gid = vmail # By default first_valid_uid is 500. If your vmail user's UID is smaller, # you need to modify this: #first_valid_uid = uid-number-of-vmail-user namespace inbox { inbox = yes separator = / } # Authenticate as system users: passdb pam { } ssl_server_cert_file = /etc/dovecot/ssl-cert.pem ssl_server_key_file = /etc/dovecot/ssl-key.pem ``` ## Configuration File Prebuilt packages usually install the configuration files into `/etc/dovecot/`. You'll find the correct path by running: sh ``` doveconf -n | head -n 1 ``` It's a good idea to read through all the config files and see what settings you might want to change. ### Installing From Sources If you compiled and installed Dovecot from sources, Dovecot has installed an example configuration file in `/usr/local/etc/dovecot/dovecot.conf` ### Split Configuration Files The default configuration starts from `dovecot.conf`, which contains an `!include conf.d/*.conf` statement to read the rest of the configuration. You can group settings into these included config files, or you can place everything into the `dovecot.conf`, whichever you prefer. Usually it does not matter in which file you write the setting. You only need to be aware that later settings replace earlier ones. If you use the same section multiple times, the settings are merged together. ## Authentication The above example configures Dovecot to use PAM for system user authentication. See [PAM authentication database](/main/core/config/auth/databases/pam.html) for how to configure it. A typical configuration with Linux would be to create `/etc/pam.d/dovecot` which contains: ``` auth required pam_unix.so account required pam_unix.so ``` ### Virtual Users If you're planning on using virtual users, it's easier to first create a simple passwd-like file to make sure that the authentication will work. Later when you know Dovecot is working, you can do it differently (see [virtual users](/main/core/config/auth/users/virtual.html)). Run as your own non-root user: sh ``` echo "$USER:{PLAIN}password" > passwd sudo mv passwd /etc/dovecot/ # If SELinux is enabled: restorecon -v /etc/dovecot/users ``` You can (and should) replace the "password" with whatever password you wish to use, but don't use any important password here as we'll be logging in with insecure plaintext authentication until [SSL](/main/core/config/ssl.html) is configured. Switch to passwd-file authentication by replacing `passdb pam` in `dovecot.conf` with `passdb passwd-file`: doveconf ``` passdb passwd-file { passwd_file_path = /etc/dovecot/passwd } ``` Verify with `doveconf -n passdb` that the output looks like above (and there are no other passdbs and no userdbs). If you're using something else, see [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html). ## Mail Location You can let Dovecot do its automatic mail location detection, but if that doesn't work you can set the location manually. See [Mailbox Root Autodetection](/main/core/config/mail_location.html#mailbox-root-autodetection). TIP It is recommended to use either [Maildir](/main/core/config/mailbox_formats/maildir.html) or [dbox](/main/core/config/mailbox_formats/dbox.html) as your mailbox format. [mbox](/main/core/config/mailbox_formats/mbox.html) is a deprecated format, and should only be used for legacy message access and not for new systems. ### Maildir For better performance you may want to set `[maildir_very_dirty_syncs = yes](/main/core/summaries/settings.html#maildir_very_dirty_syncs)`. ## Client Workarounds Check `[imap_client_workarounds](/main/core/summaries/settings.html#imap_client_workarounds)` and `[pop3_client_workarounds](/main/core/summaries/settings.html#pop3_client_workarounds)` and see if you want to enable more of them than the defaults. ## SSL and Plaintext Authentication Configure SSL certificate and private key paths with `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)` and `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` settings. An easy way to build a self-signed test certificate is using Dovecot's `doc/mkcert.sh` script. For more information see [How To SSL Self-signed Certificate Creation](/main/howto/ssl/cert_create.html#self-signed-certificate-creation) and [SSL configuration](/main/core/config/ssl.html#configuration-overview). By default `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)`, which means that Dovecot will fail the authentication if the client doesn't use SSL (or use non-cleartext authentication mechanisms). This is recommended in most situations, since it prevents leaking passwords. However, if you don't offer SSL for some reason, you'll probably want to set `[auth_allow_cleartext = yes](/main/core/summaries/settings.html#auth_allow_cleartext)`. ## NFS If you're using NFS or some other remote filesystem that's shared between multiple computers, you should read [NFS](/main/core/config/nfs.html). ## Running See [running Dovecot](/main/core/admin/running.html) and [logging](/main/core/admin/logging.html). # Recommended Metrics This page lists recommended metrics that allow inspecting dovecot's behavior in the most general situations. More specialized situations might require further refinements or additional statistics. For an overview of how to gather statistics see [Statistics](/main/core/config/statistics.html). A list of all available events and their fields can be found [here](/main/core/summaries/events.html#all-dovecot-events). The following examples use the custom `log-export` exporter. doveconf ``` event_exporter log-export { format = json format_args = time-rfc3339 transport = log } ``` ## Dovecot Proxy `@metric_defaults = proxy` contains: `auth_successes` Number of successful authentications. See `[auth_request_finished](/main/core/summaries/events.html#auth_request_finished)`. `auth_failures` Number of unsuccessful authentications. See `[auth_request_finished](/main/core/summaries/events.html#auth_request_finished)`. It may be useful to export these events into log: doveconf ``` metric auth_failures { exporter = log-export } ``` `login_aborted` Number of aborted logins, grouped by reason. See `[login_aborted](/main/core/summaries/events.html#login_aborted)`. `proxy_session_established` Added: 2.4.2 Number of successful proxy sessions. This can be used to see post-authentication latency until session is logged into a backend. See `[proxy_session_established](/main/core/summaries/events.html#proxy_session_established)`. ## Dovecot Backend ### Generic authentication metrics `@metric_defaults = backend` contains: `auth_successes` Number of successful authentications. See `[auth_request_finished](/main/core/summaries/events.html#auth_request_finished)`. `auth_failures` Number of unsuccessful authentications. See `[auth_request_finished](/main/core/summaries/events.html#auth_request_finished)`. These are not usually expected to happen in backends. It may be useful to export these events into log: doveconf ``` metric auth_failures { exporter = log-export } ``` ### Basic mail access and delivery metrics `@metric_defaults = backend` contains: `imap_commands` Number of IMAP commands, grouped by OK/NO/BAD tagged reply. See `[imap_command_finished](/main/core/summaries/events.html#imap_command_finished)`. `mail_deliveries` Number of mails delivered. See `[mail_delivery_finished](/main/core/summaries/events.html#mail_delivery_finished)`. `mail_submissions` Number of mails submitted for outside delivery (e.g. rejects, vacations). See `[smtp_submit_finished](/main/core/summaries/events.html#smtp_submit_finished)`. `mail_user_session_finished` Number of mail sessions, including their RSS memory usage and user space CPU usage at the time when the session was finished. See `[mail_user_session_finished](/main/core/summaries/events.html#mail_user_session_finished)`. # Rootless Installation It's possible to make Dovecot run under a single system user without requiring root privileges at any point. This shouldn't be thought of as a security feature, but instead simply as a way for non-admins to run Dovecot in their favorite mail server. It's also useful if you just wish to test Dovecot without messing up your system. If you think of this as a good way to achieve security, ask yourself which is worse: - A very small possibility to get root privileges through Dovecot. - A small possibility without logging in, to get into system as a non-privileged **dovecot** user, chrooted into an empty directory. - A small possibility to get user's privileges after logging in, but no possibility to read others' mails since they're saved with different UIDs (plus you might also be chrooted to your own mailbox). **or** - Absolutely zero possibility to get root privileges through Dovecot. - A small possibility to get into system as a mail user, possibly even without logging in, and being able to read everyone's mail (and finally gaining roots by exploiting some just discovered local vulnerability, unless you bothered to set up a special chrooted environment). ## Installation Install somewhere under home directory: sh ``` ./configure --prefix=$HOME/dovecot make make install ``` Dovecot is then started by running `~/dovecot/sbin/dovecot`. An example configuration is automatically installed in `~/dovecot/etc/dovecot/dovecot.conf`. ### Add Capabilities Modern linux systems support capabilities which allows you to permit selective rights to processes. This allows you to run dovecot rootless without losing chroot and privileged ports. Use following commands to enable this: sh ``` setcap cap_net_bind_service+ep ~/dovecot/sbin/dovecot setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/script-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/imap-urlauth-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/submission-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/managesieve-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/pop3-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/imap-login setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/lmtp setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/anvil # if you have installed managesieve setcap cap_sys_chroot+ep ~/dovecot/libexec/dovecot/managesieve-login ``` ## Configuration The important settings to change for rootless installation are: - Set usernames and group to the user which dovecot will be run under: doveconf ``` default_internal_user = user default_login_user = user default_internal_group = group ``` - Remove default chrooting from all services, this is optional if you want to use Linux capabilities instead. doveconf ``` service anvil { chroot = } service imap-login { chroot = } service pop3-login { chroot = } ``` - Change listener ports, this is optional if you want to use Linux capabilities instead: doveconf ``` service imap-login { inet_listener imap { port = 10143 } inet_listener imaps { port = 10993 } } service pop3-login { inet_listener pop3 { port = 10110 } inet_listener pop3s { port = 10995 } } ``` - Change logging destination: doveconf ``` log_path = /home/user/dovecot.log ``` - Instead of [PAM authentication database](/main/core/config/auth/databases/pam.html), use, for example, [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html): doveconf ``` passdb passwd-file { passwd_file_path = /home/user/dovecot/etc/passwd } userdb passwd { } ``` Where the `passwd` file contains the username and password for your login user: ``` user:{PLAIN}pass ``` # Security Dovecot has been designed with security in mind. It uses multiple processes and privilege separation to isolate different parts from each others in case a security hole is found from one part. Additional things you can configure: - Allocate each user their own UID and GID (see [System Users](/main/core/config/auth/users/system.html)) - Use a separate `dovecot-auth` user for authentication process (see [System Users](/main/core/config/auth/users/system.html)) - You can chroot authentication and mail processes (see [chrooting](/main/core/config/chrooting.html)) - There are some security related SSL settings (see [SSL configuration](/main/core/config/ssl.html#configuration-overview)) - Set `first/last_valid_uid/gid` settings to contain only the range actually used by mail processes # Service Configuration This page describes Dovecot's services comprehensively. Most admins don't need to know these details. ## Settings ### `fifo_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[fifo_listener_path](/main/core/summaries/settings.html#fifo_listener_path)` Creates a new FIFO listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[fifo_listener_path](/main/core/summaries/settings.html#fifo_listener_path)` setting. ### `fifo_listener_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Group of the listener file. Empty (default) means GID 0 (root/wheel). ### `fifo_listener_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode of the file. Note that `0600` is an octal value, while `600` is a different decimal value. Setting mode to `0` disables the listener. ### `fifo_listener_path` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` - `[fifo_listener](/main/core/summaries/settings.html#fifo_listener)` Path to the FIFO, relative to `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. The `[fifo_listener](/main/core/summaries/settings.html#fifo_listener)` filter name refers to this setting. ### `fifo_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `fifo_listener_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Owner of the listener file. Empty (default) means UID 0 (root). ### `inet_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[inet_listener_name](/main/core/summaries/settings.html#inet_listener_name)` Creates a new network socket listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[inet_listener_name](/main/core/summaries/settings.html#inet_listener_name)` setting. ### `inet_listener_haproxy` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, this listener is configured for use with HAProxy. It expects a Proxy Protocol header right after accepting the connection. Connections are aborted immediately if this protocol is violated. ### `inet_listener_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[inet_listener_type](/main/core/summaries/settings.html#inet_listener_type)` - `[inet_listener](/main/core/summaries/settings.html#inet_listener)` Name of this listener. It is meant to be descriptive for humans (e.g. `imap`, `imaps`), but it is also used by services to determine the socket type, unless `[inet_listener_type](/main/core/summaries/settings.html#inet_listener_type)` overrides it. The `[inet_listener](/main/core/summaries/settings.html#inet_listener)` filter name refers to this setting. ### `inet_listener_port` **Default:** _\[None\]_ **Value:** [Port Number](/main/core/settings/types.html#port-number) **See Also:** - `[listen](/main/core/summaries/settings.html#listen)` Port number where to listen. `0` disables the listener. ### `inet_listener_ssl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` If `yes`, the listener does an immediate SSL/TLS handshake after accepting a connection. This is needed for e.g. the `imaps` and `pop3s` ports. INFO All listeners with `ssl=yes` will be removed if the global `[ssl](/main/core/summaries/settings.html#ssl)` setting is `no`. Regardless of the value for listener's ssl setting, some services will still try to initialize encryption if the global `[ssl](/main/core/summaries/settings.html#ssl)` is `yes`. This is for example done to accommodate STARTTLS commands for IMAP/SUBMISSION/LMTP protocols. In other words, SSL is truly disabled only when the global `[ssl](/main/core/summaries/settings.html#ssl)` is `no`. Note: Do not confuse this with the `[ssl](/main/core/summaries/settings.html#ssl)` setting. When used as `inet_listener { ssl }`, it expands to this `inet_listener_ssl` setting. ### `inet_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `service` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service_name](/main/core/summaries/settings.html#service_name)` Creates a new service to the list of services. The filter name refers to the `[service_name](/main/core/summaries/settings.html#service_name)` setting. ### `service_chroot` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` The processes are chrooted to this directory at startup. Relative to `[base_dir](/main/core/summaries/settings.html#base_dir)`. ### `service_client_limit` **Default:** `[default_client_limit](/main/core/summaries/settings.html#default_client_limit)` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of simultaneous client connections per process. Once this number of connections is received, the next incoming connection will prompt Dovecot to spawn another process. ### `service_drop_priv_before_exec` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Drop all privileges after forking, but before executing the binary. This is mainly useful for dumping core files on non-Linux OSes, since the processes are no longer in "setuid" mode. This setting can't be used with non-empty chroot. ### `service_executable` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` The binary path to execute and its parameters. If the path doesn't begin with `/`, it's relative to `[base_dir](/main/core/summaries/settings.html#base_dir)`. ### `service_extra_groups` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` Secondary UNIX groups that this process belongs to. WARNING This may be dangerous to use for the same reason as `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)`. ### `service_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The primary UNIX group (GID) which runs this process. ### `service_idle_kill_interval` **Default:** `[default_idle_kill_interval](/main/core/summaries/settings.html#default_idle_kill_interval)` **Value:** [time](/main/core/settings/types.html#time) Time interval between killing extra idling processes. During the interval the master process tracks the lowest number of idling processes for the service. Afterwards it sends `SIGINT` notification to that many idling processes. If the processes are still idling when receiving the signal, they shut down themselves. Using `infinite` disables the idle-killing. ### `service_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[service](/main/core/summaries/settings.html#service)` Name of the service. The `[service](/main/core/summaries/settings.html#service)` filter name refers to this setting. ### `service_privileged_group` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | See Also | - `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` | | Advanced Setting; this should not normally be changed. | Secondary UNIX group - which is disabled by default - but can be enabled by the process. `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` setting is a more user friendly way to use this setting for mail processes. ### `service_process_limit` **Default:** `[default_process_limit](/main/core/summaries/settings.html#default_process_limit)` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of processes that may exist for this service. Changed: 2.4.5 However, if `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` > 1, when the process reaches `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)`, the process is no longer counted towards its process limit. Otherwise long-lived connections in the old process could prevent creation of new processes. ### `service_process_min_avail` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` - `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` Minimum number of processes that always should be available to accept more client connections. Note that if `[service_client_limit = 1](/main/core/summaries/settings.html#service_client_limit)`, this means there are always that many processes that are not doing anything. When a new process launches, one of the idling processes will accept the connection and a new idling process is launched. - For `[service_restart_request_count = 1](/main/core/summaries/settings.html#service_restart_request_count)` processes this decreases the latency for handling new connections, because there's no need to wait for processes to fork. This is usually not necessary to to be set. Large `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` values might be useful in some special cases, like if there are a lot of POP3 users logging in exactly at the same time to check mails. - For `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` to a value `!=1` and `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` to a value `>1` processes it could be set to the number of CPU cores (you can use `%{system:cpu_count}`) on the system to balance the load among them. This is commonly used with `*-login` processes. - For `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` with a value of `!=1` and `[service_client_limit = 1](/main/core/summaries/settings.html#service_client_limit)` processes it is likely not useful to use this, because generally there are already some idling processes waiting to accept new connections. However, it's not harmful either, since `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` includes the existing idling processes when counting how many new idling processes are needed. ### `service_protocol` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[protocols](/main/core/summaries/settings.html#protocols)` If non-empty, this service is enabled only when the protocol name is listed in `[protocols](/main/core/summaries/settings.html#protocols)` setting. ### `service_restart_request_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Number of client connections to handle until the process kills itself. Use `unlimited` to keep the process alive. `1` means only a single connection is handled until the process is stopped - this is the most secure choice since there's no way for one connection's state to leak to the next one. For better performance this can be set higher, but ideally not `unlimited` since more complex services can have small memory leaks and/or memory fragmentation and the process should get restarted eventually. For example `100` or `1000` can be good values. ### `service_reuse_port` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.4 Distribute TCP connections to processes more evenly using the [SO\_REUSEPORT](https://lwn.net/Articles/542629/) option on Linux. When enabled, `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` must be the same as `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`. This setting is mainly intended to be enabled for login processes. When `no`, all processes listening on the socket try to accept it at the same time. Whichever process is fastest (gets scheduled first by the kernel) gets the connection. This can lead to rather uneven distribution of connections. On the positive side, this behaves more gracefully once the process reaches `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`: it simply doesn't accept new connections, allowing other processes to handle them instead. If all processes are full, the master process rejects new connections. When `yes`, a separate listener socker is created for each process at startup. This is why `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` must be the same as `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`. Each process gets one of these listener sockets and uses it to accept connections. The kernel assigns incoming connections to these listener sockets based on the connection hash, so the connections should be rather evenly distributed across processes. This also means that if any process has reached `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`, it must start rejecting new connections, even if other processes aren't full. However, because of the more even distribution of connections, it's expected that when one process is full, other processes are nearly full as well. ### `service_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` Type of this service: | Value | Description | | --- | --- | | `` | The default. | | `login` | Used by login services. The login processes have "all processes full" notification fd. It's used by the processes to figure out when no more client connections can be accepted because client and process limits have been reached. The login processes can then kill some of their oldest connections that haven't logged in yet. | | `worker` | Used by various worker services. It's normal for worker processes to fill up to `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`, and there shouldn't be a warning logged about it. | | `startup` | Creates one process at startup. | | `log` `config` `anvil` | Treated specially by these specific services. | ### `service_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[default_login_user](/main/core/summaries/settings.html#default_login_user)` - `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` UNIX user (UID) which runs this process. `[default_login_user](/main/core/summaries/settings.html#default_login_user)` should be used for `type=login` processes and `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` should be used for other processes that don't require root privileges. ### `service_vsz_limit` **Default:** `[default_vsz_limit](/main/core/summaries/settings.html#default_vsz_limit)` **Value:** [size](/main/core/settings/types.html#size) Limit the process's address space (both `RLIMIT_DATA` and `RLIMIT_AS` if available). When the space is reached, some memory allocations may start failing with "Out of memory", or the kernel may kill the process with signal 9. This setting is mainly intended to prevent memory leaks from eating up all of the memory, but there can be also legitimate reasons why the process reaches this limit. For example a huge mailbox may not be accessed if this limit is too low. Use `unlimited` to disable this entirely. ### `unix_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[unix_listener_path](/main/core/summaries/settings.html#unix_listener_path)` Creates a new UNIX listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[unix_listener_path](/main/core/summaries/settings.html#unix_listener_path)` setting. ### `unix_listener_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Group of the listener file. Empty (default) means GID 0 (root/wheel). ### `unix_listener_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode of the file. Note that `0600` is an octal value, while `600` is a different decimal value. Setting mode to `0` disables the listener. ### `unix_listener_path` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` - `[unix_listener](/main/core/summaries/settings.html#unix_listener)` Path to the UNIX socket file, relative to `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. The `[unix_listener](/main/core/summaries/settings.html#unix_listener)` filter name refers to this setting. ### `unix_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `unix_listener_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Owner of the listener file. Empty (default) means UID 0 (root). ## Service Limits There are 3 types of services that need to be optimized in different ways: 1. Master services (e.g. `auth`, `anvil`, `indexer`, `log`): Currently there isn't any easy way to optimize these. If these become a bottleneck, typically you need to run another Dovecot server. In some cases it may be possible to create multiple master processes and have each one be responsible for only specific users/processes, although this may also require some extra development. 2. Services that do disk I/O or other blocking operations (e.g. `imap`, `pop3`, `lmtp`): These should have `[service_client_limit = 1](/main/core/summaries/settings.html#service_client_limit)`, because any blocking operation will block all the other clients and cause unnecessary delays and even timeouts. This means that `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` specifies the maximum number of available parallel connections. 3. Services that have no blocking operations (e.g. `imap-login`, `pop3-login`): For best performance (but a bit less safety), these should have `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` and `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` set to the number of CPU cores, so each CPU will be busy serving the process but without unnecessary context switches. Then `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` needs to be set high enough to be able to serve all the needed connections (max connections = `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` \* `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`). `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` is commonly set to `unlimited` for these services. Otherwise when the limit is beginning to be reached, the total number of available connections will shrink. With very bad luck that could mean that all the processes are simply waiting for the existing connections to die away before the process can die and a new one can be created. Although this could be made less likely by setting `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` higher than `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)`, but that's still not a guarantee since each process could get a very long running connection and the `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` would be eventually reached. ## Default Services ### anvil The anvil process tracks state of users and their connections. It is also used to kick users' connections. - `[user=root](/main/core/summaries/settings.html#service_user)`, because it needs the ability to send signals to mail processes to kick the users. - `[process_limit=1](/main/core/summaries/settings.html#service_process_limit)`, because there can be only one. - `[client_limit](/main/core/summaries/settings.html#service_client_limit)` should be large enough to handle all the simultaneous connections. Dovecot attempts to verify that the limit is high enough at startup. If it's not, it logs a warning such as: - "Warning: service anvil { client\_limit=200 } is lower than required under max. load (207)" This is calculated by counting the `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` of auth and login services, because each of them has a persistent connection to anvil. - `[idle_kill_interval=infinite](/main/core/summaries/settings.html#service_idle_kill_interval)`, because it should never die or all of its tracked state would be lost. - `[doveadm who](/main/core/summaries/doveadm.html#who)` and some other doveadm commands connect to anvil's UNIX listener and request its state. ### auth The master auth process. There are 4 types of auth client connections: **auth** Only [SASL](/main/core/admin/sasl.html) authentication is allowed. This can be safely exposed to entire world. **userdb** userdb lookups and passdb lookups (without the password itself) can be done for any user, and a list of users can be requested. This may or may not be a security issue. Access to userdb lookup is commonly needed by [LDA](/main/core/config/delivery/lda.html), doveadm, and other tools. **login** Starts a two phase user login by performing authenticating (same as `client` type). Used by login processes. **master** Finishes the two phase user login by performing a userdb lookup (similar to "userdb" type). Used by post-login processes (e.g. imap, pop3). Changed: 2.4.0 The listener type is configured explicitly using the **type** field. For older versions, the listener type is selected based on the (file)name after the last `-` in the name. For example `anything-userdb` is of `userdb` type. The default type is `auth` for unrecognized listeners. You can add as many `auth` and `userdb` listeners as you want (and you probably shouldn't touch the `login` and `master` listeners). - `[client_limit](/main/core/summaries/settings.html#service_client_limit)` should be large enough to handle all the simultaneous connections. Dovecot attempts to verify that the limit is high enough at startup. If it's not, it logs a warning such as: - Warning: service auth { client\_limit=1000 } is lower than required under max. load (1328) This is calculated by counting the `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` of every service that is enabled with the `[protocol](/main/core/summaries/settings.html#service_protocol)` setting (e.g. imap, pop3, lmtp). Only services with the `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` setting being `!= 1` are counted, because they have persistent connections to auth, while `[service_restart_request_count = 1](/main/core/summaries/settings.html#service_restart_request_count)` processes only do short-lived auth connections. - `[process_limit=1](/main/core/summaries/settings.html#service_process_limit)`, because there can be only one auth master process. - `[user=$SET:default_internal_user](/main/core/summaries/settings.html#service_user)`, because it typically doesn't need permissions to do anything (PAM lookups are done by auth-workers). - `[chroot](/main/core/summaries/settings.html#service_chroot)` could be set (to e.g. `empty`) if passdb/userdb doesn't need to read any files (e.g. SQL, LDAP config is read before chroot) ### auth-worker Auth master process connects to auth worker processes. It is mainly used by passdbs and userdbs that do potentially long running lookups. For example MySQL supports only synchronous lookups, so each query is run in a separate auth worker process that does nothing else during the query. PostgreSQL and LDAP supports asynchronous lookups, so those don't use worker processes at all. With some passdbs and userdbs you can select if worker processes should be used. - `[client_limit=1](/main/core/summaries/settings.html#service_client_limit)`, because only the master auth process connects to auth worker. - `[process_limit](/main/core/summaries/settings.html#service_process_limit)` indicates the maximum number of workers. - `[user=root](/main/core/summaries/settings.html#service_user)` by default, because by default PAM authentication is used, which usually requires reading `/etc/shadow`. If this isn't needed, it's a good idea to change this to something else, such as `$SET:default_internal_user`. - `[chroot](/main/core/summaries/settings.html#service_chroot)` could also be set if possible. - `[restart_request_count=unlimited](/main/core/summaries/settings.html#service_restart_request_count)` counts the number of processed auth requests. This can be used to cycle the process after the specified number of auth requests (default is unlimited). The worker processes also stop after being idle for `[service_idle_kill_interval](/main/core/summaries/settings.html#service_idle_kill_interval)`. ### config Config process reads and parses the `dovecot.conf` file, and exports the parsed data in simpler format to config clients. - `[user=root](/main/core/summaries/settings.html#service_user)`, because the process needs to be able to reopen the config files during a config reload, and often some parts of the config having secrets are readable only by root. - Only root should be able to connect to its UNIX listener, unless there are no secrets in the configuration. Passwords are obviously secrets, but less obviously `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` is also a secret, since it contains the actual SSL key data instead of only a filename. ### dict Dovecot has a [lib-dict](/main/core/config/dict.html) API for doing simple key-value lookups/updates in various backends (SQL, file). This is optionally used by things like quota, expire plugin, and other things in the future. It would be wasteful for each mail process to separately create a connection to SQL, so usually they go through the `proxy` dict backend. These proxy connections are the client connections of dict processes. - dict / Synchronous lookups (e.g. mysql) - `client_limit=1`, because dict lookups are synchronous and the client is supposed to disconnect immediately after the lookup. - dict-async / Asynchronous lookups (e.g. pgsql, cassandra, ldap) - `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` should commonly be the same as number of CPU cores. Although with Cassandra this may not be true, because Cassandra library can use multiple threads. - `[user=$SET:default_internal_user](/main/core/summaries/settings.html#service_user)`, because the proxy dict lookups are typically SQL lookups, which require no filesystem access. (The SQL config files are read while still running as root.) - The dict clients can do any kind of dict lookups and updates for all users, so they can be rather harmful if exposed to an attacker. That's why by default only root can connect to dict socket. Unfortunately that is too restrictive for all setups, so the permissions need to be changed so that Dovecot's mail processes (and only them) can connect to it. ### dict-expire Added: 2.4.0 This process periodically goes through configured dicts and deletes all expired rows in them. Currently this works only for dict-sql when `expire_field` has been configured. - `[process_limit=1](/main/core/summaries/settings.html#service_process_limit)`, because only one process should be running expires. - `[user](/main/core/summaries/settings.html#service_user)` and other permissions should be the same as for the dict service. ### dns\_client Used by lib-dns library to perform asynchronous DNS lookups. The dns-client processes internally use the synchronous `gethostbyname()` function. - `[client_limit=1](/main/core/summaries/settings.html#service_client_limit)`, because the DNS lookup is synchronous. - `[user=$SET:default_internal_user](/main/core/summaries/settings.html#service_user)`, because typically no special privileged files need to be read. - `[chroot](/main/core/summaries/settings.html#service_chroot)` can be used only if it contains `etc/resolv.conf` and other files necessary for DNS lookups. ### doveadm It's possible to run doveadm mail commands via doveadm server processes. This is useful for running doveadm commands for multiple users simultaneously, and it's also useful in a multiserver system where doveadm can automatically connect to the correct backend to run the command. - `[client_limit=1](/main/core/summaries/settings.html#service_client_limit)`, because doveadm command execution is synchronous. - `[restart_request_count=1](/main/core/summaries/settings.html#service_restart_request_count)` just in case there were any memory leaks. This could be set to some larger value (or `unlimited`) for higher performance. - `[user=root](/main/core/summaries/settings.html#service_user)`, but the privileges are (temporarily) dropped to the mail user's privileges after userdb lookup. If only a single UID is used, user can be set to the mail UID for higher security, because the process can't gain root privileges anymore. ### imap, pop3, submission, managesieve Post-login process for handling IMAP/POP3/Submission/ManageSieve client connections. - `[client_limit](/main/core/summaries/settings.html#service_client_limit)` may be increased from the default `1` to save some CPU and memory, but it also increases the latency when one process serving multiple clients is waiting for a long time for a lock or disk I/O. In the future these waits may be reduced or avoided completely, but for now it's not safe to set this value higher than `1` in enterprise mail systems. For small, mostly-idling hobbyist servers, a larger number may work without problems. - `[restart_request_count=1](/main/core/summaries/settings.html#service_restart_request_count)` can be changed if only a single UID is used for mail users. This improves performance, but it's less secure, because bugs in code may leak email data from another user's earlier connection. - `[process_limit](/main/core/summaries/settings.html#service_process_limit)` specifies the maximum number of simultaneous connections for the protocol that this service handles (IMAP, POP3, Submission, or ManageSieve). If you expect more connections, increase this value. ### imap-login, pop3-login, submission-login, managesieve-login See [login processes](/main/core/config/login_processes.html). ### indexer Indexer master process, which tracks and prioritizes indexing requests from mail processes. The actual indexing is done by indexer-worker processes. The indexing means both updating Dovecot's internal index and cache files with new messages and updating full text search indexes (if enabled). The indexer master process guarantees that the FTS index is never modified by more than one process. - `[process_limit=1](/main/core/summaries/settings.html#service_process_limit)`, because only one process can keep the FTS guarantee. - `[user=$SET:default_internal_user](/main/core/summaries/settings.html#service_user)`, because the process doesn't need any permissions. - `[chroot](/main/core/summaries/settings.html#service_chroot)` could be set to `[base_dir](/main/core/summaries/settings.html#base_dir)` for extra security. It still needs to be able to connect to indexer-worker socket. ### indexer-worker Indexer worker process. indexer-workers are background processes that are not normally visible to the end user (exception: if mails are not indexed, i.e. on delivery, indexing needs to occur on-demand if a user issues a SEARCH command). Therefore, they generally should be configured to a lower priority to ensure that they do not steal resources from other processes that are user facing. A recommendation is to execute the process at a lower priority. This can be done by prefixing the executable location with a priority modifier, such as: doveconf ``` service indexer-worker { executable = /usr/bin/nice -n 10 /usr/libexec/dovecot/indexer-worker } ``` - `[client_limit=1](/main/core/summaries/settings.html#service_client_limit)`, because indexing is a synchronous operation. - `[process_limit=10](/main/core/summaries/settings.html#service_process_limit)`, because the FTS index updating can eat a lot of CPU and disk I/O. You may need to adjust this value depending on your system. - `[user=root](/main/core/summaries/settings.html#service_user)`, but the privileges are (temporarily) dropped to the mail user's privileges after userdb lookup. If only a single UID is used, user can be set to the mail UID for higher security, because the process can't gain root privileges anymore. ### lmtp LMTP process for delivering new mails. - `[client_limit=1](/main/core/summaries/settings.html#service_client_limit)`, because most of the time spent on an LMTP client is spent waiting for disk I/O and other blocking operations. There's no point in having more clients waiting around during that doing nothing. However, LMTP proxying is only writing to temporary files that normally stay only in memory. So for LMTP proxying, a `client_limit` above `1` could be useful. - `[user=root](/main/core/summaries/settings.html#service_user)`, but the privileges are (temporarily) dropped to the mail user's privileges after userdb lookup. If only a single UID is used, user can be set to the mail UID for higher security, because the process can't gain root privileges anymore. - `[restart_request_count=1](/main/core/summaries/settings.html#service_restart_request_count)` this should match the same values as for the IMAP, POP3 Submission, and ManageSieve post-login processes. ### log All processes started via Dovecot master process log their messages via the `log` process. This allows some nice features compared to directly logging via syslog. - `[process_limit=1](/main/core/summaries/settings.html#service_process_limit)`, because the log process keeps track of all the other logging processes. - `[user=root](/main/core/summaries/settings.html#service_user)`, because it guarantees being able to write to syslog socket and to the log files directly. ### stats Event statistics tracking. Its behavior is very similar to the anvil process, but anvil's data is of higher importance and lower traffic than stats, so stats are tracked in a separate process. - `[client_limit](/main/core/summaries/settings.html#service_client_limit)` should be large enough to handle all the simultaneous connections. Dovecot attempts to verify that the limit is high enough at startup. If it's not, it logs a warning such as: `Warning: service stats { client_limit=1000 } is lower than required under max. load (7945)` This is calculated by counting the `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` of all the services, because each of them has a persistent connection to stats. # Shared Mailboxes Dovecot supports mailbox sharing in a single backend setup: - [Public Shared](#public-shared-mailboxes): Shared mailboxes created by administrators. - [User Shared](#user-shared-mailboxes): Users sharing their mailboxes to other users. - [Symlinking](#mailbox-sharing-with-symlinks): Quick and dirty way of sharing a few mailboxes. Sharing mailboxes when running multiple backends requires [Dovecot Pro](https://www.dovecotpro.com/). See [Filesystem Permissions](#filesystem-permissions) for common filesystem related permission problems. Note that these permissions only make sense when using a shared filesystem to enable sharing between users. ## Public Shared Mailboxes Public mailboxes are typically mailboxes that are visible to all users or to large user groups. They are created by defining a public namespace, under which all the shared mailboxes are. See [Namespaces](/main/core/config/namespaces.html) for details of how namespaces are configured. For example to create a public Maildir mailboxes, use: dovecot ``` # User's private mail location mail_driver = maildir mail_path = ~/Maildir # When creating any namespaces, you must also have a private namespace: namespace inbox { type = private separator = / prefix = # use global mail_path inbox = yes } namespace public { type = public separator = / prefix = Public/ mail_path = /var/mail/public subscriptions = no } ``` In the above example, you would then create Maildir mailboxes under the `/var/mail/public/` directory. Note that with [Maildir](/main/core/config/mailbox_formats/maildir.html) format Dovecot uses Maildir++ layout by default for folders, where the folder names must begin with a "." or Dovecot will ignore them. You can also optionally use the "fs" layout if you want the directory structure to look like: - `/var/mail/public/` (root dir) - `/var/mail/public/info/` (maildir folder) - `/var/mail/public/company/` (maildir folder) ### Per-user \\Seen Flag The recommended way to enable private flags for shared mailboxes is to create private indexes with `[mail_index_private_path](/main/core/summaries/settings.html#mail_index_private_path)`. This creates `dovecot.index.pvt[.log]` files, which contain only the message UIDs and the private flags. Currently the list of private flags is hardcoded only to the \\Seen flag. doveconf ``` namespace public { type = public separator = / prefix = Public/ mail_driver = maildir mail_path = /var/mail/public mail_index_private_path = ~/Maildir/public subscriptions = no } ``` ### Maildir: Keyword Sharing Make sure you don't try to use per-user `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` directory. Otherwise `dovecot-keywords` file doesn't get shared and keyword mapping breaks. ### Subscriptions Typically you want each user to have control over their own subscriptions for mailboxes in public namespaces. This is why you should set `subscriptions=no` to the namespace. Dovecot will then use the parent namespace's subscriptions file. If you don't otherwise have a namespace with empty prefix, create one: doveconf ``` namespace subscriptions { prefix = separator = / subscriptions = yes hidden = yes list = no alias_for = inbox # the INBOX namespace's name mailbox_subscriptions_filename = subscriptions-shared } ``` ### Read-Only Mailboxes #### mbox If you have a read-only directory structure containing mbox files, you'll need to store index files elsewhere: doveconf ``` namespace public { type = public prefix = Public/ mail_driver = mbox mail_path = /var/mail/public/ mail_index_path = /var/indexes/public subscriptions = no } ``` In the above example all the users would still be sharing the index files, so you might have problems with filesystem permissions. Alternatively you could place the index files under user's home directory. #### Maildir If your Maildir is read-only, the control and index files still need to be created somewhere. You can specify the path for these with the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` and `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` settings. The path may point to a directory that is shared among all users, or to a per-user path. Note that if the Maildir has any keywords, the per-user control directory breaks the keywords since there is no `dovecot-keywords` file. When configuring multiple namespaces, the control/index path must be different for each namespace. Otherwise if namespaces have identically named mailboxes their control/index directories will conflict and cause all kinds of problems. If you put the control files to a per-user directory, you must also put the index files to a per-user directory, otherwise you'll get errors. It is however possible to use shared control files but per-user index files, assuming you've set up permissions properly. doveconf ``` namespace public { type = public separator = / prefix = Public/ mail_driver = maildir mail_path = /var/mail/public mail_control_path = ~/Maildir/public mail_index_path = ~/Maildir/public subscriptions = no } namespace public { type = public separator = / prefix = Team/ mail_driver = maildir mail_path = /var/mail/team mail_control_path = ~/Maildir/team mail_index_path = ~/Maildir/team subscriptions = no } ``` ### Example: Public mailboxes with ACLs See [acl plugin](/main/core/plugins/acl.html) for more information about ACLs. doveconf ``` namespace public { type = public separator = . prefix = public. mail_driver = maildir mail_path = /var/mail/public subscriptions = no list = children } acl_driver = vfile ``` It's important that the namespace type is "public" regardless of whether you set the namespace prefix to "shared." or something else. After this you have to place `dovecot-acl` files in every mailbox/folder below `/var/mail/public` with rights for that folder (e.g. `user=someone lr`). `[acl_sharing_map](/main/core/summaries/settings.html#acl_sharing_map)` is not relevant for public mailboxes (only for shared). ## User Shared Mailboxes To enable mailbox sharing, you'll need to create a shared namespace. See [acl plugin](/main/core/plugins/acl.html) for more information about ACL-specific settings. doveconf ``` # User's private mail location. mail_driver = maildir mail_path = ~/Maildir # When creating any namespaces, you must also have a private namespace: namespace inbox { type = private separator = / prefix = # use global mail_path inbox = yes } namespace shared { type = shared separator = / prefix = shared/$user/ mail_path = %{owner_home}/Maildir mail_index_private_path = ~/Maildir/shared/%{owner_user} # If users have direct filesystem level access to their mails, it's safer # to not share the index files between users: #mail_index_path = ~/Maildir/shared/%{owner_user} subscriptions = no list = children } mail_plugins { acl = yes } protocol imap { mail_plugins { imap_acl = yes } } acl_driver = vfile ``` This creates a `shared/` namespace under which each user's mailboxes are. If you have multiple domains and allow sharing between them, you might want to set `[namespace_prefix = shared/$domain/$username/](/main/core/summaries/settings.html#namespace_prefix)` instead (although `$user` works just fine too). If you don't, you might want to drop the domain part and instead use `[namespace_prefix = shared/$username/](/main/core/summaries/settings.html#namespace_prefix)`. Changed: 2.4.0 The shared namespaces now use `$user`, `$username` and `$domain` template variables, rather than the old `%%u`, `%%n` and `%%d`. `[namespace_list = children](/main/core/summaries/settings.html#namespace_list)` specifies that if no one has shared mailboxes to the user, the "shared" directory isn't listed by the LIST command. If you wish it to be visible always, you can set `[namespace_list = yes](/main/core/summaries/settings.html#namespace_list)`. The sharing user can be accessed with `%{owner_user}`, `%{owner_username}` and `%{owner_domain}` variables. The sharing user's home directory can also be looked up via [User Databases](/main/core/config/auth/userdb.html) using `%{owner_home}` variable. These can be used in [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting). If the users' mailboxes can be found using a template, it's a bit more efficient to not use `%{owner_home}`. For example: doveconf ``` mail_driver = maildir mail_path = /var/mail/%{owner_domain}/%{owner_username}/Maildir mail_index_private_path = ~/Maildir/shared/%{owner_user} ``` ### dbox With dbox, the index files are a very important part of the mailboxes. You must not try to change `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` to a user-specific location. This will only result in mailbox corruption. (`[mail_index_private_path](/main/core/summaries/settings.html#mail_index_private_path)` can be used though.) ### Shared Mailbox Listing With the above configuration it's possible to open shared mailboxes if you know their name, but they won't be visible in the mailbox list. This is because Dovecot has no way of knowing what users have shared mailboxes to whom. Iterating through all users and looking inside their mail directories would be horribly inefficient for more than a couple users. To overcome this problem Dovecot needs a dictionary, which contains the list of users who have shared mailboxes and to whom they have shared. If the users aren't properly listed in this dictionary, their shared mailboxes won't be visible. Currently there's no way to automatically rebuild this dictionary, so make sure it doesn't get lost. If it does, each user having shared mailboxes must use the IMAP SETACL command (see below) to get the dictionary updated for themselves. See `[acl_sharing_map](/main/core/summaries/settings.html#acl_sharing_map)` for plugin setting information. You could use any dictionary driver, including SQL or Cassandra, but a simple flat file should work pretty well too: doveconf ``` acl_sharing_map { dict file { path = /var/lib/dovecot/db/shared-mailboxes.db } } ``` The IMAP processes must be able to write to the `db/` directory. If you're using system users, you probably want to make it mode 0770 and group `sharedusers` and set `mail_access_groups=sharedusers` (or something similar). If you use multiple domains and don't wish users to share their mailboxes to users in other domains, you can use separate dict files for each domain: doveconf ``` acl_sharing_map { dict file { path = /var/mail/%{user | domain}/shared-mailboxes.db } } ``` #### Using SQL dictionary See [dictionary](/main/core/config/dict.html) for more information, especially about permission issues. dovecot.confDatabase Tables doveconf ``` acl_sharing_map { dict proxy { name = acl } } acl_dict_index = yes dict_server { dict acl { driver = sql sql_driver = pgsql pgsql localhost { parameters { dbname = mails user = sqluser password = sqlpass } } dict_map shared/shared-boxes/user/$to/$from { sql_table = user_shares value_field dummy { } key_field from_user { value = $from } key_field to_user { value = $to } } dict_map shared/shared-user-boxes-rev/$from/$to { sql_table = user_shares value_field dummy { } key_field from_user { value = $from } key_field to_user { value = $to } } dict_map shared/shared-boxes/anyone/$from { sql_table = anyone_shares value_field dummy { } key_field from_user { value = $from } } } } ``` sql ``` CREATE TABLE user_shares ( from_user varchar(100) not null, to_user varchar(100) not null, dummy char(1) DEFAULT '1', -- always '1' currently primary key (from_user, to_user) ); COMMENT ON TABLE user_shares IS 'User from_user shares folders to user to_user.'; CREATE INDEX user_shares_from_user ON user_shares (from_user); -- because we search for from_user when rebuilding ACLs CREATE TABLE anyone_shares ( from_user varchar(100) not null, dummy char(1) DEFAULT '1', -- always '1' currently primary key (from_user) ); COMMENT ON TABLE anyone_shares IS 'User from_user shares folders to anyone.'; ``` ### Mailbox Sharing You can use `[doveadm acl](/main/core/summaries/doveadm.html#acl)` to share mailboxes, or it can be done using IMAP SETACL command. It is the only way to update the shared mailbox list dictionary. Below is a quick introduction to IMAP ACL commands. See [RFC 4314](https://datatracker.ietf.org/doc/html/rfc4314) for more details. #### `MYRIGHTS ` Returns the user's current rights to the mailbox. #### `GETACL ` Returns the mailbox's all ACLs. #### `SETACL [+|-]` Give `` the specified rights to the mailbox. #### `DELETEACL [-]` Delete ``'s ACL from the mailbox. | `` | Description | | --- | --- | | `anyone` | Matches all users, including anonymous users. | | `authenticated` | Like "anyone", but doesn't match anonymous users. | | `$group` | Matches all users belonging to the group ($ is not part of the group name). | | `$!group` | See `group-override` in [acl plugin](/main/core/plugins/acl.html) (Dovecot-specific feature). | | `user` | Matches the given user. | The `$group` syntax is not a standard, but it is mentioned in [RFC 4314](https://datatracker.ietf.org/doc/html/rfc4314) examples and is also understood by at least Cyrus IMAP. Having '`-`' before the identifier specifies negative rights. See [acl plugin](/main/core/plugins/acl.html) for list of ``. ### Sharing Mailboxes to Everyone See `[imap_acl_allow_anyone](/main/core/summaries/settings.html#imap_acl_allow_anyone)`. Note that you can also do this only for some users by using the second table "`anyone_shares`". Every user listed in this table shares his folders with everyone. See also [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). ### IMAP ACL examples Let's begin with some simple example that first gives "read" and "lookup" rights, and later adds "write-seen" right: ``` 1 SETACL Work user@domain rl 1 OK Setacl complete. 2 SETACL Work user@domain +s 2 OK Setacl complete. 3 GETACL Work * ACL "Work" "user@domain" lrs "myself" lrwstipekxacd 3 OK Getacl completed. ``` Let's see how negative rights work by testing it on ourself. See how we initially have "lookup" right, but later we don't: ``` 1 MYRIGHTS Work * MYRIGHTS "Work" lrwstipekxacd 1 OK Myrights completed. 2 SETACL Work -myself l 2 OK Setacl complete. 3 GETACL Work * ACL "Work" "-myself" l "user@domain" lr "myself" lrwstipekxacd 3 OK Getacl completed. 4 myrights Work * MYRIGHTS "Work" rwstipekxacd 4 OK Myrights completed. ``` ### Troubleshooting - Make sure `$user` or `$username` is specified in the `[namespace_prefix](/main/core/summaries/settings.html#namespace_prefix)` setting rather than the old `%%u` or `%%n`. - Make sure the `[mail_path](/main/core/summaries/settings.html#mail_path)` is set correctly in the namespace location. `[log_debug = category=mail](/main/core/summaries/settings.html#log_debug)` will help you see if Dovecot is trying to access correct paths. - `[doveadm acl debug -u user@domain shared/user/box]()` can be helpful in figuring out why a mailbox can't be accessed. ## Mailbox Sharing with Symlinks It's possible to share mailboxes simply by symlinking them among user's private mailboxes. See [Filesystem Permissions](#filesystem-permissions) for issues related to filesystem permissions. ### Maildir sh ``` ln -s /home/user2/Maildir/.Work /home/user1/Maildir/.shared.user2 ln -s /home/user3/Maildir/.Work /home/user1/Maildir/.shared.user3 ``` Now user1 has a "shared" directory containing "user2" and "user3" child mailboxes, which point to those users' "Work" mailbox. With Maildir++ layout it's not possible to automatically share "mailbox and its children". You'll need to symlink each mailbox separately. With the "fs" layout this is possible though. ## mbox Doing the same as in the above Maildir example: sh ``` mkdir /home/user1/mail/shared ln -s /home/user2/mail/Work /home/user1/mail/shared/user2 ln -s /home/user3/mail/Work /home/user1/mail/shared/user3 ``` One additional problem with mbox format is the creation of dotlock files. The dotlocks would be created under user1's directory, which makes them useless. Make sure the locking works properly with only fcntl or flock locking (See [mbox locking](/main/core/config/mailbox_formats/mbox.html#locking)) and just disable dotlocks. Alternatively instead of symlinking an mbox file, put the shared mailboxes inside a directory and symlink the entire directory. ## Filesystem Permissions IMAP processes need filesystem level permissions to access shared/public mailboxes. This means that: - If you use more than one [UNIX UID](/main/core/config/system_users.html) for your mail users (e.g. you use system users), you'll need to make sure that all users can access the mailboxes on filesystem level. ([acl plugin](/main/core/plugins/acl.html) won't help you with this.) - You can remove write permissions on purpose from public namespace root directory to prevent users from creating new mailboxes under it. Dovecot never modifies permissions for existing mail files or directories. When users share mailboxes between each others, the system must have been set up in a way that filesystem permissions don't get in the way. The easiest way to do that is to use only a single UID. Another possibility would be to use one or more groups for all the mail files that may be shared to other users belonging to the same group. For example if you host multiple domains, you might create a group for each domain and allow mailbox sharing (only) between users in the same domain. ### System User UNIX Groups There's no requirement to use UNIX groups (i.e. typically defined in `/etc/group`) for anything. If you don't care about them, you can safely ignore this section. If you use [passwd authentication database](/main/core/config/auth/databases/passwd.html), the IMAP process has access to all the UNIX groups defined for that user. You may use these groups when granting filesystem permissions. If you wish to use UNIX groups defined in `/etc/group` but don't use passwd userdb, you can still do this by returning `system_groups_user`[userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields), which contains the UNIX user name whose groups are read from the group file. You can also set up extra UNIX groups by listing them in `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)`. To have per-user UNIX groups, return `mail_access_groups` as userdb extra field. The advantage of using this method is that only Dovecot mail processes have access to the group, but nothing else, such as user's SSH session. For example, a simple way to set up shared mailbox access for all' system users is to make all mail dirs/files 0770/0660 mode and owned by group "sharedmail" and then set `[mail_access_groups = sharedmail](/main/core/summaries/settings.html#mail_access_groups)`. Using more fine-grained groups of course leaks less mail data in case there's a security hole in Dovecot. ### Permissions For New Mailboxes When creating a new mailbox, Dovecot copies the permissions from the mailbox root directory. For example, with [mbox](/main/core/config/mailbox_formats/mbox.html) if you have directories: ``` drwx--xr-x 8 user group 4096 2009-02-21 18:31 /home/user/mail/ drwxrwxrwx 2 user group 4096 2009-02-21 18:32 /home/user/mail/foo/ ``` When creating a new foo/bar/ directory, Dovecot gives it permissions: ``` drwx--xr-x 2 user group 4096 2009-02-21 18:33 /home/user/mail/foo/bar/ ``` As you can see, the file mode was copied from mail/ directory, not mail/foo/. The group is also preserved. If this causes problems (e.g. different users having different groups create mailboxes, causing permission denied errors when trying to preserve the group) you can set the setgid bit for the root directory: sh ``` chmod g+s /home/user/mail ``` This will cause the group to be automatically copied by the OS for all created files/directories under it, even if the user doesn't belong to the group. ### Permissions For New Files in Mailboxes When creating new files inside a mailbox, Dovecot copies the read/write permissions from the mailbox's directory. For example if you have: ``` drwx--xr-x 5 user group 4096 2009-02-21 18:53 /home/user/Maildir/.foo/ ``` Dovecot creates files under it with modes: ``` drwx--xr-x 2 user group 4096 2009-02-21 18:54 cur/ drwx--xr-x 2 user group 4096 2009-02-21 18:54 new/ drwx--xr-x 2 user group 4096 2009-02-21 18:54 tmp/ -rw----r-- 1 user group 156 2009-02-21 18:54 dovecot.index.log -rw----r-- 1 user group 17 2009-02-21 18:54 dovecot-uidlist ``` Note how the g+x gets copied to directories, but for files it's simply ignored. The group is copied the same way as explained in the previous section. When mails are copied between Maildirs, it's usually done by hard linking. If the source and destination directory permissions are different, Dovecot create a new file and copies data the slow way so that it can assign the wanted destination permissions. The source and destination permission lookups are done only by looking at the mailbox root directories' permissions, not individual mail files. This may become a problem if the mail files' permissions aren't as Dovecot expects. ### Permissions to New /domain/user Directories If each user has different UIDs and you have `/var/mail/domain/user/` style directories, you run into a bit of trouble. The problem is that the first user who creates `/var/mail/domain/` will create it as 0700 mode, and other users can't create their own user/ directories under it anymore. The solution is to use a common group for the users and set `/var/mail/` directory's permissions properly (group-suid is required): sh ``` chgrp dovemail /var/mail chmod 02770 /var/mail # or perhaps 03770 for extra security ``` and in `dovecot.conf`: doveconf ``` mail_driver = maildir mail_path = /var/vmail/%{user | domain}/%{user | username}/Maildir mail_access_groups = dovemail ``` The end result should look like this: ``` drwxrwsr-x 3 user dovemail 60 Oct 24 12:04 domain.example.com/ drwx--S--- 3 user user 60 Oct 24 12:04 domain.example.com/user/ ``` Note that this requires that the `[mail_path](/main/core/summaries/settings.html#mail_path)` setting is in its explicit format with [%variables](/main/core/settings/variables.html). Using `~/Maildir` won't work, because Dovecot can't really know how far down it should copy the permissions from. ### Permissions to New User Home Directories When `[mail_path](/main/core/summaries/settings.html#mail_path)` begins with `%{home}` or `~/`, its permissions are copied from the first existing parent directory if it has setgid-bit set. This isn't done when the path contains any other [%variables](/main/core/settings/variables.html). ### Mail Delivery Agent Permissions When using [LDA](/main/core/config/delivery/lda.html), it uses all the same configuration files as IMAP/POP3, so you don't need to worry about it. When using an external [MDA](/main/core/config/delivery/mda.html) to deliver to a shared mailbox, you need to make sure that the resulting files have proper permissions. For example with Procmail + Maildir, set `UMASK=007` in `.procmailrc` to make the delivered mail files group-readable. To get the file to use the proper group, set the group to the Maildir's `tmp/` directory and also set its setgid bit (`chmod g+s`). ### Dictionary Files Created dictionary files (e.g. `acl_sharing_map { dict file { ... } }`) also base their initial permissions on parent directory's permissions. After the initial creation, the permissions are permanently preserved. So if you want to use different permissions, just chown/chmod the file. # Sieve: Duplicate Extension The duplicate extension ([RFC 7352](https://datatracker.ietf.org/doc/html/rfc7352)) adds a new test command called `duplicate` to the Sieve language. This test adds the ability to detect duplications. The main application for this new test is handling duplicate deliveries commonly caused by mailing list subscriptions or redirected mail addresses. The detection is normally performed by matching the message ID to an internal list of message IDs from previously delivered messages. For more complex applications, the `duplicate` test can also use the content of a specific header field or other parts of the message. Changed: 2.4.0 `vnd.dovecot.duplicate` extension has been removed in favor of this. ## Configuration The duplicate extension is available by default. ### Settings #### `sieve_duplicate_default_period` | Key 1 | | --- | | Default | `14d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Advanced Setting; this should not normally be changed. | Default period after which tracked values are purged from the duplicate tracking database. #### `sieve_duplicate_max_period` | Key 1 | | --- | | Default | `7d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Advanced Setting; this should not normally be changed. | Maximum period after which tracked values are purged from the duplicate tracking database. ### Example doveconf ``` sieve_script personal { path = ~/.dovecot.sieve } sieve_duplicate_default_period = 1h sieve_duplicate_max_period = 1d ``` # Sieve: Editheader Extension The editheader extension ([RFC 5293](https://datatracker.ietf.org/doc/html/rfc5293)) enables Sieve scripts to delete and add message header fields, thereby allowing interaction with other components that consume or produce header fields. ## Configuration The editheader extension is not available by default and needs to be enabled explicitly by adding it to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. ### Settings #### `sieve_editheader_header` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` - `[sieve_editheader_header_forbid_add](/main/core/summaries/settings.html#sieve_editheader_header_forbid_add)` - `[sieve_editheader_header_forbid_delete](/main/core/summaries/settings.html#sieve_editheader_header_forbid_delete)` Configures a new message header, which can be forbidden to be added or deleted. The filter name refers to the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` setting. #### `sieve_editheader_header_forbid_add` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` Forbid adding the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` to the message header. Addition of the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, adding this header to this setting has no effect. #### `sieve_editheader_header_forbid_delete` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` Forbid deleting the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` from the message header. Deleting the `Received:` and `Auto-Submitted:` fields is always forbidden, while removing the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, using this setting with one of these headers has no effect. #### `sieve_editheader_header_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` The name of the message header that is forbidden to be added or deleted. #### `sieve_editheader_max_header_size` **Default:** `2k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) The maximum size in bytes of a header field value passed to the addheader command. The minimum value for this setting is `1024` bytes. ### Example doveconf ``` # Use editheader sieve_extensions { editheader = yes } # Header fields must not exceed one kilobyte sieve_editheader_max_header_size = 1k # Protected special headers sieve_editheader_header X-Verified { forbid_add = yes forbid_delete = yes } sieve_editheader_header X-Seen { forbid_delete = yes } ``` # Sieve: Extension for Notifications The Sieve enotify extension ([RFC 5435](https://datatracker.ietf.org/doc/html/rfc5435)) adds the `notify` action to the Sieve language. ## Configuration ### Settings #### `sieve_notify_mailto_envelope_from` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[sieve_redirect_envelope_from](/main/core/summaries/settings.html#sieve_redirect_envelope_from)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) Defines the source of the notification sender address for e-mail notifications. #### `sieve_notify_mailto_max_headers` | Key 1 | | --- | | Default | `16` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_recipients](/main/core/summaries/settings.html#sieve_notify_mailto_max_recipients)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit the number of additional headers per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of additional headers a single `mailto:` notify action may specify in its URI. `0` means no limit on the number of headers is enforced. #### `sieve_notify_mailto_max_recipients` | Key 1 | | --- | | Default | `8` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_headers](/main/core/summaries/settings.html#sieve_notify_mailto_max_headers)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit the number of recipients per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of recipients a single `mailto:` notify action may specify, counted across the URI and any `to`/`cc`/`bcc` headers. `0` means no limit on the number of recipients is enforced. #### `sieve_notify_max_notifications` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` - `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit maximum number of notifications per script is added. | | Advanced Setting; this should not normally be changed. | The maximum number of `notify` actions ([RFC 5435](https://datatracker.ietf.org/doc/html/rfc5435)) that can be performed during a single script execution. `0` means `notify` is prohibited; scripts using the `notify` command will fail to compile with "local policy prohibits the use of a notify action". Note that this is independent of `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)`; the `redirect` action and the `notify` mailto method are governed by separate limits. The effective per-script limit on `notify` actions is `min(sieve_notify_max_notifications, sieve_max_actions)`: when the total number of actions in a script reaches `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` (default `32`), execution aborts with "total number of actions exceeds policy limit" before this per-class limit is reached. To allow more than `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` notify actions, raise that setting as well. ### Examples #### Send Notifications with Different Importance Levels ``` require ["enotify", "fileinto", "variables"]; if header :contains "from" "boss@example.org" { notify :importance "1" :message "This is probably very important" "mailto:alm@example.com"; # Don't send any further notifications stop; } if header :contains "to" "sievemailinglist@example.org" { # :matches is used to get the value of the Subject header if header :matches "Subject" "*" { set "subject" "${1}"; } # :matches is used to get the value of the From header if header :matches "From" "*" { set "from" "${1}"; } notify :importance "3" :message "[SIEVE] ${from}: ${subject}" "mailto:alm@example.com"; fileinto "INBOX.sieve"; } ``` #### Send Notification if we Receive Mail From Domain ``` require ["enotify", "fileinto", "variables", "envelope"]; if header :matches "from" "*@*.example.org" { # :matches is used to get the MAIL FROM address if envelope :all :matches "from" "*" { set "env_from" " [really: ${1}]"; } # :matches is used to get the value of the Subject header if header :matches "Subject" "*" { set "subject" "${1}"; } # :matches is used to get the address from the From header if address :matches :all "from" "*" { set "from_addr" "${1}"; } notify :message "${from_addr}${env_from}: ${subject}" "mailto:alm@example.com"; } ``` # Sieve: Extlists Extension The extlists extension ([RFC 6134](https://datatracker.ietf.org/doc/html/rfc6134)) enables Sieve scripts to check membership of a value in an external list or for redirecting messages to an external list of recipients. An "external list" is a list whose members are stored externally to the Sieve script. This extension adds a new ":list" match type to apply to supported tests and it can be be used to implement email whitelisting, blacklisting, addressbook lookups, and other sorts of list matching. For Dovecot, the external list is always implemented using a dict lookup. Redirecting messages to a list of recipients as described in the standard ([RFC 6134](https://datatracker.ietf.org/doc/html/rfc6134)) is currently not implemented in Dovecot and will always trigger an error if used. ## Configuration The extlists extension is not available by default and needs to be enabled explicitly by adding it to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. ### Settings #### `sieve_extlists_list` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) This named list filter creates a new list definition for the ":list" match type in Sieve (extlists extension). The filter name refers to the `[sieve_extlists_list_name](/main/core/summaries/settings.html#sieve_extlists_list_name)` setting. That is the name of the list as it will be accessible from the Sieve. A dict definition that is placed inside the scope of this list filter will serve as the lookup for this extlists list. So, values from Sieve are looked up in the defined dict. This is often used to define an address book or whitelist external to the Sieve script. If no dict is present, the list will function as an empty list. Example: ``` sieve_extlists_list :addrbook:default { dict proxy { name = addressbook } } ``` #### `sieve_extlists_list_max_lookup_size` **Default:** `1k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) The maximum size of a value looked up from this Sieve extlists list. If the value is too large, the lookup will be skipped and will yield no result. Note that individual lists may have different limits, meaning that lookups in other lists may still succeed. #### `sieve_extlists_list_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) The name of the list as it will be accessible through the Sieve ":list" match type (extlists extension). According to the standard, this name must be a valid URI. This implementation further limits that to a valid URN or TAG URI. Note that the standard URN and TAG equality rules dictate that at least parts of these URIs are case-sensitive, so it is best to consider the configured name case-sensitive entirely. The `[sieve_extlists_list](/main/core/summaries/settings.html#sieve_extlists_list)` filter refers to this setting. ### Example doveconf ``` # Use extlists sieve_extensions { extlists = yes } # No value looked up from a list may exceed 512 bytes, or it will forcibly not # match sieve_extlists_list_max_lookup_size = 512 # The default addressbook stored in a proxied dict sieve_extlists_list :addrbook:default { dict proxy { name = addressbook } } sieve_extlists_list tag:example.com,2025-02-26:BadFileExts { dict proxy { name = bad_file_extensions } # Limit lookups to 10 bytes max_lookup_size = 10B } ``` ## Sieve Example The following example excludes senders listed in the user's default address book from Spam filtering. The example demonstrates the use of the [spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) as well. ``` require ["envelope", "extlists", "fileinto", "spamtest", "relational", "comparator-i;ascii-numeric"]; if allof( not envelope :list "from" ":addrbook:default", spamtest :value "ge" :comparator "i;ascii-numeric" "3" ) { fileinto "spam"; } ``` # Sieve: Include Extension The Sieve include extension ([RFC 6609](https://datatracker.ietf.org/doc/html/rfc6609)) permits users to include one Sieve script into another. This can make managing large scripts or multiple sets of scripts much easier, and allows a site and its users to build up libraries of scripts. Users are able to include their own personal scripts or site-wide scripts. Included scripts can include more scripts of their own, yielding a tree of included scripts with the main script (typically the user's personal script) at its root. ## Configuration The include extension is available by default. ### Settings #### `sieve_include_max_includes` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Advanced Setting; this should not normally be changed. | The maximum number of scripts that may be included. This is the total number of scripts involved in the include tree. #### `sieve_include_max_nesting_depth` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Advanced Setting; this should not normally be changed. | The maximum nesting depth for the include tree. # Sieve: Spamtest and Virustest Extensions Using the spamtest and virustest extensions ([RFC 5235](https://datatracker.ietf.org/doc/html/rfc5235)), the Sieve language provides a uniform and standardized command interface for evaluating spam and virus tests performed on the message. Users no longer need to know what headers need to be checked and how the scanner's verdict is represented in the header field value. They only need to know how to use the spamtest (spamtestplus) and virustest extensions. This also gives GUI-based Sieve editors the means to provide a portable and easy to install interface for spam and virus filter configuration. The burden of specifying which headers need to be checked and how the scanner output is represented falls onto the Sieve administrator. ## Configuration The spamtest, spamtestplus, and virustest extensions are not enabled by default and thus need to be enabled explicitly using `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. ### Settings: Spamtest #### `sieve_spamtest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. The syntax is identical to the `[sieve_spamtest_status_header](/main/core/summaries/settings.html#sieve_spamtest_status_header)` setting. This setting cannot be used together with `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. #### `sieve_spamtest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_spamtest_score_max_header](/main/core/summaries/settings.html#sieve_spamtest_score_max_header)`. This setting can specify a fractional score with a decimal point. #### `sieve_spamtest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. #### `sieve_spamtest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_spamtest_text_value](/main/core/summaries/settings.html#sieve_spamtest_text_value)`). #### `sieve_spamtest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) When the `[sieve_spamtest_status_type](/main/core/summaries/settings.html#sieve_spamtest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_spamtest_status_header = X-Spam-Verdict sieve_spamtest_status_type = text sieve_spamtest_text_value { 1 = Not Spam 10 = Spam } ``` ### Settings: Virustest #### `sieve_virustest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value Format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. The syntax is identical to `[sieve_virustest_status_header](/main/core/summaries/settings.html#sieve_virustest_status_header)`. This setting cannot be used together with `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. #### `sieve_virustest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_virustest_score_max_header](/main/core/summaries/settings.html#sieve_virustest_score_max_header)`. This setting can specify a fractional score with a decimal point. #### `sieve_virustest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value Format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. #### `sieve_virustest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_virustest_text_value](/main/core/summaries/settings.html#sieve_virustest_text_value)`). #### `sieve_virustest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) When the `[sieve_virustest_status_type](/main/core/summaries/settings.html#sieve_virustest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_virustest_status_header = X-VirusCheck sieve_virustest_status_type = text sieve_virustest_text_value { 1 = Clean 2 = Presumed Clean 3 = Not sure 4 = Almost Certain 5 = Definitely } ``` ### Examples This section shows several configuration examples. Each example shows a specimen of valid virus/spam test headers that the given configuration will work on. Spam Headerdovecot.conf ``` X-Spam-Score: No, score=-3.2 ``` doveconf ``` sieve_extensions { spamtest = yes spamtestplus = yes } sieve_spamtest_status_type = score sieve_spamtest_status_header = \ X-Spam-Score: [[:alnum:]]+, score=(-?[[:digit:]]+\.[[:digit:]]) sieve_spamtest_score_max_value = 5.0 ``` Spam Headerdovecot.conf ``` X-Spam-Status: Yes ``` doveconf ``` sieve_extensions { spamtest = yes spamtestplus = yes } sieve_spamtest_status_type = text sieve_spamtest_status_header = X-Spam-Status sieve_spamtest_text_value { 1 = No 10 = Yes } ``` Spam Headerdovecot.confSpam HeaderVirus Headerdovecot.conf ``` X-Spam-Score: sssssss ``` doveconf ``` sieve_extensions { spamtest = yes spamtestplus = yes } sieve_spamtest_status_header = X-Spam-Score sieve_spamtest_status_type = strlen sieve_spamtest_score_max_value = 5 ``` Spam HeaderVirus Headerdovecot.conf ``` X-Spam-Score: status=3.2 required=5.0 ``` ``` X-Virus-Scan: Found to be clean. ``` doveconf ``` sieve_extensions { spamtest = yes spamtestplus = yes virustest = yes } sieve_spamtest_status_type = score sieve_spamtest_status_header = \ X-Spam-Score: score=(-?[[:digit:]]+\.[[:digit:]]).* sieve_spamtest_score_max_header = \ X-Spam-Score: score=-?[[:digit:]]+\.[[:digit:]] required=([[:digit:]]+\.[[:digit:]]) sieve_virustest_status_type = text sieve_virustest_status_header = X-Virus-Scan: Found to be (.+)\. sieve_virustest_text_value { 1 = clean 5 = infected } ``` # Sieve: Vacation Extension The Sieve vacation extension ([RFC 5230](https://datatracker.ietf.org/doc/html/rfc5230)) defines a mechanism to generate automatic replies to incoming email messages. It takes various precautions to make sure replies are only sent when appropriate. Script authors can specify how often replies can be sent to a particular contact. In the original vacation extension, this interval is specified in days with a minimum of one day. When more granularity is necessary and particularly when replies must be sent more frequently than one day, the vacation-seconds extension ([RFC 6131](https://datatracker.ietf.org/doc/html/rfc6131)) can be used. This allows specifying the minimum reply interval in seconds with a minimum of zero (a reply is then always sent), depending on administrator configuration. ## Configuration The vacation extension is available by default. In contrast, the vacation-seconds extension - which implies the vacation extension when used - is not available by default and needs to be enabled explicitly by adding it to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. The configuration also needs to be adjusted accordingly to allow a non-reply period of less than a day. ### Settings #### `sieve_vacation_check_recipient` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This setting determines whether the checks for implicit delivery are performed. If this is skipped, this means that the vacation command does not verify that the message is explicitly addressed at the recipient. Use this option with caution. Specifying `no` will violate the Sieve standards and can cause vacation replies to be sent for messages not directly addressed at the recipient. #### `sieve_vacation_default_period` **Default:** `7d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the default period that is used when no `:days` or `:seconds` tag is specified. The configured value must lie between `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)` and `[sieve_vacation_max_period](/main/core/summaries/settings.html#sieve_vacation_max_period)`. #### `sieve_vacation_max_period` **Default:** `60d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the maximum period that can be specified for the `:days` tag of the vacation command. The configured value must be larger than `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)`. A value of `0` has a special meaning: it indicates that there is no upper limit. #### `sieve_vacation_min_period` **Default:** `1d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the minimum period that can be specified for the `:days` and `:seconds` tags of the vacation command. A minimum of `0` indicates that users are allowed to make the Sieve interpreter send a vacation response message for every incoming message that meets the other reply criteria (refer to [RFC 5230](https://datatracker.ietf.org/doc/html/rfc5230)). A value of zero is not recommended. #### `sieve_vacation_send_from_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This setting determines whether vacation messages are sent with the SMTP `MAIL FROM` envelope address set to the recipient address of the Sieve script owner. Normally this is set to `<>`, which is the default as recommended in the specification. This is meant to prevent mail loops. However, there are situations for which a valid sender address is required and this setting can be used to accommodate for those. #### `sieve_vacation_use_original_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This specifies whether the original envelope recipient should be used in the check for implicit delivery. The vacation command checks headers of the incoming message, such as `To:` and `Cc:` for the address of the recipient, to verify that the message is explicitly addressed at the recipient. If the recipient address is not found, the vacation action will not trigger a response to prevent sending a reply when it is not appropriate. Normally only the final recipient address is used in this check. This setting allows including the original recipient specified in the SMTP session if available. This is useful to handle mail accounts with aliases. Use this option with caution: if you are using aliases that point to more than a single account, as senders can get multiple vacation responses for a single message. Use the [LDA](/main/core/config/delivery/lda.html) `-a` option or the LMTP/LDA `[lda_original_recipient_header](/main/core/summaries/settings.html#lda_original_recipient_header)` setting to make the original SMTP recipient available to Sieve. ### Auto-Reply The vacation extension uses envelope sender and envelope recipient. They're taken from: - **Envelope sender**: `-f` parameter to dovecot-lda if given, otherwise `Return-Path:` header in the message. - **Envelope recipient**: `-a` parameter to dovecot-lda if given, otherwise `-d` parameter to dovecot-lda. If neither is given (delivering to system users), the `$USER` environment is used. The vacation replies are sent to the envelope sender. List of autoreplied senders is stored in `.dovecot.lda-dupes` file in user's home directory. When you're testing the vacation feature, it's easy to forget that the reply is sent only once in the number of configured days. If you have problems getting the vacation reply, try deleting this file. If that didn't help, make sure the problem isn't related to sending mails in general by trying the "reject" Sieve command. The automatic replies aren't sent if any of the following is true: - The envelope sender is not available (equal to <>) - The envelope sender and envelope recipient are the same - The sender recently (within `:days` days; default `7`) got a reply from the same vacation command - The message contains at least one of the mailing list headers `list-id`, `list-owner`, `list-subscribe`, `list-post`, `list-unsubscribe`, `list-help`, or `list-archive` - Auto-Submitted: header exists with any value except `no` - Precedence: header exists with value `junk`, `bulk`, or `list` - The envelope sender is considered a system address, which either: - begins with `MAILER-DAEMON` (case-insensitive), - begins with `LISTSERV` (case-insensitive), - begins with `majordomo` (case-insensitive), - contains the string `-request` anywhere within it (case-sensitive), or - begins with `owner-` (case-sensitive) - The envelope recipient and alternative addresses specified with the vacation command's `:addresses` tag are not found in the message's `To:`, `Cc:`, `Bcc:`, `Resent-To:`, `Resent-Cc:`, or `Resent-Bcc:` fields. ### Example doveconf ``` # Use vacation-seconds sieve_extensions { vacation-seconds = yes } # One hour at minimum sieve_vacation_min_period = 1h # Ten days default sieve_vacation_default_period = 10d # Thirty days at maximum sieve_vacation_max_period = 30d ``` # Sieve: Variables Extension The Sieve variables extension ([RFC 5229](https://datatracker.ietf.org/doc/html/rfc5229)) adds the concept of variables to the Sieve language. ## Configuration The variables extension is available by default. ### Settings #### `sieve_variables_max_scope_count` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Advanced Setting; this should not normally be changed. | The maximum number of variables that can be declared in a scope. There are currently two variable scopes: the normal script scope and the global scope created by the [Sieve include extension](/main/core/config/sieve/extensions/include.html). The minimum value for this setting is `128`. #### `sieve_variables_max_value_size` | Key 1 | | --- | | Default | `4k` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Advanced Setting; this should not normally be changed. | The maximum allowed size for the value of a variable. If exceeded at runtime, the value is always truncated to the configured maximum. The minimum value for this setting is `4000 bytes`. # ManageSieve Server The ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) service is used to manage a user's [Sieve](/main/core/config/sieve/overview.html) script collection. It has the following advantages over doing it directly via filesystem: - No need to let users log in via FTP/SFTP/etc, which could be difficult especially with virtual users. - ManageSieve is a standard protocol ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)), so users can manage their scripts using (hopefully) user-friendly ManageSieve clients. Many webmails already include a ManageSieve client. - Scripts are compiled before they are installed, which guarantees that the uploaded script is valid. This prevents a user from inadvertently installing a broken Sieve script. ## Configuration WARNING If you have used the Sieve plugin before and you have `.dovecot.sieve` files in user directories, you are advised to **make a backup first**. Although the ManageSieve daemon takes care to move these files to the Sieve storage before it is substituted with a symbolic link, this is not a very well tested operation, meaning that there is a possibility that existing Sieve scripts get lost. The ManageSieve configuration consists of ManageSieve protocol settings and [Sieve](/main/core/config/sieve/overview.html)\-related settings. The Sieve interpreter settings are shared with settings of the [Sieve](/main/core/config/sieve/overview.html), for Dovecot's [LDA](/main/core/config/delivery/lda.html) and [LMTP Server](/main/core/config/delivery/lmtp.html). First, the ManageSieve protocol settings are outlined and then the relevant Sieve settings are described. ### Protocol Configuration Along with all other binaries that Dovecot uses, the `managesieve` and `managesieve-login` binaries are installed during `make install` of [Sieve installation](/main/installation/sieve.html). The only thing you need to do to activate the ManageSieve protocol support in Dovecot is to add `sieve` to the [service configuration (protocol)](/main/core/config/service.html#protocol). The managesieve daemon will listen on port 4190 by default. ### Settings As the implementation of the managesieve daemon is largely based on the original IMAP implementation, it is very similar in terms of configuration. In addition to most mail daemon config settings, the managesieve daemon accepts a few more. The following settings can be configured in the `protocol sieve` section: #### `managesieve_client_workarounds` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | Enables various workarounds for ManageSieve clients. Currently there are none. #### `managesieve_implementation_string` | Key 1 | | --- | | Default | `Dovecot Pigeonhole` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Sets the ManageSieve implementation string returned by the `IMPLEMENTATION` capability. #### `managesieve_logout_format` **Default:** `bytes=%{input}/%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) Specifies the string pattern used to compose the logout message of an authenticated session. The following substitutions are available: | Variable Name | Description | | --- | --- | | `%{input}` | Total number of bytes read from client | | `%{output}` | Total number of bytes sent to client | | `%{put_count}` | Number of scripts uploaded by client using PUTSCRIPT command | | `%{put_bytes}` | Number of bytes with script data sent by client using PUTSCRIPT command | | `%{get_count}` | Number of scripts downloaded by client using GETSCRIPT command | | `%{get_bytes}` | Number of bytes with script data sent to client using GETSCRIPT command | | `%{check_count}` | Number of scripts checked by client using CHECKSCRIPT command | | `%{check_bytes}` | Number of bytes with script data sent by client using CHECKSCRIPT command | | `%{deleted_count}` | Number of scripts deleted by client using DELETESCRIPT command | | `%{renamed_count}` | Number of scripts renamed by client using RENAMESCRIPT command | | `%{session}` | The client session ID | #### `managesieve_max_compile_errors` | Key 1 | | --- | | Default | `5` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | The maximum number of compile errors that are returned to the client upon script upload or script verification. #### `managesieve_max_line_length` | Key 1 | | --- | | Default | `64k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The maximum ManageSieve command line length in bytes. Since long command lines are very unlikely with ManageSieve, changing this will generally not be useful. #### `managesieve_notify_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `NOTIFY` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). #### `managesieve_sieve_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `SIEVE` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). ### Sieve Interpreter Configuration The part of the [Sieve](/main/core/config/sieve/overview.html) configuration that is relevant for ManageSieve mainly consists of the configuration of the [personal](/main/core/plugins/sieve.html#script-storage-type-personal) Sieve script storage type. Currently, only the [file driver](/main/core/plugins/sieve.html#file-storage-driver) Sieve script storage driver supports the management functions needed by Managesieve. For ManageSieve, the configuration of `[sieve_script_active_path](/main/core/summaries/settings.html#sieve_script_active_path)` is required. ### Quota Support By default, users can manage an unlimited number of Sieve scripts on the server through ManageSieve. However, ManageSieve can be configured to enforce limits on the number of personal Sieve scripts per user and/or the amount of disk storage used by these scripts. The maximum size of individual uploaded scripts is dictated by the configuration of the [Sieve](/main/core/config/sieve/overview.html). The limits are configured in the `plugin` section of the Dovecot configuration: #### `sieve_max_script_size` | Key 1 | | --- | | Default | `1M` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The maximum size of a Sieve script. The compiler will refuse to compile any script larger than this limit. If set to `0`, no limit on the script size is enforced. #### `sieve_quota_script_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of personal Sieve scripts a single user can have. ### Examples The following provides example configurations for ManageSieve in `dovecot.conf`. Only sections relevant to ManageSieve and the Sieve plugin are shown. doveconf ``` ... service managesieve-login { #inet_listener sieve { # port = 4190 #} #inet_listener sieve_deprecated { # port = 2000 #} # Number of connections to handle before starting a new process. Typically # the only useful values are "unlimited" or 1. 1 is more secure, but # "unlimited" is faster. #restart_request_count = 1 # Number of processes to always keep waiting for more connections. #process_min_avail = 0 } service managesieve { # Max. number of ManageSieve processes (connections) #process_limit = 1024 } # Service configuration protocol sieve { # Maximum ManageSieve command line length in bytes. ManageSieve usually does # not involve overly long command lines, so this setting will not normally # need adjustment #managesieve_max_line_length = 65536 # Maximum number of ManageSieve connections allowed for a user from each # IP address. # NOTE: The username is compared case-sensitively. #mail_max_userip_connections = 10 # List of plugins to load (none known to be useful so far). # Do NOT try to load IMAP plugins here. #mail_plugins { #} # MANAGESIEVE logout format string: # %i - total number of bytes read from client # %o - total number of bytes sent to client #managesieve_logout_format = bytes=%i/%o # To fool ManageSieve clients that are focused on CMU's timesieved you can # specify the IMPLEMENTATION capability that the dovecot reports to clients. # For example: 'Cyrus timsieved v2.2.13' #managesieve_implementation_string = Dovecot Pigeonhole # Explicitly specify the SIEVE and NOTIFY capability reported by the server # before login. If left unassigned these will be reported dynamically # according to what the Sieve interpreter supports by default (after login # this may differ depending on the user). #managesieve_sieve_capability = #managesieve_notify_capability = # The maximum number of compile errors that are returned to the client # upon script upload or script verification. #managesieve_max_compile_errors = 5 } # Used by both the Sieve plugin and the ManageSieve protocol sieve_script personal { path = ~/sieve active_path = ~/.dovecot.sieve } ``` ### Proxy Like Dovecot's imap server, the ManageSieve login daemon supports proxying to multiple backend servers. The [proxy passdb](/main/core/config/proxy/overview.html) page for POP3 and IMAP applies automatically to ManageSieve as well. ## Troubleshooting Like Dovecot itself, **the ManageSieve service always logs a detailed error message** if something goes wrong at the server (refer to [logging](/main/core/admin/logging.html) for more details): the logs are the first place to look if you suspect something is wrong. To get additional debug messages in your log file, you should set `[log_debug = category=sieve](/main/core/summaries/settings.html#log_debug)` in `dovecot.conf` (inside `protocol sieve {...}` if you want to enable this for ManageSieve only). If the client commits protocol violations or sends invalid scripts, an error response is provided to the client which is not necessarily logged on the server. A good ManageSieve client presents such error messages to the user. Keep in mind that the ManageSieve service only provides the Sieve _protocol_, which may be somewhat confusing. This protocol can only be used to _upload_ Sieve scripts and _activate_ them for execution. Performing the steps below therefore only verifies that this functionality is working and **not** whether Sieve scripts are correctly being executed upon delivery. The execution of Sieve scripts is performed by the [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) using the [sieve plugin](/main/core/plugins/sieve.html). If you have problems with Sieve script execution upon delivery, see [Sieve troubleshooting](/main/core/config/sieve/troubleshooting.html). ### Manual Login and Script Upload If you fail to login or upload scripts to the server, it is not necessarily caused by Dovecot or your configuration. It is often best to test your ManageSieve server manually first. This also provides you with the direct error messages from the server without intermission of your client. If you do not use TLS, you can connect using a simple `telnet` or `netcat` connection to the configured port (typically 4190 or 2000 for older setups). Otherwise you must use a TLS-capable text protocol client like `gnutls-cli` as described below. Upon connection, the server presents the initial greeting with its capabilities: ``` "IMPLEMENTATION" "dovecot" "SASL" "PLAIN" "SIEVE" "comparator-i;ascii-numeric fileinto reject vacation imapflags notify include envelope body relational regex subaddress copy" "STARTTLS" OK "Dovecot ready." ``` Note that the reported `STARTTLS` capability means that the server accepts TLS, but, since you are using telnet/netcat, you cannot use this (refer to Manual TLS Login below). The `SASL` capability lists the available SASL authentication mechanisms. If this list is empty and `STARTTLS` is available, it probably means that the server forces you to initiate TLS first (as dictated by `[auth_allow_cleartext = yes](/main/core/summaries/settings.html#auth_allow_cleartext)` in `dovecot.conf`). Now you need to log in. Although potentially multiple SASL mechanisms are available, only `PLAIN` is described here. Authentication is performed using the ManageSieve `AUTHENTICATE` command. This command typically looks as follows when the `PLAIN` mechanism is used: ``` AUTHENTICATE "PLAIN" "" ``` The credentials are the base64-encoded version of the string `"\0\0 ``` The command is written to stdout and you can paste this to your protocol session, e.g.: ``` AUTHENTICATE "PLAIN" "" OK "Logged in." ``` Now that you are logged in, you can upload a script. This is done using the `PUTSCRIPT` command. Its first argument is the name for the script and its second argument is a string literal. A string literal starts with a length specification `'{+}'` followed by a newline. Thereafter the server expects `` bytes of script data. The following uploads a trivial 6 byte long sieve script that keeps every message (6th byte is the newline character): ``` PUTSCRIPT "example" {6+} keep; OK "Putscript completed." ``` Upon successful upload, you should find a file called `example.sieve` in your sieve directory. The script should also be listed by the server as follows when the `LISTSCRIPTS` command is issued: ``` LISTSCRIPTS "example" OK "Listscripts completed." ``` You can check whether your script is uploaded correctly by downloading it using the `GETSCRIPT` command. This command accepts the name of the downloaded script as its only parameter: ``` GETSCRIPT "example" {6} keep; OK "Getscript completed." ``` To let the Sieve plugin use your newly uploaded script, you must activate it using the `SETACTIVE` command (only one script can be active at any time). The active script is indicated `ACTIVE` in the `LISTSCRIPTS` output, e.g.: ``` SETACTIVE "example" OK "Setactive completed." LISTSCRIPTS "example" ACTIVE OK "Listscripts completed. ``` The symbolic link configured with the `sieve` setting should now point to the activated script in the sieve directory. If no script is active, this symbolic link is absent. #### Manual TLS Login * * * When TLS needs to be used during manual testing, `gnutls-cli` provides the means to do so. This command-line utility is part of the GNUTLS distribution and on most systems this should be easy to install. It is used to connect to ManageSieve as follows: sh ``` gnutls-cli --starttls -p ``` This starts the client in plain text mode first. As shown in the previous section, the server presents a greeting with all capabilities of the server. If `STARTTLS` is listed, you can issue the `STARTTLS` command as follows: ``` STARTTLS OK "Begin TLS negotiation now." ``` If an OK response is given by the server you can press `Ctrl-D` to make `gnutls-cli` start the TLS negotiation. Upon pressing `Ctrl-D`, `gnutls-cli` will show information on the negotiated TLS session and finally the first response of the server is shown: ``` "IMPLEMENTATION" "dovecot" "SASL" "PLAIN" "SIEVE" "comparator-i;ascii-numeric fileinto reject vacation imapflags notify include envelope body relational regex subaddress copy" OK "TLS negotiation successful." ``` Hereafter, you can continue to authenticate and upload a script as described in the previous section. ### Client Problems See [rawlog](/main/core/admin/rawlog.html) for details how to log client-server traffic. Refer to the [client issues](#client-issues) for information on known client problems. ### Known Server Issues and Protocol Deviations - The ANONYMOUS authentication mechanism is currently not supported and explicitly denied. ## Client Issues Although this ManageSieve server should comply with the RFC specification of the ManageSieve protocol, quite a few clients don't. This page lists the known client problems. ### The TLS problem The core of the TLS problem is that a ManageSieve server is required to send an unsolicited CAPABILITY response right after successful TLS negotiation. Older Cyrus servers did not do this and many clients incorporated this protocol error as the standard, meaning that these do not expect the CAPABILITY response and thus fail with subsequent commands. However, now that Cyrus' Timsieved has changed its behaviour towards protocol compliance, all those clients will follow eventually. ### Smartsieve, Websieve These clients are specifically written for Cyrus timsieved and fail on multiple stages of the protocol when connected to Pigeonhole ManageSieve. See: - [https://sourceforge.net/projects/websieve/](https://sourceforge.net/projects/websieve/) - [https://github.com/secnextechnologies/WebSieve](https://github.com/secnextechnologies/WebSieve) - [https://smartsieve.sourceforge.net/](https://smartsieve.sourceforge.net/) ### Ruby/Managesieve Ruby command line client and library to managesieve works fine. See [https://rubygems.org/gems/ruby-managesieve/versions/0.4.0](https://rubygems.org/gems/ruby-managesieve/versions/0.4.0) ### Ruby/Sieve-Parser Ruby library for sieve parsing, see [https://rubygems.org/gems/sieve-parser/versions/0.0.4](https://rubygems.org/gems/sieve-parser/versions/0.0.4). # Sieve [Sieve](http://sieve.info/) support for Dovecot is provided by Pigeonhole, which allows users to filter incoming messages by writing scripts specified in the Sieve language ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)). Sieve support is provided as a plugin for Dovecot's [LDA](/main/core/config/delivery/lda.html) and [LMTP Server](/main/core/config/delivery/lmtp.html) services. The plugin implements a Sieve interpreter, which filters incoming messages using a script specified in the Sieve language. The Sieve script is provided by the user and, using that Sieve script, the user can customize how incoming messages are handled. Messages can be delivered to specific folders, forwarded, rejected, discarded, etc. ## Supported Features The Sieve language has various extensions. You can find more information about the extensions from the Sieve Mail Filtering Language Charter or the [Sieve.info wiki page](http://sieve.info/). INFO Standard Sieve does not support running external programs. However, Dovecot provides non-standard extensions that provide limited support for doing that. ### Extensions The interpreter recognizes the following Sieve extensions: | Extension | Default Enabled | Purpose | | --- | --- | --- | | `body` ([RFC 5173](https://datatracker.ietf.org/doc/html/rfc5173)) | **yes** | Allows evaluating the body of a message | | `copy` ([RFC 3894](https://datatracker.ietf.org/doc/html/rfc3894)) | **yes** | Allows storing and forwarding messages without canceling the implicit keep | | `date` ([RFC 5260 (section 4)](https://datatracker.ietf.org/doc/html/rfc5260#section-4)) | **yes** | Adds the ability to test date and time values in various ways | | `[duplicate](/main/core/config/sieve/extensions/duplicate.html)` ([RFC 7352](https://datatracker.ietf.org/doc/html/rfc7352)) | **yes** | Allows detecting duplicate message deliveries | | `[editheader](/main/core/config/sieve/extensions/editheader.html)` ([RFC 5293](https://datatracker.ietf.org/doc/html/rfc5293)) | no | Adds the ability to add and remove message header fields | | `encoded-character` ([RFC 5228 (section 2.4.2.4)](https://datatracker.ietf.org/doc/html/rfc5228#section-2.4.2.4)) | **yes** | Allows encoding special characters numerically | | `[enotify](/main/core/config/sieve/extensions/enotify.html)` ([RFC 5435](https://datatracker.ietf.org/doc/html/rfc5435)) | **yes** | Provides the ability to send notifications by various means (currently only mailto) | | `envelope` ([RFC 5228 (section 5.4)](https://datatracker.ietf.org/doc/html/rfc5228#section-5.4)) | **yes** | Allows evaluating envelope parts, i.e. sender and recipient | | `environment` ([RFC 5183](https://datatracker.ietf.org/doc/html/rfc5183)) | **yes** | Allows testing against various labeled values from the execution environment | | `[extlists](/main/core/config/sieve/extensions/extlists.html)` ([RFC 6134](https://datatracker.ietf.org/doc/html/rfc6134)) | no | Allows looking up and matching values from external lists (dict lookup) | | `extracttext` ([RFC 5703 (section 7)](https://datatracker.ietf.org/doc/html/rfc5703#section-7)) | **yes** | Allows extracting text from individual message MIME parts | | `fileinto` ([RFC 5228 (section 4.1)](https://datatracker.ietf.org/doc/html/rfc5228#section-4.1)) | **yes** | Allows storing messages in folders other than INBOX | | `foreverypart` ([RFC 5703 (section 3)](https://datatracker.ietf.org/doc/html/rfc5703#section-3)) | **yes** | Allows iterating through the message's MIME parts | | `ihave` ([RFC 5463](https://datatracker.ietf.org/doc/html/rfc5463)) | **yes** | Adds the ability to test for support of Sieve extensions and dynamically invoke their use | | `imap4flags` ([RFC 5232](https://datatracker.ietf.org/doc/html/rfc5232)) | **yes** | Allows adding IMAP flags to stored messages | | `imapsieve` ([RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)) | no ([imap-sieve plugin](/main/core/plugins/imap_sieve.html)) | Provides access to special environment items when executing at IMAP events | | `[include](/main/core/config/sieve/extensions/include.html)` ([RFC 6609](https://datatracker.ietf.org/doc/html/rfc6609)) | **yes** | Allows including other Sieve scripts | | `index` ([RFC 5260 (section 6)](https://datatracker.ietf.org/doc/html/rfc5260#section-6))) | **yes** | Allows matching specific header field instances by index | | `mailbox` ([RFC 5490 (section 3)](https://datatracker.ietf.org/doc/html/rfc5490#section-3)) | **yes** | Provides a mailbox existence check and allows creating mailboxes upon fileinto | | `mboxmetadata` ([RFC 5490](https://datatracker.ietf.org/doc/html/rfc5490)) | no | Provides access to mailbox METADATA entries | | `mime` ([RFC 5703 (section 4)](https://datatracker.ietf.org/doc/html/rfc5703#section-4)) | **yes** | Allows testing parts of structured MIME header fields | | `regex` ([Draft](https://tools.ietf.org/html/draft-murchison-sieve-regex-08)) | **yes** | Provides regular expression match support | | `reject` ([RFC 5429 (section 2.2)](https://datatracker.ietf.org/doc/html/rfc5429#section-2.2)) | **yes** | Allows rejecting messages with a rejection bounce message | | `relational` ([RFC 5231](https://datatracker.ietf.org/doc/html/rfc5231)) | **yes** | Provides relational match support | | `servermetadata` ([RFC 5490](https://datatracker.ietf.org/doc/html/rfc5490)) | no | Provides access to server METADATA entries | | `[spamtest](/main/core/config/sieve/extensions/spamtest_virustest.html)` ([RFC 5235](https://datatracker.ietf.org/doc/html/rfc5235)) | no | Implements a uniform way to test against headers added by spam filters | | `subaddress` ([RFC 5233](https://datatracker.ietf.org/doc/html/rfc5233)) | **yes** | Allows testing against delimited elements of the local part of addresses | | `[vacation](/main/core/config/sieve/extensions/vacation.html)` ([RFC 5230](https://datatracker.ietf.org/doc/html/rfc5230)) | **yes** | Provides auto-responder functionality, e.g. for when the user is on vacation | | `[vacation-seconds](/main/core/config/sieve/extensions/vacation.html)` ([RFC 6131](https://datatracker.ietf.org/doc/html/rfc6131)) | no | Extends vacation extension with the ability to send vacation responses with intervals of seconds rather than days | | `[variables](/main/core/config/sieve/extensions/variables.html)` ([RFC 5229](https://datatracker.ietf.org/doc/html/rfc5229)) | yes | Adds variables support to the language | | `[virustest](/main/core/config/sieve/extensions/spamtest_virustest.html)` ([RFC 5235](https://datatracker.ietf.org/doc/html/rfc5235)) | no | Implements a uniform way to test against headers added by virus scanners | ### Extensions (Dovecot) The interpreter recognizes the following Dovecot-specific Sieve extensions: | Extension | Default Enabled | Purpose | | --- | --- | --- | | [`vnd.dovecot.debug`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-debug.txt) | no | Allows logging debug messages | | [`vnd.dovecot.environment`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-dovecot-environment.txt) | no | Extends the standard "environment" extension with extra items and a variables namespace for direct access | | [`vnd.dovecot.execute`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-extprograms.txt) | no ([sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html)) | Implements executing a pre-defined set of external programs with the option to process string data through the external program | | [`vnd.dovecot.filter`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-extprograms.txt) | no ([sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html)) | Implements filtering messages through a pre-defined set of external programs | | [`vnd.dovecot.pipe`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-extprograms.txt) | no ([sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html)) | Implements piping messages to a pre-defined set of external programs | | [`vnd.dovecot.report`](https://raw.githubusercontent.com/dovecot/pigeonhole/main/doc/rfc/spec-bosch-sieve-report.txt) | no | Implements sending Messaging Abuse Reporting Format (MARF) reports ([RFC 5965](https://datatracker.ietf.org/doc/html/rfc5965)) | ### Obsolete Extensions WARNING These extensions are obsolete and have been removed. | Extension | Status | Purpose | | --- | --- | --- | | `imapflags` ([obsolete draft](https://tools.ietf.org/html/draft-melnikov-sieve-imapflags-03)) | Removed: 2.4.0 | Old version of imap4flags (for backwards compatibility with CMU Sieve) | | `notify` ([obsolete draft](https://tools.ietf.org/html/draft-martin-sieve-notify-01)) | Removed: 2.4.0 | Old version of enotify (for backwards compatibility with CMU Sieve) | ## Configuration Basic configuration of the Sieve plugin can be found at [sieve plugin](/main/core/plugins/sieve.html). ## ManageSieve Server To give users the ability to upload their own Sieve scripts to your server, i.e. without the need for shell or FTP access, you can use the ManageSieve protocol. Dovecot provides a [ManageSieve](/main/core/config/sieve/managesieve.html) service to provide this protocol. ## Mailbox Names ### UTF7 vs. UTF8 Sieve uses UTF8 encoding for mailbox names, while IMAP uses modified UTF7. This means that non-ASCII characters contained in mailbox names are represented differently between IMAP and Sieve scripts. See [Sieve troubleshooting](/main/core/config/sieve/troubleshooting.html). ### Separators and Prefixes Regarding separators, you need to specify mailbox names in Sieve scripts the same way as IMAP clients see them. For example if you want to deliver mail to the "Customers" mailbox which exists under "Work" mailbox: ### Maildir Default Namespace with `[namespace_prefix = ""](/main/core/summaries/settings.html#namespace_prefix)`, `[namespace_separator = .](/main/core/summaries/settings.html#namespace_separator)`: ``` require "fileinto"; fileinto "Work.Customers"; ``` ### Courier Migration Namespace with `[namespace_prefix = INBOX.](/main/core/summaries/settings.html#namespace_prefix)`, `[namespace_separator = .](/main/core/summaries/settings.html#namespace_separator)`: ``` require "fileinto"; fileinto "INBOX.Work.Customers"; ``` ### mbox, dbox Default Namespace with `[namespace_prefix = ""](/main/core/summaries/settings.html#namespace_prefix)`, `[namespace_separator = /](/main/core/summaries/settings.html#namespace_separator)`: ``` require "fileinto"; fileinto "Work/Customers"; ``` ## Manually Compiling Sieve Scripts When the Sieve plugin executes a script for the first time (or after it has been changed), it is compiled and stored in binary form (byte code) to avoid compiling the script again for each subsequent mail delivery. The Pigeonhole Sieve implementation uses the `.svbin` extension to store compiled Sieve scripts (e.g. `.dovecot.svbin`). To store the binary, the plugin needs write access in the directory in which the script is located. A problem occurs when a global script is encountered by the plugin. For security reasons, global script directories are not supposed to be writable by the user. Therefore, the plugin cannot store the binary when the script is first compiled. Note that this doesn't mean that the old compiled version of the script is used when the binary cannot be written: it compiles and uses the current script version. The only real problem is that the plugin will not be able to update the binary on disk, meaning that the global script needs to be recompiled each time it needs to be executed, i.e. for every incoming message, which is inefficient. To mitigate this problem, the administrator must manually pre-compile global scripts using the `sievec` command line tool. For example: sh ``` sievec /var/lib/dovecot/sieve/global/ ``` This is necessary for script in storages with [after](/main/core/plugins/sieve.html#script-storage-type-after), [before](/main/core/plugins/sieve.html#script-storage-type-before), [default](/main/core/plugins/sieve.html#script-storage-type-default), and [global](/main/core/plugins/sieve.html#script-storage-type-global) storage type. For global scripts that are only included in other scripts using the Sieve include extension (from the [personal](/main/core/plugins/sieve.html#script-storage-type-personal) and [global](/main/core/plugins/sieve.html#script-storage-type-global) storage types), this step is not necessary since included scripts are incorporated into the binary produced for the main script. ## Compile and Runtime Logging Log messages produced during script compilation or during script execution are written to two locations by the LDA Sieve plugin: - If the user's personal storage is using the [file driver](/main/core/plugins/sieve.html#file-storage-driver), a log file is written in the same directory as the user's active personal script as defined by `[sieve_script_active_path](/main/core/summaries/settings.html#sieve_script_active_path)`. This log file bears the name of that script file appended with ".log", e.g. `.dovecot.sieve.log`. Alternatively, e.g. when using another storage driver, `[sieve_user_log_path](/main/core/summaries/settings.html#sieve_user_log_path)` can be used to configure the log file explicitly. If there are errors or warnings in the script, the messages are appended to that log file until it eventually grows too large (>10 kB currently). When that happens, the old log file is moved to a ".log.0" file and an empty log file is started. Informational messages are not written to this log file and the log file is not created until messages are actually logged, i.e. when an error or warning is produced. The log file name can be overridden with `[sieve_user_log_path](/main/core/summaries/settings.html#sieve_user_log_path)`. - Messages that could be of interest to the system administrator are also written to the Dovecot logging facility (usually syslog). This includes informational messages that indicate what actions are executed on incoming messages. Compile errors encountered in the user's private script are not logged here. # Configuring Sieve & Submission ## `postmaster_address` `postmaster_address = postmaster@%{user | domain}` Email address to use in the From: field for outgoing email rejections. The `%{user | domain}` variable expands to the recipient domain. ### Domain (`%{user | domain}`) is Empty IMAP or POP3 protocol doesn't have explicit support for domains. The usernames are commonly in `user@domain` format, and that is also where Dovecot gets the domain from. If the username doesn't have `@domain`, then the domain is also usually empty (unless `[auth_default_domain](/main/core/summaries/settings.html#auth_default_domain)` is used). If you login as `user@domain`, but the %{user | domain} is still empty, the problem is that your configuration lost the domain part by changing the username. Dovecot doesn't keep track of the domain separately from username, so if something changes username from `user@domain` to just plain `user`, the domain is lost and %{user | domain} returns nothing. If you have `[auth_debug = yes](/main/core/summaries/settings.html#auth_debug)`, this shows up in logs like: `Info: auth(user@domain.org): username changed user@domain.org -> user`. Below are some of the most common reasons for this. #### Settings `[auth_username_format](/main/core/summaries/settings.html#auth_username_format)` changes the username permanently when used globally. If used inside [passdb](/main/core/config/auth/passdb.html) or [userdb](/main/core/config/auth/userdb.html), it changes the username only for the duration of the lookup. See also [Virtual and System Users](/main/core/config/auth/users/virtual.html#virtual-and-system-users). #### SQL `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)` gets often misconfigured to drop the domain if username and domain are stored separately. For example: doveconf ``` # BROKEN: passdb sql { query = SELECT username AS user, password \ FROM users \ WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` The "username AS user" changes the username permanently and the domain is dropped. You can instead use: doveconf ``` # MySQL: passdb sql { query = SELECT concat(username, '@', domain) AS user, password \ FROM users \ WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` Or you can return username and domain fields separately and Dovecot will merge them into a single user field: doveconf ``` passdb sql { query = SELECT username, domain, password \ FROM users \ WHERE username = '%{user | username}' AND domain = '%{user | domain}' } ``` ## `submission_host` `submission_host = smtp-out.example.com:25` SMTP server which is used for sending email rejects, Sieve forwards, vacations, etc. Alternatively, `sendmail_path` setting can be used to send mails using the sendmail binary. # Sieve Troubleshooting This page explains how to approach problems with Sieve. ## Common Problems Common configuration problems and their solutions are described here. ### Sieve Scripts are not Executed When Sieve scripts are not being executed, there are several possibilities: #### Your MTA is not using Dovecot LDA or LMTP Sieve scripts are executed by the Dovecot [LDA](/main/core/config/delivery/lda.html) and/or the Dovecot [LMTP Server](/main/core/config/delivery/lmtp.html) service. Thus, you first need to check whether LDA or LMTP are actually being used. At least one of these is supposed to be called/accessed from your [MTA](/main/core/config/delivery/mta.html) e.g. Exim or Postfix, for local message delivery. Most MTAs have their own local delivery agent, and without explicit configuration this is what is used. In that case, your Sieve scripts are simply ignored. When you set `[log_debug = category=sieve](/main/core/summaries/settings.html#log_debug)` in your configuration, your logs will show details of LDA and/or LMTP execution. The following is an example of the first few log lines of an LDA delivery: ``` dovecot: lda: Debug: Loading modules from directory: /usr/lib/dovecot/modules dovecot: lda: Debug: Module loaded: /usr/lib/dovecot/modules/lib90_sieve_plugin.so dovecot: lda(hendrik): Debug: Effective uid=1000, gid=1000, home=/home/hendrik dovecot: lda(hendrik): Debug: Namespace inbox: type=private, prefix=, sep=, inbox=yes, hidden=no, list=yes, subscriptions=yes location= ``` The first lines show that LDA has found and loaded the Sieve plugin module. Then it shows for what user it is delivering and where his INBOX is located. LMTP produces similar log lines. If you don't see lines such as the above, your MTA is probably not using Dovecot for local delivery. You can verify whether Dovecot is working correctly by executing `dovecot-lda` manually. #### The Sieve plugin is not Enabled The Dovecot [LDA](/main/core/config/delivery/lda.html) and [LMTP Server](/main/core/config/delivery/lmtp.html) services do not provide Sieve support by default. Sieve support is provided as a separate plugin that needs to be enabled by adding it to `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` in the `protocol lda {...}` section for the LDA and the `protocol lmtp {...}` section for LMTP. If this is omitted, Sieve scripts are ignored. See [sieve plugin](/main/core/plugins/sieve.html). Without actually running LDA, you can also check if executing `doveconf -f service=lda mail_plugins` includes "sieve". #### The Sieve plugin is misconfigured or the involved Sieve scripts contain errors If there is a configuration error or when a Sieve script cannot be compiled and executed, an error is always logged. ### Mailbox Names with non-ASCII Characters Cause Problems This problem most often manifests with the following error message: ``` error: msgid=<234234.234234@example.com>: failed to store into mailbox 'INBOX/Co&APY-rdineren' (INBOX/Co&-APY-rdineren): Mailbox doesn't exist: INBOX.Co&-APY-rdineren. ``` The Sieve script causing this error contained the following command: ``` fileinto "INBOX/Co&APY-rdineren"; ``` The specified mailbox contains the non-ASCII character 'ö'. Unfortunately, the author of this script used the wrong encoding. This is modified UTF-7 such as used by IMAP. However, Sieve expects UTF-8 for mailbox names. Depending on version and configuration, Dovecot uses modified UTF-7 internally. The Sieve interpreter expects UTF-8 and converts that to UTF-7 when necessary. When the mailbox is encoded in UTF-7 by the user, the '&' will just be escaped into '&-' during the UTF-7 conversion, yielding an erroneous mailbox name for Dovecot. That is what causes the error message presented above. Instead, the `fileinto` command should have looked as follows: ``` fileinto "INBOX/Coördineren"; ``` The old CMUSieve plugin did use UTF-7 for folder names. Therefore, this problem could have emerged after migrating from CMUSieve to Pigeonhole. # Spam Reporting TIP This page describes the recommended way of implementing spam/ham reporting within Dovecot. This is not the only possible solution, but matches the behavior of many clients and is entirely self-contained within the server. Spam/not-spam reporting within Dovecot (IMAP) can be handled by the user action of moving a message into (or out of) a defined Spam mailbox. ## Configuration Spam reporting messages are handled via [imap-sieve plugin](/main/core/plugins/imap_sieve.html). A global configuration script is used to capture the event of moving messages in/out of the Spam mailbox. WARNING **You cannot run shell scripts anywhere you want.** Sieve allows you to only run scripts under `[sieve_pipe_bin_dir](/main/core/summaries/settings.html#sieve_pipe_bin_dir)`. You can't use `/usr/local/bin/my-sieve-filter.sh`, you have to put the script under `sieve_pipe_bin_dir` and use `my-sieve-filter.sh` in the script instead. The Spam mailbox is defined and reported to the MUA via a Special-Use flag. ### External Reporting In this setup, the sieve scripts the script send the reported message using [RFC 5965](https://datatracker.ietf.org/doc/html/rfc5965) compliant spam reporting format to an external reporting e-mail address, using the [report extension](https://raw.githubusercontent.com/dovecot/pigeonhole/master/doc/rfc/spec-bosch-sieve-report.txt). #### Configuration dovecot.conf/etc/dovecot/sieve/report-spam.sieve/etc/dovecot/sieve/report-ham.sieve doveconf ``` # Display \Junk special-flag for Spam mailbox namespace inbox { mailbox Spam { auto = create special_use = \Junk } } # Setup actions based on message movement protocol imap { mail_plugins { imap_sieve = yes } } sieve_plugins { sieve_imapsieve = yes } sieve_global_extensions { vnd.dovecot.report = yes } mailbox Spam { # From elsewhere to Spam folder sieve_script report-spam { type = before cause = copy path = /etc/dovecot/sieve/report-spam.sieve } } # From Spam folder to elsewhere imapsieve_from Spam { sieve_script report-ham { type = before cause = copy path = /etc/dovecot/sieve/report-ham.sieve } } # Needed to send message to external mail server submission_host = 127.0.0.1:587 ``` ``` require "vnd.dovecot.report"; report "abuse" "User added this message to the Spam folder." "spam-report@example.com"; ``` ``` require "vnd.dovecot.report"; require "environment"; require "imapsieve"; if environment "imap.mailbox" "Trash" { # Putting spam in Trash mailbox is not significant stop; } if environment "imap.mailbox" "Spam" { # Copying mail inside Spam mailbox is not significant stop; } report "not-spam" "User removed this message from the Spam folder." "ham-report@example.com"; ``` ### Local Reporting In this setup, the sieve scripts pass the reported message to local binaries to do classification. #### Caveats and Possible Pitfalls - INBOX name is case-sensitive - [imap-sieve plugin](/main/core/plugins/imap_sieve.html) will **only** apply to IMAP. It **will not** apply to LDA or LMTP. Use [Sieve](/main/core/config/sieve/overview.html) normally for LDA/LMTP. - With this configuration, moving mails will slow down due to learn being done per email. If you want to avoid this, you need to think of something else. Probably piping things into a FIFO or perhaps using a socket based worker might work better. - Please read [Sieve](/main/core/config/sieve/overview.html) and [Sieve configuration](/main/core/plugins/sieve.html#configuration) to understand sieve configuration better. - Please read [Sieve plugins](/main/core/config/sieve/overview.html#extensions) for more information about sieve extensions. - If you run SpamAssassin trough Amavis and you use a virtual users setup, you should instead configure SpamAssassin to use MySQL/PostgreSQL as a backend, unless you want a headache with file permissions and lock files. See: [https://docs.iredmail.org/store.spamassassin.bayes.in.sql.html](https://docs.iredmail.org/store.spamassassin.bayes.in.sql.html). In this case, the `-u` parameter passed to `sa-learn` (and the relevant sieve variables) is obsolete and can be safely removed. - Reloading Dovecot doesn't activate changes in this configuration, you'll need to perform a full restart. #### Configuration dovecot.conf/etc/dovecot/sieve/report-spam.sieve/etc/dovecot/sieve/report-ham.sieve doveconf ``` # Display \Junk special-flag for Spam mailbox namespace inbox { mailbox Spam { auto = create special_use = \Junk } } # Setup actions based on message movement protocol imap { mail_plugins { imap_sieve = yes } } sieve_plugins { sieve_imapsieve = yes sieve_extprograms = yes } sieve_global_extensions { vnd.dovecot.pipe = yes vnd.dovecot.environment = yes } sieve_pipe_bin_dir = /usr/lib/dovecot/sieve mailbox Spam { # From elsewhere to Spam folder sieve_script report-spam { type = before cause = copy path = /etc/dovecot/sieve/report-spam.sieve } } # From Spam folder to elsewhere imapsieve_from Spam { sieve_script report-ham { type = before cause = copy path = /etc/dovecot/sieve/report-ham.sieve } } ``` ``` require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.user" "*" { set "username" "${1}"; } # "sa-learn-spam.sh" MUST live in /usr/lib/dovecot/sieve pipe :copy "sa-learn-spam.sh" [ "${username}" ]; ``` ``` require ["vnd.dovecot.pipe", "copy", "imapsieve", "environment", "variables"]; if environment :matches "imap.mailbox" "*" { set "mailbox" "${1}"; } if string "${mailbox}" "Trash" { stop; } if environment :matches "imap.user" "*" { set "username" "${1}"; } # "sa-learn-ham.sh" MUST live in /usr/lib/dovecot/sieve pipe :copy "sa-learn-ham.sh" [ "${username}" ]; ``` ### Shell Scripts #### SpamAssassin WARNING `spamc` interaction scripts are not tested yet. sa-learn-spam.shsa-learn-ham.sh sh ``` #!/bin/sh # you can also use tcp/ip here, consult spamc(1) exec /usr/bin/spamc -u "$1" -L spam ``` sh ``` #!/bin/sh # you can also use tcp/ip here, consult spamc(1) exec /usr/bin/spamc -u "$1" -L ham ``` You can also use sa-learn. Note that using sa-learn often incurs significant start-up time for every message. This can cause "lockout" of the user until all the processes sequentially complete, potentially tens of seconds or minutes. If spamd is being used and the administrator is willing to accept the potential security issues of allowing unauthenticated learning of spam/ham, spamd can be invoked with the `--allow-tell` option and spamc with the `--learntype=` option. Please consult the man pages of spamd and spamc for further details. sa-learn-spam.shsa-learn-ham.sh sh ``` #!/bin/sh exec /usr/bin/sa-learn -u "$1" --spam ``` sh ``` #!/bin/sh exec /usr/bin/sa-learn -u "$1" --ham ``` #### dspam sa-learn-spam.shsa-learn-ham.sh sh ``` #!/bin/sh exec /usr/bin/dspam --client --user "$1" --class=spam --source=error ``` sh ``` #!/bin/sh exec /usr/bin/dspam --client --user "$1" --class=innocent --source=error ``` **CRLF handling** dspam may fail to read the mail if it contains CRLF line endings. Add the **Broken lineStripping** option in dspam.conf if needed. #### rspamd By default, rspamd does global learning. If you want per-user classification, or something more complex, see [https://rspamd.com/doc/configuration/statistic.html](https://rspamd.com/doc/configuration/statistic.html) Alternative scripts can be found from [https://github.com/darix/dovecot-sieve-antispam-rspamd/](https://github.com/darix/dovecot-sieve-antispam-rspamd/) sa-learn-spam.shsa-learn-ham.sh sh ``` #!/bin/sh exec /usr/bin/rspamc -h /run/rspamd/worker-controller.socket -P learn_spam ``` sh ``` #!/bin/sh exec /usr/bin/rspamc -h /run/rspamd/worker-controller.socket -P learn_ham ``` Before running following commands, make sure `dovecot.conf` has all the sieve configuration you want. Then run following commands: sh ``` sievec /etc/dovecot/sieve/report-spam.sieve sievec /etc/dovecot/sieve/report-ham.sieve chmod +x /etc/dovecot/sieve/sa-learn-ham.sh /etc/dovecot/sieve/sa-learn-spam.sh ``` Now your learn scripts should be invoked when you move mails between folders. ## Debugging To debug, you need to import "vnd.dovecot.debug" extension. Then you can put in your Sieve script, when required (variables supported): ``` debug_log "something" ``` # SQL Driver: Cassandra Driver name `cassandra`. Driver for Apache Cassandra CQL server. To compile support for this driver, you need to have [DataStax C/C++ driver](https://docs.datastax.com/en/developer/cpp-driver/index.html) and headers installed. ## Settings ### `cassandra_connect_timeout` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Connection timeout. ### `cassandra_debug_queries` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to log CQL queries. ### `cassandra_delete_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when deleting from the database. See [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency). ### `cassandra_delete_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when deleting from the database fails with primary consistency. ### `cassandra_execution_retry_interval` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) If the driver supports speculative execution policy, configures constant speculative execution policy. See [https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative\_execution/index.html](https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative_execution/index.html) WARNING If Cassandra is completely unavailable and speculative execution is used, Cassandra cpp-driver library starts hanging all queries due to a bug. This may cause problems in the dict process even after Cassandra is back online. When this happens, "Dict server timeout" errors are being logged. ### `cassandra_execution_retry_times` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) If the driver supports speculative execution policy, configures constant speculative execution policy. See [https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative\_execution/index.html](https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative_execution/index.html) WARNING If Cassandra is completely unavailable and speculative execution is used, Cassandra cpp-driver library starts hanging all queries due to a bug. This may cause problems in the dict process even after Cassandra is back online. When this happens, "Dict server timeout" errors are being logged. ### `cassandra_heartbeat_interval` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) How often to send keepalive packets to cassandra nodes. ### `cassandra_hosts` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of hosts or IP addresses to connect. ### `cassandra_idle_timeout` **Default:** `0` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to idle before disconnecting. ### `cassandra_io_thread_count` **Default:** `_[driver dependent]_` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Set number of IO threads to handle query requests. ### `cassandra_keyspace` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specifies the keyspace name to use. ### `cassandra_latency_aware_routing` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) When turned on, latency-aware routing tracks the latency of queries to avoid sending new queries to poorly performing Cassandra nodes. ### `cassandra_log_level` **Default:** `warn` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `critical``error``warn``info``debug``trace` Driver log level. ### `cassandra_log_retries` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to log about failed requests that are retried (which may or may not succeed after the retry). ### `cassandra_metrics` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path where to write JSON metrics. See [Cassandra: Metrics](/main/core/config/sql/cassandra.html#metrics). ### `cassandra_page_size` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) When a query returns many rows, it can be sometimes inefficient to return them as a single response message. Instead, the driver can break the results into pages which get returned as they are needed. This setting controls the size of each page. Set to `0` to disable. ### `cassandra_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication. ### `cassandra_port` **Default:** `9042` **Value:** [Port Number](/main/core/settings/types.html#port-number) CQL port to use. ### `cassandra_protocol_version` **Default:** `depends on driver version` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `3``4``5` Cassandra protocol version to use. It is good idea to specify this to avoid warnings about version handshake if the driver supports a higher protocol version than the server. INFO If you want to use server-side prepared statements, you need to use at least `4`. ### `cassandra_read_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Read consistency. ### `cassandra_read_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Read consistency if primary consistency fails. ### `cassandra_request_timeout` **Default:** `60s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait for a query to finish. ### `cassandra_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``cert-only``cert-ip` **See Also:** - [SSL configuration](/main/core/config/ssl.html#configuration-overview) - `[ssl_client_require_valid_cert](/main/core/summaries/settings.html#ssl_client_require_valid_cert)` Whether to use SSL when connecting to Cassandra, and how to verify the certificate: `no` Don't use SSL `cert-only` Verify the certificate, but not the IP address or host name. `cert-ip` Verify the certificate, and require IP address to match the certificate's common name or one of its subject alternative names. You can also skip certificate validation by setting `[ssl_client_require_valid_cert = yes](/main/core/summaries/settings.html#ssl_client_require_valid_cert)`. The `cassandra_ssl` setting value must something else than `no`. Configure SSL certificates using the `ssl_client_*` settings. See [SSL configuration](/main/core/config/ssl.html#configuration-overview). ### `cassandra_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Username for authentication. ### `cassandra_warn_timeout` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Emit warning if query takes longer than this. ### `cassandra_write_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when updating or inserting to the database. ### `cassandra_write_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when updating or inserting to the database fails with primary consistency. ## SSL/TLS Settings Not all of Dovecot SSL settings are supported by the Cassandra library. Below is the list of supported settings: ### `ssl_client_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Password for the `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)`. ### `ssl_client_require_valid_cert` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Require a valid certificate when connecting to external SSL services? ## Consistency Consistency levels in Cassandra can be configured to manage availability versus data accuracy. ### Read Consistency #### `any` DANGER Not supported for reads. #### `local-serial` Allows reading the current (and possibly uncommitted) state of data without proposing a new addition or update. If a `SERIAL` read finds an uncommitted transaction in progress, it will commit the transaction as part of the read. Local serial is confined to datacenter. #### `serial` Allows reading the current (and possibly uncommitted) state of data without proposing a new addition or update. If a `SERIAL` read finds an uncommitted transaction in progress, it will commit the transaction as part of the read. #### `one` Returns a response from the closest replica, as determined by the snitch. #### `two` Returns the most recent data from two of the closest replicas. #### `three` Returns the most recent data from three of the closest replicas. #### `local-quorum` Returns the record after a quorum of replicas in the current datacenter as the coordinator has reported. #### `quorum` Returns the record after a quorum of replicas from all datacenters has responded. #### `each-quorum` DANGER Not supported for reads. #### `all` Returns the record after all replicas have responded. The read operation will fail if a replica does not respond. ### Write/Delete Consistency #### `any` At least one node must succeed in the operation. #### `local-serial` DANGER Not supported for writes. #### `serial` DANGER Not supported for writes. #### `one` Operation must be at least in commit log and one memory table of one replica. #### `two` Operation must be at least in commit log and one memory table of two replicas. #### `three` Operation must be at least in commit log and one memory table of three replicas. #### `local-quorum` A write must be written to the commit log and memory table on a quorum of replica nodes in the same datacenter as the coordinator. #### `quorum` A write must be written to the commit log and memory table on a quorum of replica nodes across all datacenters. #### `each-quorum` A write must be written to the commit log and memory table on a quorum of replica nodes in each datacenter. #### `all` A write must be written to the commit log and memtable on all replica nodes in the cluster for that partition. ## Metrics This describes the format of the JSON output produced when the metrics configuration option is activated. Source: [https://docs.datastax.com/en/developer/cpp-driver/latest/api/struct.CassMetrics/index.html](https://docs.datastax.com/en/developer/cpp-driver/latest/api/struct.CassMetrics/index.html) Details json ``` { "Requests": { # Minimum in microseconds "min": [Number: integer], # Maximum in microseconds "max": [Number: integer], # Mean in microseconds "mean": [Number: integer], # Standard deviation in microseconds "stddev": [Number: integer], # Median in microseconds "median": [Number: integer], # 75th percentile in microseconds "percentile_75th": [Number: integer], # 95th percentile in microseconds "percentile_95th": [Number: integer], # 98th percentile in microseconds "percentile_98th": [Number: integer], # 99th percentile in microseconds "percentile_99th": [Number: integer], # 99.9th percentile in microseconds "percentile_999th": [Number: integer], # Mean rate in requests per second "mean_rate": [Number: fraction], # 1 minute rate in requests per second "one_minute_rate": [Number: fraction], # 5 minute rate in requests per second "five_minute_rate": [Number: fraction], # 15 minute rate in requests per second "fifteen_minute_rate": [Number: fraction] }, "stats": { # The total number of connections "total_connections": [Number: integer], # The number of connections available to take requests "available_connections": [Number: integer], # Occurrences when requests exceeded a pool's water mark "exceeded_pending_requests_water_mark": [Number: integer], # Occurrences when number of bytes exceeded a connection's water mark "exceeded_write_bytes_water_mark": [Number: integer] }, "queries": { # Number of queries sent to Cassandra "sent": [Number: integer], # Number of successful responses "recv_ok": [Number: integer], # Number of requests that couldn’t be sent, because the local # Cassandra driver’s queue was full. "recv_err_queue_full": [Number: integer], # Number of requests that didn’t succeed because the Cassandra # driver couldn’t connect to the server. "recv_err_no_hosts": [Number: integer], # Number of requests that didn’t succeed because the Cassandra # driver timed out while waiting for response from server. "recv_err_client_timeout": [Number: integer], # Number of requests that didn’t succeed because the Cassandra # server reported a timeout communicating with other nodes. "recv_err_server_timeout": [Number: integer], # Number of requests which couldn’t succeed, because not enough # Cassandra nodes were available for the consistency level. "recv_err_server_unavailable": [Number: integer] # Number of requests which couldn’t succeed for other reasons. "recv_err_other": [Number: integer] }, "errors": { # Occurrences of a connection timeout "connection_timeouts": [Number: integer], # [No description provided] "pending_request_timeouts": [Number: integer], # Occurrences of requests that timed out waiting for a connection "request_timeouts": [Number: integer] } } ``` # SQL Driver: MySQL/MariaDB Driver name `mysql`. The mysql driver works with both MySQL and MariaDB. MariaDB is the recommended choice, as a community-governed, fully open-source drop-in replacement. To compile support for this driver, you need to have the MySQL or MariaDB client library and development headers installed (for example `libmysqlclient-dev`, or `libmariadb-dev` for MariaDB). ## Example Configuration See [MySQL/MariaDB authentication](/main/core/config/auth/databases/sql.html#mysql). ## Settings ### `mysql` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[mysql_host](/main/core/summaries/settings.html#mysql_host)` Creates a new MySQL/MariaDB connection. If more than one is specified, the connections are automatically used for load balancing and for failover. The filter name refers to the `[mysql_host](/main/core/summaries/settings.html#mysql_host)` setting. ### `mysql_client_flags` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Flags to use when connecting to the database, provided as base-10 number. See [https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html](https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html) ### `mysql_connect_timeout` **Default:** `5s` **Value:** [time](/main/core/settings/types.html#time) How long to wait for connection. ### `mysql_connection_limit` **Default:** `5` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of parallel connections. Currently MySQL queries are blocking, so only a single connection can be used in parallel. ### `mysql_dbname` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Database name to connect to. ### `mysql_host` **Default:** `localhost` **Value:** [string](/main/core/settings/types.html#string) Host or UNIX socket path to connect to. The `[mysql](/main/core/summaries/settings.html#mysql)` setting defaults to this. INFO MySQL drivers can default to using UNIX socket connection when host is `localhost` and port is `0` (default). To force it to use TCP connection, set `[mysql_host = 127.0.0.1](/main/core/summaries/settings.html#mysql_host)` or set `[mysql_port](/main/core/summaries/settings.html#mysql_port)` explicitly. ### `mysql_option_file` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) File to read for client library specific configuration. ### `mysql_option_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Section name to read from `[mysql_option_file](/main/core/summaries/settings.html#mysql_option_file)`. ### `mysql_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication. ### `mysql_port` **Default:** `0 (defaults to 3306 for TCP connections)` **Value:** [Port Number](/main/core/settings/types.html#port-number) Port to connect to. ### `mysql_read_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) Timeout when reading data from server. ### `mysql_ssl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [SSL](/main/core/config/ssl.html) Whether to use SSL when connecting to MySQL. Configure it using the `ssl_client_*` settings. See [SSL](/main/core/config/ssl.html). ### `mysql_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Username for authentication. ### `mysql_write_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) Timeout in seconds when writing data to server. ### `ssl_cipher_list` **Default:** `ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH (for ssl_server, empty for ssl_client)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` - `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The list of SSL ciphers to use for TLSv1.2 and below connections, in order of preference. Use `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` for TLSv1.3 connections. You do not need to edit this setting in order to disable specific SSL protocols; that is best done with `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` instead. This setting is used for both incoming and outgoing SSL connections. ### `ssl_client_ca_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The directory where trusted SSL CA certificates can be found. For example `/etc/ssl/certs`. These certificates are used only for outgoing SSL connections (e.g. with the imapc driver). For extra security you might want to point to a directory containing certificates only for the CAs that are actually needed for the server operation instead of all the root CAs. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_require_valid_cert` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Require a valid certificate when connecting to external SSL services? ## SSL/TLS Settings Not all of Dovecot SSL settings are supported by the MySQL library. Below is the list of supported settings: # SQL Driver: PostgreSQL Driver name is `postgresql`. To compile support for this driver, you need PostgreSQL client library and headers. ## Example Configuration See [PostgreSQL authentication](/main/core/config/auth/databases/sql.html#postgresql). ## Settings ### `pgsql` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[pgsql_host](/main/core/summaries/settings.html#pgsql_host)` Creates a new PostgreSQL connection. If more than one is specified, the connections are automatically used for load balancing and for failover. The filter name refers to the `[pgsql_host](/main/core/summaries/settings.html#pgsql_host)` setting. ### `pgsql_connection_limit` **Default:** `5` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of parallel connections. ### `pgsql_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pgsql](/main/core/summaries/settings.html#pgsql)` Host to connect to. The `[pgsql](/main/core/summaries/settings.html#pgsql)` setting defaults to this. ### `pgsql_parameters` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) List of key/value settings passed to PostgreSQL. See [https://www.postgresql.org/docs/current/libpq-connect.html](https://www.postgresql.org/docs/current/libpq-connect.html) for available parameters. # SQL Driver: SQLite Driver name is `sqlite`. To compile support for this driver. you need sqlite library and headers. ## Example Configuration See [Sqlite authentication](/main/core/config/auth/databases/sql.html#sqlite). ## Settings ### `sqlite_busy_timeout` | Key 1 | | --- | | Default | `1s` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.5 | | Advanced Setting; this should not normally be changed. | Time to wait for a locked sqlite database to become available before giving up with an `SQLITE_BUSY` error. Even in write-ahead logging mode (see `[sqlite_journal_mode](/main/core/summaries/settings.html#sqlite_journal_mode)`) only one writer runs at a time, so concurrent writers may need to wait. Increase this on slow storage where writes contend. ### `sqlite_journal_mode` **Default:** `wal` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `delete``wal` Allows using write-ahead logging mode for database. ### `sqlite_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to the sqlite database. ### `sqlite_readonly` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Specifies that this database is read-only and should not be attempted to be created or written to. ### `sqlite_synchronous` **Default:** `default` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `default``off``normal``full``extra` **Changes:** - Added: 2.4.3 Sets the synchronization mode for SQlite database. See [https://sqlite.org/pragma.html#pragma\_synchronous](https://sqlite.org/pragma.html#pragma_synchronous) for full explanation of values. This has no effect if database is opened in read-only mode. Setting special value `default` elides pragma call. # SSL/TLS Configuration ## Configuration Overview The most important SSL settings are: doveconf ``` ssl = yes # Preferred permissions: root:root 0444 ssl_server_cert_file = /etc/ssl/certs/dovecot.pem # Preferred permissions: root:root 0400 ssl_server_key_file = /etc/ssl/private/dovecot.pem ``` The certificate file can be world-readable, since it doesn't contain anything sensitive (in fact it's sent to each connecting SSL client). The key file's permissions should be restricted to only root (and possibly ssl-certs group or similar if your OS uses such). Dovecot opens both of these files while still running as root, so you don't need to give Dovecot any special permissions to read them (in fact: **do not give dovecot user any permissions to the key file**). Settings for the SSL certificate and SSL secret key files: doveconf ``` ssl_server_cert_file = /etc/dovecot/dovecot.crt ssl_server_key_file = /etc/dovecot/dovecot.key ``` It's possible to keep the certificate and the key both in the same file: doveconf ``` # Preferred permissions: root:root 0400 ssl_server_cert_file = /etc/ssl/dovecot.pem ssl_server_key_file = /etc/ssl/dovecot.pem ``` For using multiple SSL certificates, see [Multiple SSL Certificates](/main/core/config/ssl.html#multiple-ssl-certificates). TIP It's important to note that `ssl = yes` must be set globally if you require SSL for any protocol (or Dovecot will not listen on the SSL ports), which in turn requires that a certificate and key are specified globally even if you intend to specify certificates per protocol. ## How to Specify When SSL/TLS is Required There are a couple of different ways to specify when SSL/TLS is required: - `[ssl = no](/main/core/summaries/settings.html#ssl)`: SSL/TLS is completely disabled. - `[ssl = yes](/main/core/summaries/settings.html#ssl)` and `[auth_allow_cleartext = yes](/main/core/summaries/settings.html#auth_allow_cleartext)`: SSL/TLS is offered to the client, but the client isn't required to use it. The client is allowed to login with cleartext authentication even when SSL/TLS isn't enabled on the connection. This is insecure, because the cleartext password is exposed to the internet. - `[ssl = yes](/main/core/summaries/settings.html#ssl)` and `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)`: SSL/TLS is offered to the client, but the client isn't required to use it. The client isn't allowed to use cleartext authentication, unless SSL/TLS is enabled first. However, if non-cleartext authentication mechanisms are enabled they are still allowed even without SSL/TLS. Depending on how secure they are, the authentication is either fully secure or it could have some ways for it to be attacked. - `[ssl = required](/main/core/summaries/settings.html#ssl)`: SSL/TLS is always required, even if non-cleartext authentication mechanisms are used. Any attempt to authenticate before SSL/TLS is enabled will cause an authentication failure. Note that this setting is unrelated to the STARTTLS command - either implicit SSL/TLS or STARTTLS command is allowed. TIP If you have only cleartext mechanisms enabled (e.g. `[auth_mechanisms = plain login](/main/core/summaries/settings.html#auth_mechanisms)` and `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)`, `[ssl = yes](/main/core/summaries/settings.html#ssl)` and `[ssl = required](/main/core/summaries/settings.html#ssl)` are completely equivalent because in either case the authentication will fail unless SSL/TLS is enabled first. TIP With both `[ssl = yes](/main/core/summaries/settings.html#ssl)` and `[ssl = required](/main/core/summaries/settings.html#ssl)`, it's still possible that the client attempts to do a cleartext authentication before enabling SSL/TLS, which exposes the cleartext password to the internet. Dovecot attempts to indicate this to the IMAP clients via the LOGINDISABLED capability, but many clients still ignore it and send the password anyway. There is unfortunately no way for Dovecot to prevent this behavior. The POP3 standard doesn't have an equivalent capability at all, so the POP3 clients can't even know if the server would accept a cleartext authentication. - The main difference between `[ssl = required](/main/core/summaries/settings.html#ssl)` and `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)` is that if `[ssl = required](/main/core/summaries/settings.html#ssl)`, it guarantees that the entire connection is protected against eavesdropping (SSL/TLS encrypts the rest of the connection), while `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)` only guarantees that the password is protected against eavesdropping (SASL mechanism is encrypted, but no SSL/TLS is necessarily used). Nowadays you most likely should be using SSL/TLS anyway for the entire connection, since the cost of SSL/TLS is cheap enough. Using both SSL/TLS and non-cleartext authentication would be the ideal situation since it protects the cleartext password even against man-in-the-middle attacks. TIP The cleartext authentication mechanisms are always allowed (and SSL not required) for [secured connections](#secured-connections). ## Secured Connections The value of `[ssl](/main/core/summaries/settings.html#ssl)` influences whether a connection is considered "secure". - Dovecot-terminated TLS connections are always `secured`. - [HAProxy TLS Termination](/main/core/config/proxy/haproxy.html#tls-termination-at-haproxy) are always `secured`. - This is true even if HAProxy isn't running on the same server as Dovecot, and the connection between HAProxy and Dovecot isn't secured. The reasoning here is that this kind of a configuration is most likely intentional. If such connection wasn't treated `secured`, it would prevent using `ssl=required` to enforce end clients to use TLS. - Connections from a client to HAProxy running on localhost are always `secured`. - This is true even if HAProxy isn't running on the same server as Dovecot, and the connection between HAProxy and Dovecot isn't secured. The reasoning here is similar to above on why connections marked as TLS by HAProxy are treated as secured. - It doesn't matter whether a connection from HAProxy to Dovecot is on localhost. - Non-haproxy connections from localhost are always `secured`. - Other connections from `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` are `secured`, but only if `ssl` setting is not `required`. - Other connections from HAProxy are `secured`, but only if `ssl` setting is not `required`. Connections that are `secured` are always allowed to use plaintext authentication. Auth lookups will have the connection marked as `secured`, which also affects the `%{secured}` variable (see `[Settings variables](/main/core/settings/variables.html)`). ## Multiple SSL Certificates ### Different Certificates per Algorithm You can specify alternative SSL certificate that will be used if the algorithm differs from the primary certificate. This is useful when migrating to, e.g., ECDSA certificate. doveconf ``` ssl_server_alt_cert_file = /path/to/alternative/cert.pem ssl_server_alt_key_file = /path/to/alternative/key.pem ``` ### Different Certificates per IP and Protocol If you have multiple IPs available, this method is guaranteed to work with all clients. doveconf ``` # instead of IP you can also use hostname, which will be resolved local 192.0.2.10 { protocol imap { ssl_server_cert_file = /etc/ssl/dovecot/imap-01.example.com.cert.pem ssl_server_key_file = /etc/ssl/dovecot/imap-01.example.com.key.pem } protocol pop3 { ssl_server_cert_file = /etc/ssl/dovecot/pop-01.example.com.cert.pem ssl_server_key_file = /etc/ssl/dovecot/pop-01.example.com.key.pem } } local 192.0.2.20 { protocol imap { ssl_server_cert_file = /etc/ssl/dovecot/imap-02.example.com.cert.pem ssl_server_key_file = /etc/ssl/dovecot/imap-02.example.com.key.pem } protocol pop3 { ssl_server_cert_file = /etc/ssl/dovecot/pop-02.example.com.cert.pem ssl_server_key_file = /etc/ssl/dovecot/pop-02.example.com.key.pem } } ``` WARNING You will still need a top-level default `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` and `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)`, or you will receive errors. ### With Client TLS SNI (Server Name Indication) Support The SNI mechanism allows a server to present different hosts via a common connection, that the client can request by name. These are set up using the [local\_name](/main/core/settings/syntax.html#connection-filters) filter. It is important to note that having multiple SSL certificates per IP will not be compatible with all clients, especially mobile ones. It is a TLS SNI limitation. When using wildcard certificates, keep in mind that the `*` applies only to a single label. This is a general limitation of TLS, not Dovecot specifically. For example, `*.example.org` will match `mail.example.org` but not `int.mail.example.org`. Also note that the ordering of [local\_name](/main/core/settings/syntax.html#connection-filters) is important. The wildcard certificate must be listed first, followed by more specific names. Note that the last matching [local\_name](/main/core/settings/syntax.html#connection-filters) filter will be used. doveconf ``` local_name imap.example.org { ssl_server_cert_file = /etc/ssl/certs/imap.example.org.crt ssl_server_key_file = /etc/ssl/private/imap.example.org.key } local_name imap.example2.org { ssl_server_cert_file = /etc/ssl/certs/imap.example2.org.crt ssl_server_key_file = /etc/ssl/private/imap.example2.org.key } # ..etc.. ``` Added: 2.4.0: A server can reload different SSL certificates and other related settings using SNI (e.g. `[login_greeting](/main/core/summaries/settings.html#login_greeting)` or `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)`). The reloading of settings based on SNI is supported for IMAP, SMTP and LMTP. #### Client Support Clients confirmed working with TLS SNI: - Thunderbird (Linux) - [K-9 on Android](https://github.com/k9mail/k-9/pull/718) - Apple Mail (according to [Mail SSL SNI - cPanel](https://support.cpanel.net/hc/en-us/community/posts/19633051862807-Mail-SSL-SNI)) - [Mutt](https://gitlab.com/muttmua/trac-tickets/-/blob/master/tickets/closed/3923-mutt_may_need_to_support_TLSs_Server_Name_Indiciation_SNI_fo.txt?ref_type=heads) - [NeoMutt](https://www.neomutt.org/feature/tls-sni) Not working Clients: - Apple Mail (Mac OS X 10.10 and lower AND iOS 9.3 and lower) - Outlook for Mac version 15 (according to [Mail SSL SNI - cPanel](https://support.cpanel.net/hc/en-us/community/posts/19633051862807-Mail-SSL-SNI)) - Kindle Fire HD 8 - Outlook 2013 ## Password Protected Key Files SSL key files may be password protected. There are two ways to provide Dovecot with the password: 1. Starting Dovecot with `dovecot -p` asks the password. It's not stored anywhere, so this method prevents Dovecot from starting automatically at startup. 2. `[ssl_server_key_password](/main/core/summaries/settings.html#ssl_server_key_password)` setting. Note that `dovecot.conf` is by default world-readable, so you probably shouldn't place it there directly. Instead you could store it in a different file, such as `/etc/dovecot-private.conf` containing: doveconf ``` ssl_server_key_password = secret ``` and then use `!include_try /etc/dovecot-private.conf` in the main `dovecot.conf`. ## Chained SSL Certificates Put all the certificates in the `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)` in this order: 1. Dovecot's public certificate 2. First Intermediate Certificate 3. Second Intermediate Certificate Most CA providers these days provide a "full chain" certificate file, which contains the required certificates in correct order. You should use this. ## SSL security settings You can specify path to DH parameters file using: doveconf ``` ssl_server_dh_file = /path/to/dh.pem ``` This is fully optional, and most modern clients do not need this. To generate new parameters file, you can use: sh ``` # This might take a very long time. # Run it on a machine with sufficient entropy. openssl dhparam 4096 > dh.pem ``` By default Dovecot's allowed ciphers list contains: doveconf ``` ssl_cipher_list = ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH ``` Disallowing more won't really gain any security for those using better ciphers, but it does prevent people from accidentally using insecure ciphers. See [https://www.openssl.org/docs/manmaster/man1/ciphers.html](https://www.openssl.org/docs/manmaster/man1/ciphers.html) for a list of the ciphers. For TLSv1.3 server ciphers should not longer be preferred: doveconf ``` ssl_server_prefer_ciphers = client ``` ## SSL verbosity To make Dovecot log all the problems it sees with SSL connections: doveconf ``` log_debug = category=ssl ``` Some errors might be caused by dropped connections, so it could be quite noisy. ## Client Certificate Verification/Authentication If you want to require clients to present a valid SSL certificate, you'll need these settings: doveconf ``` ssl_server_ca_file = /etc/ssl/ca.pem ssl_server_request_client_cert = yes auth_ssl_require_client_cert = yes # if you want to get username from certificate as well, enable this #auth_ssl_username_from_cert = yes ``` The CA file should contain the certificate(s) followed by the matching CRL(s). TIP The CRLs are required to exist. For a multi-level CA place the certificates in this order: 1. Issuing CA cert 2. Issuing CA CRL 3. Intermediate CA cert 4. Intermediate CA CRL 5. Root CA cert 6. Root CA CRL The certificates and the CRLs have to be in PEM format. To convert a DER format CRL (e.g. [http://crl.cacert.org/class3-revoke.crl](http://crl.cacert.org/class3-revoke.crl)) into PEM format, use: sh ``` openssl crl -in class3-revoke.crl -inform DER -outform PEM > class3-revoke.pem ``` With the above settings, if a client connects which doesn't present a certificate signed by one of the CAs in the \[setting,ssl\_server\_ca\_file\]\], Dovecot won't let the user log in. This could present a problem if you're using Dovecot to provide SASL authentication for an MTA (such as Postfix) which is not capable of supplying client certificates for SASL authentication. If you need Dovecot to provide SASL authentication to an MTA without requiring client certificates and simultaneously provide IMAP service to clients while requiring client certificates, you can put `[auth_ssl_require_client_cert = yes](/main/core/summaries/settings.html#auth_ssl_require_client_cert)` inside of a protocol block as shown below to make an exemption for SMTP SASL clients (such as Postfix). doveconf ``` protocol !smtp { auth_ssl_require_client_cert=yes } ``` You may also force the username to be taken from the certificate by setting `[auth_ssl_username_from_cert = yes](/main/core/summaries/settings.html#auth_ssl_username_from_cert)`. - The text is looked up from subject DN's specified field using OpenSSL's `X509_NAME_get_text_by_NID()` function. - By default the `CommonName` field is used. - You can change the field with `[ssl_server_cert_username_field = name](/main/core/summaries/settings.html#ssl_server_cert_username_field)` setting (parsed using OpenSSL's `OBJ_txt2nid()` function). `x500UniqueIdentifier` is a common choice. You may also want to disable the password checking completely. Doing this currently circumvents Dovecot's security model so it's not recommended to use it, but it is possible by making the passdb allow logins using any password (typically requiring `nopassword` extra field to be returned). ## Testing Try out your new setup: sh ``` openssl s_client -servername mail.sample.com -connect mail.sample.com:pop3s ``` You should see something like this: ``` CONNECTED(00000003) depth=2 /O=Root CA/OU=http://www.cacert.org/CN=CA Cert Signing Authority/emailAddress=support@cacert.org verify error:num=19:self signed certificate in certificate chain verify return:0 --- Certificate chain 0 s:/CN=mail.example.com i:/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root 1 s:/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root i:/O=Root CA/OU=http://www.cacert.org/CN=CA Cert Signing Authority/emailAddress=support@cacert.org 2 s:/O=Root CA/OU=http://www.cacert.org/CN=CA Cert Signing Authority/emailAddress=support@cacert.org i:/O=Root CA/OU=http://www.cacert.org/CN=CA Cert Signing Authority/emailAddress=support@cacert.org --- Server certificate -----BEGIN CERTIFICATE----- MIIE1DCCArygAwIBAgIDAMBPMA0GCSqGSIb3DQEBBAUAMFQxFDASBgNVBAoTC0NB Y2VydCBJbmMuMR4wHAYDVQQLExVodHRwOi8vd3d3LkNBY2VydC5vcmcxHDAaBgNV BAMTE0NBY2VydCBDbGFzcyAzIFJvb3QwHhcNMTAxMjIwMTM1NDQ1WhcNMTIxMjE5 MTM1NDQ1WjAmMSQwIgYDjksadnjkasndjksandjksandjksandj5YXJlYS5vcmcw ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3jOX3FC8wVqnb2r65Sfvk cYUpJhlbhCfqPdN41c3WS0y1Jwwum1q4oMAJvdRnD5TMff1+fqTFy3lS1sYxIXiD kBRo478eNqzXHMpBOqbvKjYp/UZgWUNA9ebI1nQtwd7rnjmm/GrtyItjahCsgzDS qPAie+mXYzuT49ZoG+Glg7/R/jDcLMcJY0d5eJ7kufB1RLhvRitZD4FEbJVehqhY aevf5bLk1BNFhzRBfLXmv6u/kfvWf2HjGAf0aFhaQyiAldDgnZrvaZOFjkToJk27 p9MguvwGmbciao0DmMjcJhQ0smclFwy8Kj98Tz+nTkfAlU8jJdb1J/tIatJdpSRh AgMBAAGjgdwwgdkwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAgYI KwYBBQUHAwEGCWCGSAGG+EIEAQYKKwYBBAGCNwoDAzALBgNVHQ8EBAMCBaAwMwYI KwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhadodHRwOi8vb2NzcC5jYWNlcnQub3Jn LzBRBgNVknsadkjasnjdksandjksandjsnNlY3VyaXR5YXJlYS5vcmegKQYIKwYB BQUHCAWgHQwbbWFpbC5qb2ludC5zZWN1cml0eWFyZWEub3JnMA0GCSqGSIb3DQEB BQUAA4ICAQAX8ceObvUZNKYTlNQ/cv0BiA1XweRsVNca1ILACNLdVPR9mvf+aXCh ODkHaZAmGngj1DfD4fJsTbaydGWSPeVH91Qi9F+Pi6szhsxylI83NKbuXihcenuG twnte8aIb5FelVHttLQPSKRR62E8YmDWk3KYivuFAuZqDaGnWc5yeneTBpsGter/ 4awqsgymBK2YEg1HIWMPaRBvwzCVN/yUyWhFH9Nj11f/xgZE87VXrjLHWT/73i2Z S4uIZ2KHQUYuxMGldgpXm+QxFM8DGA6z1T1oPCVfW85cezlfr8QVvX6SXZrAUNL0 3D5YPzQuevW+5CrqnGA+F5ff4mBMl8R8Sg0+0LoLqt5PbpGyTt9vS1INZCdfvtIA /d7Ae7Xp9W8FVRqd7tvNMIy3ZA0/wNMDUczkhC/YtvHfMELpjtMJAGF15OtO7Vik V+FZnBP1Yd7760dtEmd6bF8vjcXCvDdxwGtcAehAUpIgAWvkHHOt8+H56tkFENAP /ZpJ+Wr+K3lxkkG+BN1bucxMuAdVyTpFyZfKDHRXIO/5e0hpPOaTO+obD3kifzdh yy7KmdKvDclHTiPuonJBzEXeM3JQBjcDHbMSyA6+38yBcso27h9VqCQJB2cZmSlW ArS/9wt2X21KgeuGHlTZ/8z9gXAjQKXhDYECWWd6LkWl98ZDBihslQ== -----END CERTIFICATE----- subject=/CN=mail.example.com issuer=/O=CAcert Inc./OU=http://www.CAcert.org/CN=CAcert Class 3 Root --- No client certificate CA names sent --- SSL handshake has read 5497 bytes and written 293 bytes --- New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA Server public key is 2048 bit Secure Renegotiation IS supported Compression: zlib compression Expansion: zlib compression SSL-Session: Protocol : TLSv1 Cipher : DHE-RSA-AES256-SHA Session-ID: 114A22BE4625B33F6893124ACF640AE0628B48B5039E90B3B9A20ADF7FA691F3 Session-ID-ctx: Master-Key: B8A55EC91A060575CFB29503FBF7160C2DC8BCBFE02D20A7F704882F72D8D00272D8D002CE5CCC4B94A492F43ED8F Key-Arg : None TLS session ticket: 0000 - 86 c7 46 63 a5 b6 48 74-16 d8 e0 a7 e2 64 e8 89 ..Fc..Ht.....d.. 0010 - 97 90 59 4b 57 f3 e2 b3-e2 d2 88 90 a8 aa b4 44 ..YKW..........D 0020 - ea 24 08 5e b4 14 7f e1-2a 1a 1c 40 ca 85 e7 41 .$.^....*..@...A 0030 - 9d 0d a8 4c f7 e3 db 1e-ef da 53 9c fe 43 cc 62 ...L......S..C.b 0040 - 79 b6 ad ea 9d cf ca b2-37 41 b7 0f ea 7d 59 e8 y.......7A...}Y. 0050 - 10 01 a0 eb dc c2 63 66-56 54 6a e8 3a 4b 93 49 ......cfVTj.:K.I 0060 - 77 da e4 4b 21 e8 30 7e-bf 10 91 3a 2c f9 59 80 w..K!.0~...:,.Y. 0070 - 01 1f 36 0b 92 85 67 55-c8 86 1d 44 b1 6f 0d ae ..6...gU...D.o.. 0080 - 15 36 b6 49 3a ef 94 9a-ef 6d 27 f0 80 20 43 09 .6.I:....m'.. C. 0090 - be 70 c5 30 15 3b 93 c6-c1 4c e9 7f 5c 34 98 dd .p.0.;...L..\4.. Compression: 1 (zlib compression) Start Time: 1292857721 Timeout : 300 (sec) Verify return code: 19 (self signed certificate in certificate chain) --- +OK Dovecot ready. ``` ### Testing CA The above test procedure returns: ``` Verify return code: 19 (self signed certificate in certificate chain) ``` which is expected result since test command omits option to verify CA root certificate. The following commands will enable CA root certificate validation. #### Testing CA On Debian On Debian derived distributions try: sh ``` openssl s_client -CApath /etc/ssl/certs -connect mail.sample.com:pop3s ``` #### Testing CA On RHEL On Red Hat Enterprise Linux derived distributions try: sh ``` openssl s_client -CAfile /etc/pki/tls/cert.pem -connect mail.sample.com:pop3s ``` ### Testing CA Success Verify return code: 0 (ok) ### Client Connections By default Dovecot uses OpenSSL's default system CAs to verify SSL certificates for outgoing connections. This can be overridden by specifying either `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` or `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)`. TIP Using `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` is preferred because it uses less memory. doveconf ``` ssl_client_ca_dir = /path/to/pem/certificates ssl_client_ca_file = /path/to/pem/bundle ``` ## JA3 Identifier Added: 2.4.0 Dovecot supports calculating [JA3 hash](https://engineering.salesforce.com/tls-fingerprinting-with-ja3-and-ja3s-247362855967) for checking client TLS implementation. This adds `ssl_ja3` and `ssl_ja3_hash` to `[Login variables](/main/core/settings/variables.html#login-variables)`, to be used with `[login_log_format_elements](/main/core/summaries/settings.html#login_log_format_elements)` and `ssl_ja3_hash` for `[Authentication variables](/main/core/settings/variables.html#authentication-variables)`, to be used with, e.g., [Authentication Policy](/main/core/config/auth/policy.html). To get JA3 values, you will need to use OpenSSL 1.1 or newer. Common JA3 hash databases usually use values provided by HTTP clients. Since IMAP, POP3 etc. do not currently use some of these extensions, you should not use these. They will not match. Some examples for demonstration purposes only. ``` Mutt 2.2.9, TLS1.3, GnuTLS ja3=771,4866-4867-4865-4868-49196-52393-49325-49162-49195-49324-49161-49200-52392-49172-49199-49171-157-49309-53-156-49308-47-159-52394-49311-57-158-49310-51,5-10-11-13-22-23-35-51-43-65281-0-45,23-24-25-29-30-256-257-258-259-260,0 ja3_hash=b7e9d913d85c071f5b806d59601e9b96 OpenSSL 1.1.1n, TLS1.3 ja3=771,4866-4867-4865-49196-49200-159-52393-52392-52394-49195-49199-158-49188-49192-107-49187-49191-103-49162-49172-57-49161-49171-51-157-156-61-60-53-47-255,11-10-35-22-23-13-43-45-51,29-23-30-25-24,0-1-2 ja3_hash=c34a54599a1fbaf1786aa6d633545a60 Thunderbird 102.4.2+build2-0ubuntu0.22.04.1, TLS1.3 ja3=771,4865-4867-4866-49195-49199-52393-52392-49196-49200-49162-49161-49171-49172-156-157-47-53,0-23-65281-10-11-35-5-51-43-13-45-21,29-23-24-25-256-257,0 ja3_hash=3ed71a458200f4af79031644408b8e58 ``` # Statistics See [All Dovecot Events](/main/core/summaries/events.html#all-dovecot-events) for list of all events that can be used in statistics. Dovecot supports gathering statistics from events (see [Events Design](/main/core/admin/events.html)). Currently there are no statistics logged by default, and therefore they must be explicitly added using the `metric` configuration blocks. The [`metric_filter`](#metric_filter) setting is the only required setting in a [`metric`](#metric) block. It specifies which events should be used when calculating the statistics for a given metric block. Event filtering is described in detail in [Event Filtering](/main/core/config/events/filter.html). Note that Dovecot also has many unnamed events. These aren't generally useful for statistics, but in some situations they may become visible in statistics. To avoid surprises, it's a good idea to always specify `event=name` in the filter setting. You can also use `event=*` to match all named events. ## Settings ### `metric` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[metric_name](/main/core/summaries/settings.html#metric_name)` Creates a new metric. The filter name refers to the `[metric_name](/main/core/summaries/settings.html#metric_name)` setting. ### `metric_description` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Human-readable description of the metric. This is included in the HELP text sent to OpenMetrics. ### `metric_exporter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Event Export](/main/core/config/events/export.html) - `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` Export events matching the filter with this [event exporter](/main/core/config/events/export.html). Refers to the `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` setting. If empty, the events are used only for statistics, and no exporting is done. ### `metric_exporter_include` **Default:** `name hostname timestamps categories fields` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Specifies which parts of the event are exported to the serialized event: | Values | Description | | --- | --- | | `name` | The name of the event. | | `hostname` | The name of the host generating this event. | | `timestamps` | The event start and end timestamps. | | `categories` | A set of categories associated with this event. | | `fields` | The fields associated with this event. The fields that will be exported are defined by the `[metric_fields](/main/core/summaries/settings.html#metric_fields)` setting. | For example: ``` metric example { exporter_include = name hostname timestamps } ``` includes just the 3 specified parts, while ``` metric another_example { exporter_include = } ``` includes nothing and the exported event will be empty (i.e. `{}` in JSON). ### `metric_fields` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Changed: 2.4.0 All listed fields are exported to OpenMetrics as well. A list of fields included in the metric. All events have a default `duration` field that does not need to be listed explicitly. ### `metric_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Metric Filters](/main/core/config/events/filter.html#metric-filter-syntax) [Event filter](/main/core/config/events/filter.html#metric-filter-syntax) that matches the events belonging to this metric. ### `metric_group_by` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Creates a new [group\_by](/main/core/config/statistics.html#group-by) for dynamically generating sub-metrics based on the specified field's values. The filter name refers to the `[metric_group_by_field](/main/core/summaries/settings.html#metric_group_by_field)`. ### `metric_group_by_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Generate sub-metrics based on this event field name. The `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)` filter name refers to this setting. WARNING Avoid high-cardinality fields. Each distinct value creates a sub-metric kept in memory with no upper limit, so fields like usernames, email addresses, remote IPs, message IDs, or mailbox GUIDs will cause unbounded memory growth. Use low-cardinality fields, or reduce cardinality with the `exponential`/`linear` aggregation methods or `[metric_group_by_method_discrete_modifier](/main/core/summaries/settings.html#metric_group_by_method_discrete_modifier)`. ### `metric_group_by_method` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures `[metric_group_by_method_method = the aggregation method](/main/core/summaries/settings.html#metric_group_by_method_method)` for the `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)`. Only a single method can be specified for a group\_by. The filter name refers to the `[metric_group_by_method_method](/main/core/summaries/settings.html#metric_group_by_method_method)`. ### `metric_group_by_method_discrete_modifier` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures a modifier string for values grouped by the [discrete method](/main/core/config/statistics.html#discrete). [%variables](/main/core/settings/variables.html) and their functions can be used: `%{value}` The original value. `%{user | domain}` If the value is in `user@domain` format, this contains the `domain` text. Otherwise empty. ### `metric_group_by_method_exponential_base` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_min_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)` - `[metric_group_by_method_exponential_max_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` Configures the base for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). Only `2` and `10` are supported. ### `metric_group_by_method_exponential_max_magnitude` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_min_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)` - `[metric_group_by_method_exponential_base](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)` Configures the maximum magnitude for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). ### `metric_group_by_method_exponential_min_magnitude` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_max_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` - `[metric_group_by_method_exponential_base](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)` Configures the minimum magnitude for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). ### `metric_group_by_method_linear_max` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_min](/main/core/summaries/settings.html#metric_group_by_method_linear_min)` - `[metric_group_by_method_linear_step](/main/core/summaries/settings.html#metric_group_by_method_linear_step)` Configures the maximum for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_linear_min` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_max](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` - `[metric_group_by_method_linear_step](/main/core/summaries/settings.html#metric_group_by_method_linear_step)` Configures the minimum for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_linear_step` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_min](/main/core/summaries/settings.html#metric_group_by_method_linear_min)` - `[metric_group_by_method_linear_max](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` Configures the step for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_method` **Default:** `discrete` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `discrete``exponential``linear` **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures the aggregation method for the `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)`. The `[metric_group_by_method](/main/core/summaries/settings.html#metric_group_by_method)` filter name refers to this setting. ### `metric_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the metric. It is visible in statistics outputs. The `[metric](/main/core/summaries/settings.html#metric)` filter name refers to this setting. ## Group By The `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)` setting allows dynamic hierarchical metric generation based on event fields' values. Each listed `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)` generates one level of "sub-metrics". These automatically generated metrics are indistinguishable from those statically defined in the config file. "sub-metric" names can be up to 256 bytes in total. WARNING Do not group by high-cardinality fields. Each distinct value observed creates a new sub-metric that is kept in memory for the lifetime of the stats process, and there is no upper limit on how many sub-metrics can be retained. Grouping by fields such as usernames, email addresses, remote IPs, message IDs, or mailbox GUIDs will cause unbounded memory growth. Prefer low-cardinality fields (e.g., IMAP command name, reply status), or quantize numeric fields with the [exponential](/main/core/config/statistics.html#exponential) or [linear](/main/core/config/statistics.html#linear) aggregation method. For `discrete`, use `[metric_group_by_method_discrete_modifier](/main/core/summaries/settings.html#metric_group_by_method_discrete_modifier)` to reduce cardinality (e.g., `%{value | domain}` to group by domain instead of per-user). Dovecot supports a number of aggregation methods that can be used to quantize a field's value before it is used to generate a metric. ### `discrete` The simplest aggregation method is to use the value as is. Because this is a very common use case, this is the default aggregation method. The value can be further modified by `[metric_group_by_method_discrete_modifier](/main/core/summaries/settings.html#metric_group_by_method_discrete_modifier)`. Added: 2.4.0 An additional parameter can be added to provide modifiers to the discrete value. This is done as [%variables](/main/core/settings/variables.html) and their modifiers. The following variables are provided: - `%{value}` - The original value - `%{user | domain}` - Text after the `@` character, or empty string if there is no `@`. Example: doveconf ``` metric imap_command { filter = event=imap_command_finished group_by cmd_name { } group_by tagged_reply_state { } } metric login_domains { filter = event=auth_request_finished fields = user group_by user { method discrete { modifier = %{value | domain | lower} } } } ``` This example configuration will generate statistics for each IMAP command. The first "sub-metric" level is based on the IMAP command name, and the second (and in this example final) level is based on the tagged reply. For example, a `SELECT` IMAP command that succeeded (in other words, it had an `OK` reply) will generate the metric `imap_command_SELECT_ok`. In addition to the final level metric, all intermediate level metrics are generated as well. For example, the same `SELECT` IMAP command will generate all of the following metrics: - `imap_command` - `imap_command_SELECT` - `imap_command_SELECT_ok` Note: While the top level metrics (e.g., `imap_command` above) are generated at start up, all `group_by` metrics are generated dynamically when first observed. ### `exponential` The field's integer value is quantized into exponentially sized ranges. The exponential aggregation method uses three settings: - `[metric_group_by_method_exponential_min_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)` - `[metric_group_by_method_exponential_max_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` - `[metric_group_by_method_exponential_base](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)` Note: Currently, only base 2 and base 10 are supported. The first range starts at negative infinity and ends at `pow(base, min_magnitude)`. The second range begins at `pow(base, min_magnitude) + 1` and ends at `pow(base, min_magnitude + 1)`, the next covers `pow(base, min_magnitude + 1) + 1` to `pow(base, min_magnitude + 2)`, and so on. The last range covers `pow(base, max_magnitude) + 1` to positive infinity. For example, given the settings `[metric_group_by_method_exponential_min_magnitude = 1](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)`, `[metric_group_by_method_exponential_max_magnitude = 5](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` and `[metric_group_by_method_exponential_base = 10](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)`, the ranges would be: - (-inf, 10\] - \[11, 100\] - \[101, 1000\] - \[1001, 10000\] - \[10001, 100000\] - \[100001, +inf) Much like the metric names generated with the [discrete](#discrete) aggregation method, the ones generated by the `exponential` method include information about the value of the field. However, in this case it is the range the value belongs to. Specifically, it is the name of the field being quantized, and the lower and upper bounds for the range. Example: doveconf ``` metric imap_command { filter = event=imap_command_finished group_by cmd_name { } group_by duration { method exponential { min_magnitude = 1 max_magnitude = 5 base = 10 } } } ``` This will generate metric names of the format `imap_command_{cmd}_duration_{min}_{max}` where `{cmd}` is the IMAP command name, and `{min}` and `{max}` are the range bounds. Therefore, for a `SELECT` IMAP command, the possible generated metric names are: - `imap_command_SELECT_ninf_10` - `imap_command_SELECT_11_100` - `imap_command_SELECT_101_1000` - `imap_command_SELECT_1001_10000` - `imap_command_SELECT_10001_100000` - `imap_command_SELECT_100001_inf` Note: Since the metric names cannot contain `-`, the string `ninf` is used to denote negative infinity. Note: Much like in the [discrete](#discrete) case, the metrics are allocated only when first observed. Finally, because all intermediate level metrics are generated as well. The above example, will also generate all of the following metrics: - `imap_command` - `imap_command_SELECT` ### `linear` The field's integer value is quantized into linearly sized ranges. The linear aggregation method uses three settings: - `[metric_group_by_method_linear_min](/main/core/summaries/settings.html#metric_group_by_method_linear_min)` - `[metric_group_by_method_linear_max](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` - `[metric_group_by_method_linear_step](/main/core/summaries/settings.html#metric_group_by_method_linear_step)` The first range starts at negative infinity and ends at `min`. The second range begins at `min + 1` and ends at `min + step`, the next covers `min + step + 1` to `min + (2 * step)`, and so on. The last range covers `max + 1` to positive infinity. For example, given settings `[metric_group_by_method_linear_min = 0](/main/core/summaries/settings.html#metric_group_by_method_linear_min)`, `[metric_group_by_method_linear_max = 5000](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` and `[metric_group_by_method_linear_step = 1000](/main/core/summaries/settings.html#metric_group_by_method_linear_step)`, the ranges would be: - (-inf, 0\] - \[1, 1000\] - \[1001, 2000\] - \[2001, 3000\] - \[3001, 4000\] - \[4001, 5000\] - \[5001, +inf) See the description of the [exponential](#exponential) aggregation method for how metric names are formed from these ranges. ## Listing Statistics The gathered statistics are available by running: sh ``` doveadm stats dump ``` Each event has a `duration` field, which tracks in microseconds how long the event existed. For example with `imap_command_finished` field it could be: ``` metric_name field count sum min max avg median stddev %95 imap_commands duration 35 1190122 162 340477 34003 244 31215 188637 ``` The above means: | Field | Description | | --- | --- | | `count` | There have been 35 IMAP commands | | `sum` | The IMAP commands were running in total for 1190122 microseconds (= 1.1 seconds) | | `min` | The fastest IMAP command took 162 microseconds | | `max` | The slowest IMAP command took 340477 microseconds | | `avg` | The average time spent on an IMAP commands was 34003 microseconds | | `median` | The median time spent on an IMAP command was 244 microseconds | | `stddev` | Standard deviation for the time spent on IMAP commands | | `%95` | 95% of the IMAP commands took 188637 microseconds or less | The other fields (than duration) track whatever that field represents. For example with imap\_command\_finished's `net_in_bytes` field could be tracking how many bytes were being used by the IMAP commands. Non-numeric fields can also be tracked, although only the `count` is relevant to those. The list of fields can be specified with the `-f` parameter. The default is: sh ``` doveadm stats dump -f 'count sum min max avg median stddev %95' ``` It's also possible to specify other percentiles than just 95%, for example: sh ``` doveadm stats dump -f 'count sum min max avg median stddev %95 %99 %99.9 %99.99' ``` The stats counters are reset whenever the stats process is started, which also means a dovecot reload will reset statistics. Using `doveadm stats -r` parameter will also reset the statistics atomically after they're dumped. ## Modifying Statistics Dynamically Metrics can be added or removed dynamically. The changes do not persist after configuration reload. Metrics can be added dynamically by running `[doveadm stats add]()`. - `exporter`: See `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)`. - `exporter-include`: See `[metric_exporter_include](/main/core/summaries/settings.html#metric_exporter_include)`. - `fields`: See `[metric_fields](/main/core/summaries/settings.html#metric_fields)`. - `group-by`: See [Statistics: Group By](/main/core/config/statistics.html#group-by). - ``: See `[metric_filter](/main/core/summaries/settings.html#metric_filter)`. For example: sh ``` doveadm stats add --description "IMAP SELECT commands" \ --exporter log-exporter --exporter-include "name timestamps" \ --fields "net_in_bytes net_out_bytes" \ --group-by "cmd_name tagged_reply_state" imap_cmd_select \ "event=imap_command_finished AND cmd_name=SELECT" ``` Metrics can be removed dynamically by running `[doveadm stats remove]()`. For example: sh ``` doveadm stats remove imap_cmd_select ``` ## Examples ### IMAP Command Statistics doveconf ``` metric imap_select_no { filter = event=imap_command_finished AND cmd_name=SELECT AND \ tagged_reply_state=NO } metric imap_select_no_notfound { filter = event=imap_command_finished AND cmd_name=SELECT AND \ tagged_reply="NO*Mailbox doesn't exist:*" } metric storage_http_gets { filter = event=http_request_finished AND category=storage AND \ method=get } # generate per-command metrics on successful commands metric imap_command { filter = event=imap_command_finished AND tagged_reply_state=OK group_by cmd_name { } } ``` ### Push Notifications doveconf ``` metric push_notifications { filter = event=push_notification_finished } # for OX driver metric push_notification_http_finished { filter = event=http_request_finished AND category=push_notification } ``` ## OpenMetrics ### Basic Configuration Dovecot has support for [OpenMetrics](https://openmetrics.io/) exposition format for statistics. This can be enabled by adding following configuration: doveconf ``` service stats { inet_listener http { port = 9900 } } ``` This will enable Dovecot to expose all configured metrics in OpenMetrics format on `http://host:9900/metrics` using [text-based format](https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format). ### Statistics Format By default, Dovecot exposes all configured metrics. If the metric name does not conform with OpenMetrics requirements, it is not exported. All metric names are prefixed with `dovecot_` and each non-histogram metric is exported as `dovecot__total` and `dovecot__duration_seconds_total`. Dynamically generated statistics with [group\_by](#group-by) will be exported too. The name of the base metric is used as above, and any dynamically generated sub-metrics are exported using labels. Quantized sub-metrics are exported as histograms. Histograms are exported as `dovecot__bucket` with corresponding labels. Each histogram will have an automatically generated `_sum` (specifying sum of all values in quantiles) and `_count` (total number of samples in the quantiles) metrics. Durations, including histogram ranges, are converted to seconds. Dovecot will also export version information and startup time as special metrics even if nothing is configured. These are called `dovecot_build_info` and `process_start_time_seconds`. ### Example An excerpt of an example Dovecot configuration that defines a set of metrics, and the sample exported data with such metrics configuration: dovecot.confOpenMetrics Data doveconf ``` metric auth_success { filter = (event=auth_request_finished AND success=yes) } metric imap_command { filter = event=imap_command_finished group_by cmd_name { } group_by tagged_reply_state { } } metric smtp_command { filter = event=smtp_server_command_finished group_by cmd_name { } group_by status_code { } group_by duration { method exponential { min_magnitude = 1 max_magnitude = 5 } } } metric mail_delivery { filter = event=mail_delivery_finished group_by duration { method exponential { min_magnitude = 1 max_magnitude = 5 } } } ``` ``` # HELP process_start_time_seconds Timestamp of service start # TYPE process_start_time_seconds gauge process_start_time_seconds 1606393397 # HELP dovecot_build Dovecot build information # TYPE dovecot_build info dovecot_build_info{version="2.4.devel",revision="38ecc424a"} 1 # HELP dovecot_auth_success Total number of all events of this kind # TYPE dovecot_auth_success counter dovecot_auth_success_total 892 # HELP dovecot_auth_success_duration_seconds Total duration of all events of this kind # TYPE dovecot_auth_success_duration_seconds counter dovecot_auth_success_duration_seconds_total 0.085479 # HELP dovecot_imap_command Total number of all events of this kind # TYPE dovecot_imap_command counter dovecot_imap_command_total{cmd_name="LIST"} 423 dovecot_imap_command_total{cmd_name="LIST",tagged_reply_state="OK"} 423 dovecot_imap_command_total{cmd_name="STATUS"} 468 dovecot_imap_command_total{cmd_name="STATUS",tagged_reply_state="OK"} 468 dovecot_imap_command_total{cmd_name="SELECT"} 890 dovecot_imap_command_total{cmd_name="SELECT",tagged_reply_state="OK"} 890 dovecot_imap_command_total{cmd_name="APPEND"} 449 dovecot_imap_command_total{cmd_name="APPEND",tagged_reply_state="OK"} 449 dovecot_imap_command_total{cmd_name="LOGOUT"} 892 dovecot_imap_command_total{cmd_name="LOGOUT",tagged_reply_state="OK"} 892 dovecot_imap_command_total{cmd_name="UID FETCH"} 888 dovecot_imap_command_total{cmd_name="UID FETCH",tagged_reply_state="OK"} 888 dovecot_imap_command_total{cmd_name="FETCH"} 2148 dovecot_imap_command_total{cmd_name="FETCH",tagged_reply_state="OK"} 2148 dovecot_imap_command_total{cmd_name="STORE"} 794 dovecot_imap_command_total{cmd_name="STORE",tagged_reply_state="OK"} 794 dovecot_imap_command_total{cmd_name="EXPUNGE"} 888 dovecot_imap_command_total{cmd_name="EXPUNGE",tagged_reply_state="OK"} 888 dovecot_imap_command_count 7840 # HELP dovecot_imap_command_duration_seconds Total duration of all events of this kind # TYPE dovecot_imap_command_duration_seconds counter dovecot_imap_command_duration_seconds_total{cmd_name="LIST"} 0.099115 dovecot_imap_command_duration_seconds_total{cmd_name="LIST",tagged_reply_state="OK"} 0.099115 dovecot_imap_command_duration_seconds_total{cmd_name="STATUS"} 0.161195 dovecot_imap_command_duration_seconds_total{cmd_name="STATUS",tagged_reply_state="OK"} 0.161195 dovecot_imap_command_duration_seconds_total{cmd_name="SELECT"} 0.184907 dovecot_imap_command_duration_seconds_total{cmd_name="SELECT",tagged_reply_state="OK"} 0.184907 dovecot_imap_command_duration_seconds_total{cmd_name="APPEND"} 0.273893 dovecot_imap_command_duration_seconds_total{cmd_name="APPEND",tagged_reply_state="OK"} 0.273893 dovecot_imap_command_duration_seconds_total{cmd_name="LOGOUT"} 0.033494 dovecot_imap_command_duration_seconds_total{cmd_name="LOGOUT",tagged_reply_state="OK"} 0.033494 dovecot_imap_command_duration_seconds_total{cmd_name="UID FETCH"} 0.181319 dovecot_imap_command_duration_seconds_total{cmd_name="UID FETCH",tagged_reply_state="OK"} 0.181319 dovecot_imap_command_duration_seconds_total{cmd_name="FETCH"} 1.169456 dovecot_imap_command_duration_seconds_total{cmd_name="FETCH",tagged_reply_state="OK"} 1.169456 dovecot_imap_command_duration_seconds_total{cmd_name="STORE"} 0.368621 dovecot_imap_command_duration_seconds_total{cmd_name="STORE",tagged_reply_state="OK"} 0.368621 dovecot_imap_command_duration_seconds_total{cmd_name="EXPUNGE"} 0.247657 dovecot_imap_command_duration_seconds_total{cmd_name="EXPUNGE",tagged_reply_state="OK"} 0.247657 dovecot_imap_command_duration_seconds_sum 2.719657 # HELP dovecot_smtp_command Histogram # TYPE dovecot_smtp_command histogram dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="0.000010"} 0 dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="0.000100"} 1 dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="0.001000"} 1 dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="0.010000"} 1 dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="0.100000"} 1 dovecot_smtp_command_bucket{cmd_name="LHLO",status_code="250",le="+Inf"} 1 dovecot_smtp_command_sum{cmd_name="LHLO",status_code="250"} 0.000020 dovecot_smtp_command_count{cmd_name="LHLO",status_code="250"} 1 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="0.000010"} 0 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="0.000100"} 1 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="0.001000"} 1 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="0.010000"} 1 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="0.100000"} 1 dovecot_smtp_command_bucket{cmd_name="MAIL",status_code="250",le="+Inf"} 1 dovecot_smtp_command_sum{cmd_name="MAIL",status_code="250"} 0.000021 dovecot_smtp_command_count{cmd_name="MAIL",status_code="250"} 1 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="0.000010"} 0 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="0.000100"} 0 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="0.001000"} 1 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="0.010000"} 1 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="0.100000"} 1 dovecot_smtp_command_bucket{cmd_name="RCPT",status_code="250",le="+Inf"} 1 dovecot_smtp_command_sum{cmd_name="RCPT",status_code="250"} 0.000195 dovecot_smtp_command_count{cmd_name="RCPT",status_code="250"} 1 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="0.000010"} 0 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="0.000100"} 0 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="0.001000"} 0 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="0.010000"} 1 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="0.100000"} 1 dovecot_smtp_command_bucket{cmd_name="DATA",status_code="250",le="+Inf"} 1 dovecot_smtp_command_sum{cmd_name="DATA",status_code="250"} 0.001249 dovecot_smtp_command_count{cmd_name="DATA",status_code="250"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="0.000010"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="0.000100"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="0.001000"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="0.010000"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="0.100000"} 1 dovecot_smtp_command_bucket{cmd_name="QUIT",status_code="221",le="+Inf"} 1 dovecot_smtp_command_sum{cmd_name="QUIT",status_code="221"} 0.000010 dovecot_smtp_command_count{cmd_name="QUIT",status_code="221"} 1 # HELP dovecot_mail_delivery Histogram # TYPE dovecot_mail_delivery histogram dovecot_mail_delivery_bucket{le="0.000010"} 0 dovecot_mail_delivery_bucket{le="0.000100"} 0 dovecot_mail_delivery_bucket{le="0.001000"} 1 dovecot_mail_delivery_bucket{le="0.010000"} 1 dovecot_mail_delivery_bucket{le="0.100000"} 1 dovecot_mail_delivery_bucket{le="+Inf"} 1 dovecot_mail_delivery_sum 0.000656 dovecot_mail_delivery_count 1 # EOF ``` # Submission Server Dovecot provides an SMTP submission service, also known as a Mail Submission Agent (MSA) ([RFC 6409](https://datatracker.ietf.org/doc/html/rfc6409)). DANGER It is currently implemented as a proxy that acts as a front-end for any [MTA](/main/core/config/delivery/mta.html), adding the necessary functionality required for a submission service: it adds the required AUTH ([RFC 4954](https://datatracker.ietf.org/doc/html/rfc4954)) support, avoiding the need to configure the MTA for [SASL](/main/core/admin/sasl.html). DANGER Dovecot's submission server is NOT a full-featured SMTP server. It REQUIRES proxying to an external relay SMTP submission server to deliver non-local messages. It is currently implemented as a [proxy](/main/core/admin/login_proxy.html) that acts as a front-end for any [MTA](/main/core/config/delivery/mta.html), adding the necessary functionality required for a submission service: it adds the required AUTH ([RFC 4954](https://datatracker.ietf.org/doc/html/rfc4954)) support, avoiding the need to configure the MTA for [SASL](/main/core/admin/sasl.html). Additionally, the client TLS layer is terminated at Dovecot (either with or without STARTTLS), so that all mail protocol certificates are handled solely by Dovecot and do not need to be configured in the MTA (although inter-server TLS encryption can be enabled if needed.) More SMTP capabilities like CHUNKING ([RFC 3030](https://datatracker.ietf.org/doc/html/rfc3030)) and SIZE ([RFC 1870](https://datatracker.ietf.org/doc/html/rfc1870)) are supported, without requiring the backend MTA supporting these extensions. Other capabilities like 8BITMIME ([RFC 6152](https://datatracker.ietf.org/doc/html/rfc6152)) and DSN ([RFC 3461](https://datatracker.ietf.org/doc/html/rfc3461)) currently require support from the backend/relay MTA. The most notable feature that the proxy adds is the BURL capability ([RFC 4468](https://datatracker.ietf.org/doc/html/rfc4468)). The main application of that capability, together with [IMAP server](/main/core/config/imap.html) and URLAUTH ([RFC 4467](https://datatracker.ietf.org/doc/html/rfc4467)), is avoiding a duplicate upload of submitted e-mail messages. Normally the message is both sent through SMTP and uploaded to the `Sent` folder through IMAP. Using BURL, the client can first upload the message to IMAP and then use BURL to make the SMTP server fetch the message from IMAP for submission, thereby avoiding a second upload. Few clients currently support the BURL capability, but once it becomes available on the server side, client developers will at least have some incentive to provide support for this feature. WARNING Currently, the submission proxy is still pretty basic. However, it will provide a basis for adding all kinds of functionality in the (not so distant) future. For the first time, it will be possible to act upon message submission, rather than only message retrieval; e.g. plugins can be devised that process outgoing messages somehow. Examples of the things that could be implemented are adding Sieve filtering support for outgoing messages, or implicitly storing submitted messages to the Sent folder. Once a plugin API is devised, you can create your own plugins. The submission service, when protocol submission is enabled, will listen to 587/tcp (STARTTLS) by default. ## Features The following SMTP capabilities are supported by the Dovecot submission service: - **8BITMIME** ([RFC 6152](https://datatracker.ietf.org/doc/html/rfc6152)): Only if relay MTA provides support - **AUTH** ([RFC 4954](https://datatracker.ietf.org/doc/html/rfc4954)) - **BURL** ([RFC 4468](https://datatracker.ietf.org/doc/html/rfc4468)) - **CHUNKING** ([RFC 3030](https://datatracker.ietf.org/doc/html/rfc3030)) - **DSN** ([RFC 3461](https://datatracker.ietf.org/doc/html/rfc3461)): Only if relay MTA provides support - **ENHANCEDSTATUSCODES** ([RFC 2034](https://datatracker.ietf.org/doc/html/rfc2034)) - **PIPELINING** ([RFC 2920](https://datatracker.ietf.org/doc/html/rfc2920)) - **SIZE** ([RFC 1870](https://datatracker.ietf.org/doc/html/rfc1870)) - **STARTTLS** ([RFC 3207](https://datatracker.ietf.org/doc/html/rfc3207)) - **VRFY** ([RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321)) - **XCLIENT**: See [https://www.postfix.org/XCLIENT\_README.html](https://www.postfix.org/XCLIENT_README.html) ## Flow Diagram mermaid ``` flowchart LR %% Nodes MUA([MUA
Mail Client]) %% Grouping (Package) subgraph DOVECOT direction TB SubLogin(submission-login
Handles TLS & Auth) Auth[Dovecot Auth] Submission(submission
Validates SMTP; proxies mail) Storage[(Mail Storage)] end MTA[Backend MTA
e.g., Postfix/Exim] Net((Remote Delivery)) %% Connections MUA -->|Connect SMTP: Port 465/587| SubLogin SubLogin --> Auth Auth --> Submission Submission -.->|Optional Handles BURL| Storage Submission -->|Relays message SMTP| MTA MTA --> Net ``` ## Configuration ### Submission Service Add `submission` to the `[protocols](/main/core/summaries/settings.html#protocols)` setting and configure the relay MTA server. The submission service is a login service, just like IMAP, POP3 and [ManageSieve](/main/core/config/sieve/managesieve.html), so clients are required to authenticate. The same [authentication configuration](/main/core/config/auth/overview.html) will apply to the submission service, unless you're doing protocol-specific things, in which case you may need to amend your configuration for the new protocol. BURL support requires a working IMAP URLAUTH implementation. See `[imap_urlauth_host](/main/core/summaries/settings.html#imap_urlauth_host)`. #### Settings ### `hostname` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) The hostname to be used in email messages sent out by the local delivery agent (such as the Message-ID: header), in LMTP replies, and as the hostname advertised by submission SMTP service. ### `submission_add_received_header` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 Controls if "Received:" header should be added to mails by the submission backend. ### `submission_client_workarounds` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Configures the list of active workarounds for Submission client bugs. Supported workaround identifiers are: `implicit-auth-external` Implicitly login using the EXTERNAL SASL mechanism upon the first MAIL command, provided that the client provides a valid TLS client certificate. This is helpful for clients that omit explicit SASL authentication when configured for authentication using a TLS certificate (Thunderbird for example). `mailbox-for-path` Allow using bare Mailbox syntax (i.e., without <...>) instead of full path syntax. `whitespace-before-path` Allow one or more spaces or tabs between 'MAIL FROM:' and path and between 'RCPT TO:' and path. ### `submission_host` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) Use this SMTP submission host to send messages. Overrides `[sendmail_path](/main/core/summaries/settings.html#sendmail_path)` value, if set. ### `submission_logout_format` **Default:** `in=%{input} out=%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The SMTP Submission logout format string. Variables supported, including `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)`: | Variable Name | Description | | --- | --- | | `%{input}` | Bytes read from client | | `%{output}` | Bytes sent to client | | `%{command_count}` | Number of commands received from client | | `%{reply_count}` | Number of replies sent to client | | `%{transaction_id}` | ID of the current transaction, if any | ### `submission_max_mail_size` **Default:** `40M` **Value:** [size](/main/core/settings/types.html#size) The maximum message size accepted for relay. This value is announced in the SMTP SIZE capability. If empty, this value is either determined from the relay server or left unlimited if no limit is known; the relay MTA will reply with error if some unknown limit exists there, which will be passed back to the client. ### `submission_max_recipients` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of recipients accepted per connection. ### Relay MTA The Dovecot SMTP submission service directly proxies the mail transaction to the SMTP relay. #### Settings ### `submission_relay_command_timeout` **Default:** `5mins` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for SMTP commands issued to the submission service's relay server. The timeout is reset every time more data is being sent or received. ### `submission_relay_connect_timeout` **Default:** `30secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for connecting to and logging into the submission service's relay server. ### `submission_relay_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Host of the relay server (REQUIRED to provide the submission service). ### `submission_relay_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Master user name for authentication to the relay MTA if authentication is required. ### `submission_relay_max_idle_time` **Default:** `29mins` **Value:** [time](/main/core/settings/types.html#time) Submission relay max idle time for connection to relay MTA. ### `submission_relay_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication to the relay MTA if authentication is required. ### `submission_relay_port` **Default:** `25` **Value:** [Port Number](/main/core/settings/types.html#port-number) Port for the submission relay server. ### `submission_relay_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Write protocol logs for relay connection to this directory for debugging. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `submission_relay_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``smtps``starttls` If enabled, SSL/TLS is used for the connection to the relay server. Available values: `no` No SSL connection is used. `smtps` An SMTPS connection (immediate SSL) is used. `starttls` The STARTTLS command is used to establish the TLS layer. ### `submission_relay_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, TLS certificate of the relay server must be verified. ### `submission_relay_trusted` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, the relay server is trusted. Determines whether we try to send (Postfix-specific) XCLIENT data to the relay server (only if enabled). ### `submission_relay_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) User name for authentication to the relay MTA if authentication is required. ### Login Proxy Like IMAP and POP3, the Submission login service supports [proxying](/main/core/config/proxy/overview.html) to multiple backend Dovecot servers. WARNING Please note that the login proxy described here is configured between two Dovecot servers (e.g. proxy frontend and mail storage backend). This is not the way to configure the relay connection between the Dovecot submission service and the MTA! That is configured using the relay settings described in the previous section. If you get this wrong, things will seem to work (at least to some extent), but the service provided by Dovecot will be effectively bypassed. ### SMTPS To listen on SMTPS port (465/tcp), add this to `dovecot.conf`: doveconf ``` service submission-login { inet_listener submissions { port = 465 ssl = yes } } ``` ## Design The Dovecot submission service is structured very much like the imap and pop3 services, meaning that it has separate pre-login and post-login services called submission-login and submission respectively. The pre-login service can be chrooted and can be devoid of most privileges and only serves to operate the TLS layer and perform authentication. This serves to isolate the sensitive SMTP functionality from unauthorized access, also when the frontend were compromised. Once authenticated, the connection is handed over to the post-login service. The post-login service checks the validity of the SMTP transactions and forwards them on the fly to the backend MTA, while also translating or handling capabilities such as BURL and CHUNKING; e.g., when the backend MTA provides no support for these features. For the BURL capability, the post-login service has direct access to the user's mail storage. Proxying for the submission service works identical to the imap and pop3 services. This means that the submission-login service proxies to another Dovecot backend instance that handles the subsequent relay to the MTA infrastructure. In this case there is proxying between two Dovecot instances and a relay from the Dovecot backend instance to a non-Dovecot backend MTA. IMPORTANT It is explicitly not supported to use submission-login to proxy directly to a backend MTA. This would mean that after authentication the connection is proxied directly to the external non-Dovecot MTA, which will then completely handle the SMTP protocol exchange. Although authentication and TLS can still operate this way, the other features and additional protocol verification that Dovecot submission adds will be broken. Additionally, the submission-login service is likely to lie to the client about which SMTP capabilities are supported by the service, since the announced capabilities and those provided by the MTA will likely differ. # System Users Configuration Dovecot typically requires 3 or more system users: - `root`: Dovecot is started as root. - [`dovenull`](#dovenull-user): Dovecot uses an unprivileged user for untrusted login processes. - [`dovecot`](#dovecot-user): Dovecot uses an unprivileged user for internal processes. - [`mail user(s)`](#mail-users): Mails are accessed using yet another user. The mail user should NOT be `dovecot` user. - [`auth user`](#authentication-process-user): Password and user database lookups are done as auth user. Using multiple users allows privilege separation, which makes it harder for attackers to compromise the whole system if a security hole is found from one component. However, if you really want to run everything under a single user, it is possible. See [Rootless Installation](/main/core/config/rootless.html). ## `dovenull` user `dovenull` user is used internally for processing users' logins. It shouldn't have access to any files, authentication databases or anything else either. It should belong to its own private `dovenull` group where no one else belongs to, and which doesn't have access to any files either (other than what Dovecot internally creates). You can change the default `dovenull` user to something else via `[default_login_user](/main/core/summaries/settings.html#default_login_user)`. ## `dovecot` user `dovecot` user is used internally for unprivileged Dovecot processes. It should belong to its own private `dovecot` group. Mail files are not accessed as dovecot user, so you shouldn't give it access to mails. You can change the default `dovecot` user to something else via `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)`. ## Mail Users You can use one or more system users for accessing users' mails. Most configurations can be placed to two categories: 1. [System Users](/main/core/config/auth/users/system.html), where each Dovecot user has their own system user in `/etc/passwd`. For system user setups you generally don't have to worry about UIDs or GIDs, they are returned by [passwd authentication database](/main/core/config/auth/databases/passwd.html). 2. [virtual users](/main/core/config/auth/users/virtual.html), where all Dovecot users run under a single system user. Typically you'd set this with `[mail_uid](/main/core/summaries/settings.html#mail_uid)` (e.g. `mail_uid=vmail`). Note that you most likely don't want the userdb lookup to return any UID/GID, as they override `[mail_uid](/main/core/summaries/settings.html#mail_uid)`. However it's possible to use a setup that is anything between these two. For example use a separate system user for each domain. See below for more information about how UIDs can be used. ### UIDs Dovecot's [userdb](/main/core/config/auth/userdb.html) configuration calls system users UIDs. There are a few things you should know about them: - Although UID normally means a numeric ID (as specified by `/etc/passwd`), it's anyway possible to use names as UID values and let Dovecot do the lookup (eg. `uid=vmail`). However depending on where you used it, it may slow down the authentication. - The UIDs don't really have to exist in `/etc/passwd` (the kernel doesn't care about that). For example you could decide to use UIDs 10000-59999 for 50000 virtual Dovecot users. You'll then just have to be careful that the UIDs aren't used unintentionally elsewhere. - The important thing to consider with your UID allocation policy is that if Dovecot has a security hole in its IMAP or POP3 implementation, the attacker can read mails of other people who are using the same UID. So clearly the most secure way is to allocate a different UID for each user. It can however be a bit of a pain and OSes don't always support more than 65536 UIDs. - By default Dovecot allows users to log in only with UID numbers 500 and above. This check tries to make sure that no-one can ever log in as daemons or other system users. If you're using an UID lower than 500, you'll need to change `[first_valid_uid](/main/core/summaries/settings.html#first_valid_uid)`. ### GIDs System groups (GIDs) work very much the same way as UIDs described above: You can use names instead of numbers for GID values, and the used GIDs don't have to exist in `/etc/group`. System groups are useful for sharing mailboxes between users that have different UIDs but belong to a same group. Currently Dovecot doesn't try to do anything special with the groups, so if you're not sure how you should create them, you might as well place all the users into a single group or create a separate group for each user. If you use multiple UIDs and you wish to create [shared mailboxes](/main/core/config/shared_mailboxes.html), setting up the groups properly may make your configuration more secure. For example if you have two teams and their mailboxes are shared only to their team members, you could create a group for each team and set the shared mailbox's group to the team's group and permissions to `0660`, so neither team can even accidentally see each others' shared mailboxes. Currently Dovecot supports specifying only the primary group, but if your userdb returns `system_user` [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields), the non-primary groups are taken from `/etc/group` for that user. In a future version the whole GID list will be configurable without help from `/etc/group`. It's also possible to give all the users access to extra groups with `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)`. ## Authentication Process User Depending on passdb and userdb configuration, the lookups are done either by auth process or auth worker process. They have different default users: doveconf ``` service auth { user = $SET:default_internal_user } service auth-worker { user = root } ``` The user must have access to your [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html). It's not used for anything else. The default is to use `root`, because it's guaranteed to have access to all the password databases. If you don't need this, you should change it to `$SET:default_internal_user`. [PAM authentication database](/main/core/config/auth/databases/pam.html) is usually configured to read `/etc/shadow` file. Even this doesn't need root access if the file is readable by shadow group: doveconf ``` service auth-worker { user = $SET:default_internal_user group = shadow } ``` # Time Synchronization Dovecot relies on accurate time on synchronization the local system. There are two choices for synchronizing your clock: 1. Use [ntpd](https://www.ntp.org/). It periodically checks the current time from NTP server and slows down or speeds up the clock if necessary. Unlike ntpdate, it doesn't just move the time forwards or backwards (unless the difference is large). - If the time difference is too large for ntpd and it "steps", then use "-x" as a command line option for ntpd or use "tinker step 0" in `/etc/ntp.conf`. - This shows up in logs as: `ntpd[17697]: time reset -2.075483 s` 2. If ntpd doesn't work well (e.g. a bad network connection), you can use [clockspeed](https://cr.yp.to/clockspeed.html) or [chrony](https://chrony.tuxfamily.org/) as well. In some systems ntpd/ntpdate is run at boot, but only after Dovecot has started. That can cause Dovecot to die immediately. If you have this problem, fix your init scripts to run ntpd/ntpdate first, before starting Dovecot. Also, seriously consider running ntp-wait before starting Dovecot. ## Server Startup Time Synchronization With systemd add `time-sync.target` to the `After` setting. This isn't enough though, because it only waits for time-sync to start, not finish. To do that, enable also `systemd-time-wait-sync.service`. ## What about Daylight Saving/Summer Time? On Unix-like systems, time is stored internally as the number of seconds since January 1, 1970, 00:00:00 UTC (see [UNIX time](https://en.wikipedia.org/wiki/Unix_time)); concepts such as time zones and daylight saving time are applied in user space by the C library, and will normally not have an impact on Dovecot's behavior. ## "But Dovecot Shouldn't Just Die!" Dovecot's behavior when time moves backwards is: - Existing imap and pop3 processes either sleep or die - Master process stops creating new processes until either the original time is reached, or after a maximum wait of 3 minutes. - Other processes log a warning, but do nothing else. - Timeouts are updated so that the timeout is executed approximately at the original intended time. Dovecot also notices when time unexpectedly jumps forwards. In that situation it logs a warning and also updates timeouts. The reason why imap/pop3 processes get killed and new ones can't be created for a while is to avoid problems related to timestamps. Some issues are: - Uniqueness of Maildir filenames and dbox global unique identifiers relies on a growing timestamp. - Dotlock files' staleness is detected by looking at its mtime. - Timestamps are stored internally all around in memory (as well as in index files) and compared to current time. Those checks may or may not be buggy if current time shrinks. While killing mail processes doesn't fully solve any of those issues, they're at least less likely to happen then. # doveadm-acl(1) - Manage Access Control List (ACL) ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **acl** _command_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION The **doveadm acl** _COMMANDS_ can be used to execute various Access Control List related actions. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. This command uses by default the output formatter **table**. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _id_ The id (identifier) is one of: - **group-override** = _group\_name_ - **user** = _user\_name_ - **owner** - **group** = _group\_name_ - **authenticated** - **anyone** - **anonymous**, which is an alias for anyone The ACLs are processed in the precedence given above, so for example if you have given read-access to a group, you can still remove that from specific users inside the group. Group-override identifier allows you to override users' ACLs. Probably the most useful reason to do this is to temporarily disable access for some users. For example: ``` user=timo rw group-override=tempdisabled ``` Now if timo is a member of the tempdisabled group, he has no access to the mailbox. This wouldn't be possible with a normal group identifier, because the **user=timo** would override it. _mailbox_ The name of the mailbox, for which the ACL manipulation should be done. It's also possible to use the wildcard characters "\*****" and/or "**?**" in the mailbox name. _right_ Dovecot ACL right name. This isn't the same as the IMAP ACL letters, which aren't currently supported. Here is a mapping of the IMAP ACL letters to Dovecot ACL names: **l -> lookup** _Mailbox_ is visible in mailbox list. _Mailbox_ can be subscribed to. **r -> read** _Mailbox_ can be opened for reading. **w -> write** Message flags and keywords can be changed, except **\\Seen** and **\\Deleted**. **s -> write-seen** **\\Seen** flag can be changed. **t -> write-deleted** **\\Deleted** flag can be changed. **i -> insert** Messages can be written or copied to the _mailbox_. **p -> post** Messages can be posted to the _mailbox_ by **dovecot-lda**, e.g. from Sieve scripts. **e -> expunge** Messages can be expunged. **k -> create** Mailboxes can be created/renamed directly under this _mailbox_ (but not necessarily under its children, see [ACL Inheritance](/main/core/plugins/acl.html#acl-inheritance-and-default-acls). Note: Renaming also requires the delete right. **x -> delete** _Mailbox_ can be deleted. **a -> admin** Administration rights to the _mailbox_ (currently: ability to change ACLs for _mailbox_). ## COMMANDS ### acl add **doveadm** \[_GLOBAL OPTIONS_\] acl add \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox id_ _right_ \[_right_ ...\] Add ACL rights to the _mailbox_/_id_. If the _id_ already exists, the existing rights are preserved. ### acl debug **doveadm** \[_GLOBAL OPTIONS_\] acl debug \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox_ This command can be used to debug why a shared mailbox isn't accessible to the user. It will list exactly what the problem is. ### acl delete **doveadm** \[_GLOBAL OPTIONS_\] acl delete \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox id_ Remove the whole ACL entry for the _mailbox_/_id_. ### acl get **doveadm** \[_GLOBAL OPTIONS_\] acl get \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-m**\] _mailbox_ Show all the ACLs for the _mailbox_. **\-m** Only show ACLs that match the mailbox. ### acl recalc **doveadm** \[_GLOBAL OPTIONS_\] acl recalc \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] Make sure the _user_'s shared mailboxes exist correctly in the _acl\_sharing\_map_. ### acl remove **doveadm** \[_GLOBAL OPTIONS_\] acl remove \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox id_ _right_ \[_right_ ...\] Remove the specified ACL rights from the _mailbox_/_id_. If all rights are removed, the entry still exists without any rights. ### acl rights **doveadm** \[_GLOBAL OPTIONS_\] acl rights \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox_ Show the _user_'s current ACL rights for the _mailbox_. ### acl set **doveadm** \[_GLOBAL OPTIONS_\] acl set \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox id_ _right_ \[_right_ ...\] Set ACL rights to the _mailbox_/_id_. If the _id_ already exists, the existing rights are replaced. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` Additional resources: - [ACL Inheritance](/main/core/plugins/acl.html#acl-inheritance-and-default-acls) # doveadm-altmove(1) - Move matching mails to the alternative storage (dbox-only) ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **altmove** \[**\-r**\] \[**\-S** _socket\_path_\] **\-A** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **altmove** \[**\-r**\] \[**\-S** _socket\_path_\] **\-F** _file search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **altmove** \[**\-r**\] \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **altmove** \[**\-r**\] \[**\-S** _socket\_path_\] **\-u** _user search\_query_ ## DESCRIPTION This command can be used with sdbox or mdbox storage to move mails to alternative storage path when _mail\_alt\_path_ is specified for the mail location. In the first form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will execute the **altmove** for all users. In the second form, the command will be performed for all users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the last form, only matching mails of the given _user_(s) will be moved to the alternative storage. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-r** When the **\-r** option is given this _command_ works the other way round. Mails will be moved from the alternative storage back to the default mail location. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _search\_query_ Messages matching this search query will be moved to alt storage. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## FILES _/etc/dovecot/conf.d/10-mail.conf_ Mailbox locations and namespaces. ## EXAMPLE This example moves seen mails older than one week to alternative storage under /nfsmount: doveconf ``` mail_driver = mdbox mail_path = ~/mdbox mail_alt_path = /nfsmount/%{home}/mdbox ``` sh ``` doveadm altmove -u johnd@example.com seen savedbefore 1w< ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-auth(1) - Flush/lookup/test authentication data ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **auth** _command_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION The **doveadm auth** _COMMANDS_ can be used to perform various authentication related actions. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _user_ The _user_'s login name. Depending on the configuration, the login name may be for example **jane** or **[john@example.com](mailto:john@example.com)**. _password_ Optionally the user's password. `[doveadm(1)](/main/core/man/doveadm.1.html)` will prompt for the password, if none was given. ## COMMANDS ### auth cache flush **doveadm** \[_GLOBAL OPTIONS_\] auth cache flush \[**\-a** _master\_socket\_path_\] \[_user-mask_ ...\] Flush the authentication cache. By default the cache is flushed for all the users. You can also flush the cache for one or more users by providing a user-mask matching their usernames. **\-a** _master\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-master_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ### auth cache status **doveadm** \[_GLOBAL OPTIONS_\] auth cache status \[**\-a** _master\_socket\_path_\] \[**\--reset**\] Show authentication cache statistics: number of hits, misses, hit ratio, number of positive/negative cache entries and the positive/negative/used/max cache sizes. This information can be used for tuning the cache size and TTL. **\-a** _master\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-master_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\--reset** Reset the hit/miss/insert counters after reading them. ### auth lookup **doveadm** \[_GLOBAL OPTIONS_\] auth lookup \[**\-a** _userdb\_socket\_path_\] \[**\-x** _auth\_info_\] \[**\-f** _field_\] _user_ \[...\] Similar to `[doveadm-user(1)](/main/core/man/doveadm-user.1.html)` command, except it performs a _passdb_ lookup (without authentication) instead of a _userdb_ lookup. **\-a** _userdb\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-userdb_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-f** _field_ When this option and the name of a userdb field is given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will show only the value of the specified field. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ### auth test **doveadm** \[_GLOBAL OPTIONS_\] auth test \[**\-a** _auth\_socket\_path_\] \[**\-A** _sasl\_mech_\] \[**\-x** _auth\_info_\] _user_ \[_password_\] Test authentication for the given user. **\-a** _auth\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-client_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-A** _sasl\_mech_ The SASL mechanism used for the authentication. By default PLAIN is used. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ### auth login **doveadm** \[_GLOBAL OPTIONS_\] auth login \[**\-a** _auth\_socket\_path_\] \[**\-m** _auth\_master\_socket\_path_\] \[**\-A** _sasl\_mech_\] \[**\-x** _auth\_info_\] _user_ \[_password_\] Test full login for the given user; i.e. performing both passdb lookup (authentication) and userdb lookup (login). **\-a** _auth\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-client_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-m** _auth\_master\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket for the master socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-master_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-A** _sasl\_mech_ The SASL mechanism used for the authentication. By default PLAIN is used. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## EXAMPLE This example demonstrates an imap authentication test for user john, assuming the user is connected from the host with the IP address 192.0.2.143. sh ``` doveadm auth test -x service=imap -x rip=192.0.2.143 john ``` ``` Password: passdb: john auth succeeded extra fields: user=john ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-backup(1) - Dovecot's one-way mailbox synchronization feature ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **sync** \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-1fPR**\] \[**\-l** _secs_\] \[**\-r** _rawlog path_\] \[**\-m** _mailbox_\] \[**\-g** _mailbox guid_\] \[**\-n** _namespace_ | **\-N**\] \[**\-x** _exclude_\] \[**\-a** _all mailbox_\] \[**\-s** _state_\] \[**\-T** _secs_\] \[**\-t** _start date_\] \[**\-e** _end date_\] \[**\-O** _sync flag_\] \[**\-I** _max size_\] \[**\-p** _setting=value_\] _destination_ **doveadm** \[_GLOBAL OPTIONS_\] **backup** \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-fPR**\] \[**\-l** _secs_\] \[**\-r** _rawlog path_\] \[**\-m** _mailbox_\] \[**\-g** _mailbox guid_\] \[**\-n** _namespace_ | **\-N**\] \[**\-x** _exclude_\] \[**\-a** _all mailbox_\] \[**\-s** _state_\] \[**\-T** _secs_\] \[**\-t** _start date_\] \[**\-e** _end date_\] \[**\-O** _sync flag_\] \[**\-I** _max size_\] \[**\-p** _setting=value_\] _destination_ ## DESCRIPTION dsync (short for doveadm sync) is Dovecot's mailbox synchronization feature. It can be used for several different use cases: Two-way synchronization of mailboxes, creating backups of mails, and convert mailboxes from/to different mailbox formats. All of these can be used within the same server or between different servers (via ssh(1) or tcp connections). Remote mailboxes can be accessed also via IMAP protocol, which allows using dsync for mailbox migration purposes. You can run dsync in one of three modes: - **doveadm backup** performs one-way synchronization. If there are any changes in the destination they will be reverted, so the destination will look exactly like the source. - **doveadm sync** performs two-way synchronization. It merges all changes without losing anything. Both the mailboxes will end up looking identical after the synchronization is finished. - **doveadm sync -1** performs one-way synchronization. If there are any changes in the destination, they will be preserved and the new changes will be merged on top of them. This merging doesn't currently work perfectly, so its use should be limited. Its main purpose is that during mailbox migration you can run **doveadm backup** multiple times, then switch mails to be delivered to the new mailbox and run **doveadm sync -1** once more to transfer any last new mails from the old mailbox. - The one-way algorithm is the same as two-way dsync algorithm except the source account is not modified. It fetches the message's GUID (Global UID), which is used to identify any conflicting UIDs in messages. As long as the source and destination side has matching UID<->GUID mapping, those emails are assumed to be synced correctly. Only after the first mismatch will changes begin. Example: Source mailbox has messages UID 1..5; source mailbox is synchronized using **doveadm backup** to the destination. Subsequently, UID 6 is delivered to the source mailbox and UID 1 is expunged from the destination mailbox. In this example, UID 1 is kept removed (in destination) because UID 1..5 have identical Date+Message-ID headers. UID 6 is not seen in destination so it's copied. If both source and destination have UID 6, but the messages are different, the headers don't match and both the messages are kept in the destination but they're given new UIDs 7 and 8 just to be sure any client didn't get confused about what UID 6 actually was. Thus, one-way sync begins to quickly diverge from the source mailbox once changes start to occur on either side; one-way sync should therefore normally only be used within a short period of time after a **doveadm backup** or **doveadm sync** command was used to synchronize the mailboxes. There are also three different synchronization algorithms: - Full synchronization (-f parameter) scans through all the messages in all the mailboxes. This guarantees that everything will be synchronized, but it's unnecessarily slow for incremental synchronization. - Fast synchronization (default) first attempts to find mailboxes that have changed, and synchronize only those. This is done by checking the mailboxes' metadata (NEXTUID and HIGHESTMODSEQ). Usually this works fine, especially with one-way synchronization, but if both sides do exactly the same number of changes, the metadata may end up containing the same values even if the changes were different. - Stateful synchronization (-s parameter) is the most efficient way to synchronize mailboxes. It relies on having the earlier dsync run's state saved somewhere and being passed to the next dsync run. Based on this state dsync can send only the changes that happened after the previous dsync run. As long as the state or the mailboxes aren't corrupted this algorithm should work perfectly. The syncing is done as perfectly as possible: an IMAP or a POP3 client shouldn't be able to notice any differences between the two mailboxes. Two-way syncing means that it's safe to do any kind of modifications in both sides, and dsync will merge the changes without losing any changes done on either side. This is possible because dsync can access Dovecot's index logs that keep track of changes. It's of course possible to have conflicts during merging, these are resolved in a safe way. See the _dsync design_ document for more information. dsync uses the same configuration files as the rest of Dovecot (via `[doveconf(1)](/main/core/man/doveconf.1.html)`). The entire configuration can be changed by giving -c parameter to another configuration file, or using -o parameter to override specific settings. When executing a remote dsync program it works the same way: it uses its own local configuration. dsync can be run completely standalone. It doesn't require any Dovecot server processes to be running, except when using -u parameter to do a _userdb_ lookup from auth process. dsync can sync either one or multiple users using the -u or -A parameters. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-1** Do one-way synchronization instead of two-way synchronization. **\-f** Do full synchronization. **\-N** Synchronize all the available namespaces. By default only the inbox=yes namespace is synchronized. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-P** Run a `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html)` for the destination (remote) storage after synchronization. **\-R** Do a reverse sync. Normally, messages would be pushed from the local system to the destination (remote). This option reverses the flow, and will instead pull messages from the remote to the local storage. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-T** _secs_ Specify the time in seconds, how long `[doveadm(1)](/main/core/man/doveadm.1.html)` should wait for stalled I/O operations. The default timeout is 600 seconds. **\-p** _setting_**\=**_value_ Overrides the configuration _setting_ for source storage. **\-g** _mailbox\_guid_ Same as -m, but find the mailbox to be synchronized by its GUID instead of by name. **\-l** _secs_ Lock the dsync for this user. Wait for maximum _secs_ before giving up. This parameter should be used to avoid broken synchronization if it's possible that dsync is being run concurrently for the same user. **\-m** _mailbox_ Synchronize only this mailbox name. **\-n** _namespace_ Synchronize only the specified namespace. This parameter can be used multiple times. **\-a** _all mailbox_ Name for the "All mails" virtual mailbox. If specified, mails are attempted to be copied from this mailbox instead of being saved separately. This may reduce the total disk space usage as well as disk IO. **\-t** _start date_ Skip any mails whose received-timestamp is older than the specified time. **\-e** _end date_ Skip any mails whose received-timestamp is newer than the specified time. **\-O** _sync flag_ Sync only mails that have the specified flag. If the flag name begins with "**\-**", sync all mails except the ones with the specified flag. **\-I** _max size_ Skip any mails larger than the specified size. **\-r** _rawlog\_path_ Running dsync remotely, write the remote input/output traffic to the specified log file. **\-s** _previous\_state_ Use stateful synchronization. This allows for optimized incremental synchronization by providing a "sync state string" from the previous dsync run. dsync will use this state to only synchronize the changes that have occurred since that state was captured, avoiding a full mailbox scan. If the previous state is unknown, use an empty string. The new state is always printed to standard output if this parameter is specified. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-x** _mailbox\_mask_ Exclude the specified mailbox name/mask. The mask may contain "**?**" and "\*\*\*\*\*" wildcards. The mask can also be a special-use name (e.g. \\Trash). This parameter can be used multiple times. ## ARGUMENTS _destination_ This argument specifies the synchronized destination. It can be one of: _mail\_driver:mail\_path_ Uses the storage specified by _mail\_driver_ and _mail\_path_. Use the **\-p** parameter to specify additional settings. **remote:** _login@host_ Uses _dsync\_remote\_cmd_ setting to connect to the remote host (usually via ssh) _remoteprefix:login@host_ This is the same as remote, except "user@domain\\n" is sent before dsync protocol starts. This allows implementing a trusted wrapper script that runs doveadm dsync-server by reading the username from the first line. **tcp:** _host\[:port\]_ Connects to remote doveadm server via TCP. The default port is specified by _doveadm\_port_ setting. **tcps:** _host\[:port\]_ This is the same as tcp, but with SSL. **command \[arg1 \[, arg2, ...\]\]** Runs a local command that connects its standard input & output to a dsync server. ## EXIT STATUS **dsync** will exit with one of the following values: **0** Synchronization was done perfectly. **2** Synchronization was done without errors, but some changes couldn't be done, so the mailboxes aren't perfectly synchronized. Running dsync again usually fixes this. Typically this occurs for message modification sequences with newly created mailboxes. It can also occur if one of the mailboxes change during the syncing. **1, >2** Synchronization failed. See also `[doveadm(1)](/main/core/man/doveadm.1.html)` for other possible error codes. ## EXAMPLE ### SYNCHRONIZATION Synchronize mailboxes with a remote server. Any errors are written to stderr: sh ``` doveadm sync -u username@example.com remote:server-replica.example.com ``` If you need more complex parameters to ssh, you can use e.g.: sh ``` doveadm sync -u username@example.com ssh -i id_dsa.dovecot \ mailuser@example.com doveadm dsync-server -u username@example.com ``` ### CONVERTING Example to convert mails from Maildir format to the format specified in the configuration file: sh ``` doveadm backup -u user maildir:~/Maildir ``` If you want to do this without any downtime, you can do the conversion one user at a time. We consider this scenario: - Configuration uses **mail\_driver = maildir** and **mail\_path = ~/Maildir** - We want to convert to the mdbox format: **mail\_driver = mdbox** and **mail\_path = ~/mdbox** Initially: - Set up the possibility of doing per-user mail location using _userdb_ extra fields. Then for each user: 1. Run _doveadm sync mdbox:~/mdbox_ once to do the initial conversion. 2. Run _doveadm sync mdbox:~/mdbox_ again, because the initial conversion could have taken a while and new changes could have occurred during it. This second time only applies changes, so it should be fast. 3. Update userdb to return the wanted new mail format configuration. In this scenario: **mail\_driver = mdbox** and **mail\_path = ~/mdbox**. If you're using auth cache, you need to flush it, e.g. **doveadm auth cache flush**. 4. Wait for a few seconds and then kill (doveadm kick) the user's all existing imap and pop3 sessions (that are still using maildir). 5. Run _doveadm sync maildir:~/Maildir_ with the old location to apply final changes that were possibly done. After this there should be no changes to Maildir, because the user's mail location has been changed and all existing processes using it have been killed. Once all users have been converted, you can update the global _mail\_driver_ and _mail\_path_ settings and remove the per-user mail locations from _userdb_. ## SECURITY This command cannot be used safely by untrusted users, unless the input is heavily sanitized. The destination allows running arbitrary commands. If using _dsync\_remote\_cmd_, the username and hostname may contain spaces or begin with a dash, which can allow passing unsafe parameters to `ssh`. Also since `ssh` runs commands via shell, parameters containing e.g. `;` or `&&` can result in running multiple commands. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html)` # doveadm compress-connect(1) - Establish a compress-aware imap connection ## SYNOPSIS **doveadm compress-connect** _host_ \[_port_\] ## DESCRIPTION Connects to a compression-enabled IMAP service at given _host:port_. **doveadm-compress-connect** takes care of the compression/decompression, and to switch it on at the appropriate moment when the client sends the IMAP command **COMPRESS DEFLATE** ## ARGUMENTS - _host_ - the hostname/ip address to connect to - _port_ - the port to connect to, 143 by default ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, [RFC 4978](https://datatracker.ietf.org/doc/html/rfc4978) # doveadm-copy(1) - Copy messages matching the given search query into another mailbox ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-A** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-F** _file_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-u** _user_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-A** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-F** _file_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-u** _user_ **destination** \[**user** _source\_user_\] _search\_query_ ## DESCRIPTION **doveadm move** can be used for moving mails between mailboxes for one or more users. The _search\_query_ is used to restrict which messages are moved into the _destination_ mailbox. **doveadm copy** behaves the same as **doveadm move**, except that copied messages will not be expunged after copying. In the first form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will iterate over all users, found in the configured _user\_db_ (s), and move or copy each user's messages, matching the given _search\_query_, into the user's _destination_ mailbox. In the second form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will iterate over all users, found in the given _file_, and move or copy each user's messages, matching the given _search\_query_, into the user's _destination_ mailbox. In the third form, matching mails will be moved or copied only for given _user_(s). ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _destination_ The name of the destination mailbox, into which the mails should be moved or copied. The _destination_ mailbox must exist, otherwise this command will fail. _search\_query_ Move/copy messages matching the given search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. For the _move_ command, the **mailbox** parameter is REQUIRED. For the _move_ command, a message range limiter is REQUIRED. In other words, you must have at least one query parameter that limits the message range. If all messages are needed, the "all" query parameter can be used. **user** _source\_user_ The keyword **user** followed by a valid user name. When this argument is present, `[doveadm(1)](/main/core/man/doveadm.1.html)` will apply the _search\_query_ to the _source\_user_'s mail location. **Limitation:** Currently the users, specified by **\-u** _user_ and **user** _source\_user_, must share the same system UID and GID. ## EXAMPLE Move jane's messages - received in September 2011 - from her INBOX into her archive: sh ``` doveadm move -u jane Archive/2011/09 mailbox INBOX BEFORE \ 2011-10-01 SINCE 01-Sep-2011 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-deduplicate(1) - Expunge duplicate messages ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **deduplicate** \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-m**\] _search\_query_ ## DESCRIPTION This command is used to expunge duplicated messages in mailboxes. **doveadm deduplicate** is mainly useful to revert some (more or less) accidental duplication of messages, e.g. after **doveadm copy** or **doveadm import**. `[doveadm(1)](/main/core/man/doveadm.1.html)` will delete the newest duplicated messages from the mailbox and keep the oldest. Deduplication across multiple mailboxes is not supported. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-m** if the **\-m** option is given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will deduplicate by Message-Id header. By default deduplication will be done by message GUIDs. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _search\_query_ expunge duplicates found from messages matching the given search query. Typically a search query like '**mailbox** _mailbox\_name_**OR mailbox** _other\_box_' will be sufficient. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## EXAMPLE This example shows how to list and expunge duplicate messages from a mailbox. sh ``` doveadm -f table fetch -u jane 'guid uid' mailbox a_Box | sort ``` ``` guid uid 8aad0f0a30169f4bea620000ca356bad 18751 8aad0f0a30169f4bea620000ca356bad 18756 923e301ab9219b4b4f440000ca356bad 18748 923e301ab9219b4b4f440000ca356bad 18753 ... ``` sh ``` doveadm deduplicate -u jane mailbox a_Box doveadm -f table fetch -u jane 'guid uid' mailbox a_Box | sort ``` ``` guid uid 8aad0f0a30169f4bea620000ca356bad 18751 923e301ab9219b4b4f440000ca356bad 18748 a7999e1530739c4bd26d0000ca356bad 18749 ... ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-import(1)](/main/core/man/doveadm-import.1.html)` # doveadm-dict(1) - Commands related to dictionary manipulation ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **dict** _command_ \[**\-u** _user_\] \[_OPTIONS_\] _config-filter-name_ \[_args_\] **doveadm** \[_GLOBAL OPTIONS_\] **mail dict** _command_ \[_OPTIONS_\] _config-filter-name_ \[_args_\] ## DESCRIPTION **doveadm dict** and **doveadm mail dict** can be used to query and modify dictionary entries. The **doveadm mail dict** command can be used within a specific user context. This may be useful if e.g. userdb lookup returns user-specific dict settings. Otherwise the commands behave identically. _config-filter-name_ Filter name to use when looking up the dict settings. For example _mail\_attribute_, _quota\_clone_. It's also possible to use an empty filter name and specify the dict settings using the **\-o** parameters. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **doveadm dict** command-specific _options_: **\-u** _user_ The user to use. **doveadm mail dict** command-specific _options_: **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## COMMANDS ### get **doveadm** \[_GLOBAL OPTIONS_\] dict get \[**\-u** _user_\] _config-filter-name_ _key_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict get \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _key_ Fetch a key from a dictionary. _key_ The key to fetch. ### iter **doveadm** \[_GLOBAL OPTIONS_\] dict iter \[**\-u** _user_\] \[**\-1RV**\] _config-filter-name_ _prefix_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict iter \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-1RV**\] _config-filter-name_ _prefix_ Find the keys matching the a prefix in a dictionary. **\-1** Exact match. **\-R** Recurse. **\-V** Don't print values, just key names _prefix_ The key prefix to look for. List keys into the dictionary. ### inc **doveadm** \[_GLOBAL OPTIONS_\] dict inc \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ _diff_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict inc \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ _diff_ Increment the value of a numeric key in the keys into the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. _key_ The key to increment. _diff_ The amount of the increment. ### set **doveadm** \[_GLOBAL OPTIONS_\] dict set \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] \[**\-e** _expire-secs_\] _config-filter-name_ _key_ _value_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict set \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] \[**\-e** _expire-secs_\] _config-filter-name_ _key_ _value_ Set/create keys into the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. **\-e** _expire-secs_ Set the key duration also. _key_ The key to set. _value_ The value to set. ### unset **doveadm** \[_GLOBAL OPTIONS_\] dict unset \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict unset \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ Remove a key from the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. _key_ The key to unset. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-dump(1) - Dump the content of Dovecot's binary mailbox index/log ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **dump** \[**\-t** _type_\] _path_ \[_type-specific args_\] ## DESCRIPTION Dovecot uses several binary index and log files in order to improve performance for accessing mails. For some mailbox formats, such as sdbox and mdbox, the index files are part of the format itself. For details about index files, see \[\[link,design\_index\_format\_main\]. **doveadm dump** is used to show the contents of those mailbox index/log files, in human readable format. This is mainly useful for Dovecot developers when debugging some problem. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-t** _type_ the file type of the file to be dumped. If the _type_ was omitted, `[doveadm(1)](/main/core/man/doveadm.1.html)` tries to detect the type of _path_. _type_ can be: **dbox** \=> m._n_ (sdbox or mdbox mailbox file) **fts-expunge-log** Dump the list of expunged mails in _dovecot-expunges.log_. **fts-flatcurve** Dump the keywords indexed in _fts-flatcurve_ indexes directory and their frequencies. **imap-compress** Decompress an IMAP traffic log, which contains data compressed using the IMAP COMPRESSION extension. **dcrypt-file** Dump metadata of a dcrypt encrypted file. _type-specific args_ **private\_key**\=_/path_ to decrypt file contents. **password**\=_secret_ to decrypt private key. **dcrypt-key** Dump metadata of a dcrypt key. _type-specific args_ **private\_key**\=_/path_ to decrypt file contents. **password**\=_secret_ to decrypt private key. **dump**\=_pem|dovecot|jwk_ to specify format to dump in. **index** \=> dovecot.index, dovecot.map.index _type-specific args_ **uid**\=_number_ Dump only message with UID **log** \=> dovecot.index.log, dovecot.map.index.log **mailboxlog** \=> dovecot.mailbox.log **thread** \=> dovecot.index.thread ## ARGUMENTS _path_ The path to the corresponding dbox storage, index or log file. If only a directory is specified, doveadm tries to detect the type of files under it and dumps them. ## EXAMPLE Look at the contents of a mailbox's index: sh ``` doveadm dump ~/Maildir/.work/ ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-exec(1) - Easily execute commands from Dovecot's libexec directory. ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **exec** _binary_ \[_binary arguments_\] ## DESCRIPTION This command allows administrators and local users to simply execute commands from within _/usr/libexec/dovecot_. So for example a logged in system user could start a pre-authenticated imap session, using the command: **doveadm exec imap**. An administrator would use the command **doveadm exec imap -u** _username_. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## ARGUMENTS _binary_ the name of an executable located in _/usr/libexec/dovecot_. _binary arguments_ options and arguments, which will be passed through to the _binary_. ## EXAMPLE This example demonstrates how to deliver a message from a file to a user's mailbox. sh ``` doveadm exec dovecot-lda -d user@example.net -f admin@example.net < ~/stuff/welcome.msg ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-expunge(1) - Expunge messages matching given search query ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **expunge** \[**\-S** _socket\_path_\] \[**\-d**\] **\-A** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **expunge** \[**\-S** _socket\_path_\] \[**\-d**\] **\-F** _file_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **expunge** \[**\-S** _socket\_path_\] \[**\-d**\] **\--no-userdb-lookup** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **expunge** \[**\-S** _socket\_path_\] \[**\-d**\] **\-u** _user_ _search\_query_ ## DESCRIPTION This command can be used to expunge mails matching the given search query. It is typically used to expunge old mails from users' Trash and/or Spam mailboxes. To test which messages a given search query would match, you can use _doveadm fetch_ or _doveadm search_ commands. In the first form, the command will be performed for all users. In the second form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will expunge messages of the users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the final form, only matching mails of the given _user_ (s) will be expunged. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-d** Delete the mailbox if it is empty after expunging. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _search\_query_ Expunge messages matching this search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. A mailbox parameter is REQUIRED. A message range limiter is REQUIRED. In other words, you must have at least one query parameter that limits the message range. If all messages are needed, the "all" query parameter can be used. ## EXAMPLE This example expunges messages from Spam mailbox that were saved/copied there more than two weeks ago: sh ``` doveadm expunge -u jane.doe@example.org mailbox Spam savedbefore 2w ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-search(1)](/main/core/man/doveadm-search.1.html)` # doveadm-fetch(1) - Fetch partial/full messages or message information ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **fetch** \[**\-S** _socket\_path_\] **\-A** _fields_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **fetch** \[**\-S** _socket\_path_\] **\-F** _file_ _fields_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **fetch** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _fields_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **fetch** \[**\-S** _socket\_path_\] **\-u** _user_ _fields_ _search\_query_ ## DESCRIPTION **doveadm fetch** can be used to fetch messages' contents and metadata. This can be useful for scripts and for debugging. If you want to fetch messages one at a time, see `[doveadm-search(1)](/main/core/man/doveadm-search.1.html)`. - Please respect your users' privacy. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. This command uses by default the output formatter **pager**. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _fields_ One or more result field names to display, if the _search\_query_ matches any messages. In order to specify multiple fields, enclose them in single or double quotes. Supported _fields_ are: **binary** Message body in decoded format. **binary.
** Part of the body decoded, e.g. binary.1 **body** The body of a message. **body.
** Part of the body, e.g. body.1 **body.preview** Short preview of the body. **body.snippet** Old alias for preview. **date.received** Date and time of final delivery, when the message was delivered to a user's mailbox for the first time. The internal date and time of the source message, when the message was copied by the IMAP COPY command. The date-time attribute when present, otherwise the current time, when the message was saved by the IMAP APPEND command. **date.received.unixtime** date.received as unix timestamp. **date.saved** Date and time when the message was saved to mailbox. **date.saved.unixtime** date.saved as unix timestamp. **date.sent** Date and time of the message's Date: header. **date.sent.unixtime** date.sent as unix timestamp. **flags** A message's IMAP flags, e.g. \\Seen **guid** A message's globally unique identifier. **hdr** The header of the message. **hdr.** Named header from the message. **imap.body** IMAP BODY output of the message (see [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)). **imap.bodystructure** IMAP BODYSTRUCTURE output of the message (see [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)). **imap.envelope** IMAP ENVELOPE output of the message (see [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)). **mailbox** Name of the mailbox, in which the message is stored. The name is in UTF-8. **mailbox-guid** The globally unique identifier of the mailbox, in which the message is located. **modseq** Modification sequence number for the mail. **pop3.order** A message's order number within a mailbox. **pop3.uidl** A message's unique (POP3) identifier within a mailbox. **refcount** Mail reference count, mdbox only. **seq** A message's sequence number in a mailbox. **size.physical** A message's physical size. **size.virtual** A message's virtual size, computed with CRLF line terminators. **storageid** Mailbox driver specific ID for the mail. **text** The entire message (header and body). **text.utf8** The entire message (header and body) — UTF-8 encoded. **uid** A message's unique (IMAP) identifier in a mailbox. **user** A message owner's login name. _search\_query_ Fetch messages matching this search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## EXAMPLE This example based on the first example from `[doveadm-search(1)](/main/core/man/doveadm-search.1.html)`. We are fetching the fields **mailbox** and **date.sent** from user bob's mailbox with the guid "3a94c928d66ebe4bda04000015811c6a" for the messages with the UIDs **8**, **25** and **45**. sh ``` doveadm fetch -u bob "mailbox date.sent" mailbox-guid 3a94c928d66ebe4bda04000015811c6a uid 8,25,45 ``` ``` mailbox: dovecot/pigeonhole/2.0 date.sent: 2010-01-19 01:17:41 (+0100) ^L mailbox: dovecot/pigeonhole/2.0 date.sent: 2010-01-28 09:38:49 (+0100) ^L mailbox: dovecot/pigeonhole/2.0 date.sent: 2010-03-28 18:41:14 (+0200) ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-flags(1) - Add, remove or replace messages' flags ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **flags** \[**\-S** _socket\_path_\] **\-A** _command flags_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **flags** \[**\-S** _socket\_path_\] **\-F** _file_ _command flags_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **flags** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _command flags_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **flags** \[**\-S** _socket\_path_\] **\-u** _user_ _command flags_ _search\_query_ ## DESCRIPTION This command is used to manipulate flags of messages. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _flags_ Message flags as described in [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501), section 2.3.2 (Flags Message Attribute): **\\Answered**, **\\Deleted**, **\\Draft**, **\\Flagged**, **\\Recent** and **\\Seen**. And the IMAP keywords **$Forwarded**, **$MDNSent**, **$SubmitPending** and **$Submitted** or user-defined keywords, e.g. **Junk**, **$NonSpam** or **$Label1**. One or multiple flags and/or keywords can be specified. _search\_query_ Manipulate the flags of messages matching the given search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## COMMANDS ### flags add **doveadm** \[_GLOBAL OPTIONS_\] flags add \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _flags search\_query_ This command is used to extend the current set of flags with the given _flags_. ### flags remove **doveadm** \[_GLOBAL OPTIONS_\] flags remove \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _flags search\_query_ In order to remove the given _flags_ from the current set of flags, use this command. ### flags replace **doveadm** \[_GLOBAL OPTIONS_\] flags replace \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _flags search\_query_ This command is used to replace ALL current flags with the given _flags_. ## EXAMPLE List and manipulate the message flags of the message with uid 81563: sh ``` doveadm fetch -u bob 'uid flags' mailbox dovecot uid 81563 ``` ``` uid: 81563 flags: \Answered \Seen NonJunk ``` sh ``` doveadm flags remove -u bob NonJunk mailbox dovecot uid 81563 doveadm flags add -u bob '\Flagged $Forwarded' mailbox dovecot uid 81563 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-force-resync(1) - Repair broken mailboxes ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **force-resync** \[**\-S** _socket\_path_\] **\-A** _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **force-resync** \[**\-S** _socket\_path_\] **\-F** _file_ _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **force-resync** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **force-resync** \[**\-S** _socket\_path_\] **\-u** _user_ _mailbox_ ## DESCRIPTION Under certain circumstances it may happen, that `[dovecot(1)](/main/core/man/dovecot.1.html)` is unable to automatically solve problems with mailboxes. In such situations the **force-resync** command may be helpful. It tries to fix all problems. For sdbox and mdbox mailboxes the storage files will be also checked. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _mailbox_ The name of the mailbox to fix. With mdbox all of the mailboxes are fixed, so you can use for example INBOX as the name. ## EXAMPLE Fix bob's INBOX: sh ``` doveadm force-resync -u bob INBOX ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-fs(1) - Interact with the abstract mail storage filesystem ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **fs** _command_ _config-filter-name_ \[_ARGUMENTS_\] **doveadm** \[_GLOBAL OPTIONS_\] **mail fs** _command_ \[_OPTIONS_\] _config-filter-name_ \[_ARGUMENTS_\] ## DESCRIPTION The **doveadm fs** _commands_ are used to abstractly interact with the storage driver defined in the Dovecot configuration. It allows access to the mailbox structure without needing to know details of how the storage driver is designed. _config-filter-name_ Filter name to use when looking up the fs settings. For example _mail\_attachments_, _obox_, _metacache_, _fts/dovecot_. It's also possible to use an empty filter name and specify the fs settings using the **\-o** parameter. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. ## COMMANDS ### copy **doveadm** \[_GLOBAL OPTIONS_\] fs copy _config-filter-name_ _source-path_ _dest-path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs copy \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _source-path_ _dest-path_ Copy source path to the destination path. ### delete **doveadm** \[_GLOBAL OPTIONS_\] fs delete \[**\-R**\] \[**\-n** _count_\] _config-filter-name_ _path_ \[_path_ ...\] **doveadm** \[_GLOBAL OPTIONS_\] mail fs delete \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-R**\] \[**\-n** _count_\] _config-filter-name_ _path_ \[_path_ ...\] Delete all data associated with the path provided. **\-R** Recursively delete files. **\-n** _count_ Maximum number of parallel operations to perform. ### get **doveadm** \[_GLOBAL OPTIONS_\] fs get _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs get \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Retrieve data associated with the path provided. ### iter **doveadm** \[_GLOBAL OPTIONS_\] fs iter _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs iter \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Iterate through all data files in the path provided. ### iter-dirs **doveadm** \[_GLOBAL OPTIONS_\] fs iter-dirs _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs iter-dirs \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Iterate through all directories in the path provided. ### put **doveadm** \[_GLOBAL OPTIONS_\] fs put \[**\-h** _hash_\] \[**\-m** _key_\=_value_\] _config-filter-name_ _input\_path_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs put \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-h** _hash_\] \[**\-m** _key_\=_value_\] _config-filter-name_ _path_ Store data at the path provided. **\-h** _hash_ Save provided hash as content hash. Must be either MD5 or SHA256 in hexdigits. **\-m** _key=value_ Assign a value to a metadata. Can be provided as many times as needed. Requires either metawrap as driver or metadata capable FS-driver. ### stat **doveadm** \[_GLOBAL OPTIONS_\] fs stat _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs stat \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Retrieve files status for the path provided. Currently, only the total size (in bytes) of the item is returned. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## EXAMPLE How to decompress a file. sh ``` doveadm -o fs=compress,posix -o fs/compress/fs_driver=compress -o fs/posix/fs_driver=posix -o fs_posix_prefix=/tmp/ fs get "" test.gz ``` Using a named filter. sh ``` doveadm mail fs -u testuser get obox path/to/file ``` ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-fts(1) - Manipulate the Full Text Search (FTS) index ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **fts** _command_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION The doveadm fts _COMMANDS_ can be used to manipulate the Full Text Search (FTS) index. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. This command uses by default the output formatter **flow** (without the _key_\=prefix). ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _namespace_ The name of a namespace, e.g. the name of the shared namespace. When no namespace was given, the user's private namespace will be used. ## COMMANDS ### fts optimize **doveadm** \[_GLOBAL OPTIONS_\] fts optimize \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_namespace_\] Optimize the full text search index. This is also done automatically by the full text search engines, but this enforces it to happen. ### fts rescan **doveadm** \[_GLOBAL OPTIONS_\] fts rescan \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_namespace_\] Scan what mails exist in the full text search index and compare those to what actually exist in mailboxes. This removes mails from the index that have already been expunged and makes sure that the next **doveadm index** will index all the missing mails (if any). Note that currently most FTS drivers do not implement this properly, but instead they delete all the FTS indexes. This may change in the future versions. ### fts flatcurve check **doveadm** \[_GLOBAL OPTIONS_\] fts flatcurve check \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_mailbox_ ...\] Run a simple check on Dovecot Xapian databases, and attempt to fix basic errors (it is the same checking done by the xapian-check command with the `-F` command-line option). _mailbox_ is the list of mailboxes to process. It is possible to use wildcards (\* and ?) in this value. For each mailbox that has FTS data, it outputs the following key/value fields: | Key | Value | | --- | --- | | mailbox | The human-readable mailbox name. (key is hidden) | | guid | The GUID of the mailbox. | | errors | The number of errors reported by the Xapian library. | | shards | The number of index shards processed. | ### fts flatcurve remove **doveadm** \[_GLOBAL OPTIONS_\] fts flatcurve remove \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_mailbox_ ...\] Removes all FTS data for a mailbox. _mailbox_ is the list of mailboxes to process. It is possible to use wildcards (\* and ?) in this value. For each mailbox removed, it outputs the following key/value fields: | Key | Value | | --- | --- | | mailbox | The human-readable mailbox name. (key is hidden) | | guid | The GUID of the mailbox. | ### fts flatcurve rotate **doveadm** \[_GLOBAL OPTIONS_\] fts flatcurve rotate \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_mailbox_ ...\] Triggers an index rotation for a mailbox. _mailbox_ is the list of mailboxes to process. It is possible to use wildcards (\* and ?) in this value. For each mailbox rotated, it outputs the following key/value fields: | Key | Value | | --- | --- | | mailbox | The human-readable mailbox name. (key is hidden) | | guid | The GUID of the mailbox. | ### fts flatcurve stats **doveadm** \[_GLOBAL OPTIONS_\] fts flatcurve stats \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[_mailbox_ ...\] Returns FTS data for a mailbox. _mailbox_ is the list of mailboxes to process. It is possible to use wildcards (\* and ?) in this value. For each mailbox that has FTS data, it outputs the following key/value fields: | Key | Value | | --- | --- | | mailbox | The human-readable mailbox name. (key is hidden) | | guid | The GUID of the mailbox. | | last\_uid | The last UID indexed in the mailbox. | | messages | The number of messages indexed in the mailbox. | | shards | The number of index shards. | | version | The (Dovecot internal) version of the FTS data. | ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` Additional resources: - [fts plugin](/main/core/plugins/fts.html) # doveadm-help(1) - Show information about doveadm commands ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **help** \[_command_\] ## DESCRIPTION With no _command_ argument given, **doveadm help** will print: - the synopsis for the most of the `[doveadm(1)](/main/core/man/doveadm.1.html)` commands, - groups of commands, e.g. **log** or **mailbox**. When the name of a _command_ (or a group) was given, it will show the man page for that command. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-import(1) - Import messages matching given search query ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **import** \[**\-S** _socket\_path_\] \[**\-U** _source\_user_\] \[**\-s**\] **\-A**_source\_mail\_driver_:_source\_mail\_path_ _dest\_parent_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **import** \[**\-S** _socket\_path_\] \[**\-U** _source\_user_\] \[**\-s**\] **\-F** _file_ _source\_mail\_driver_:_source\_mail\_path_ _dest\_parent_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **import** \[**\-S** _socket\_path_\] \[**\-U** _source\_user_\] \[**\-s**\] **\-u** _user_ _source\_mail\_driver_:_source\_mail\_path_ _dest\_parent_ _search\_query_ ## DESCRIPTION This command can be used to import mails from another mail storage specified by _source\_mail\_driver_, _source\_mail\_path_, and other settings specified via **\-p** parameters to one or more user's mailboxes. All the mailboxes are imported under the given _dest\_parent_ mailbox, or to root level if _dest\_parent_ is empty (""). The _search\_query_ can be used to restrict which mailboxes or messages are imported. By default the import is done in destination user's context, you can use -U to change this. In the first form, the mails will be imported for all users. In the second form, the mails will be imported for all users listed in the given _file_. In the final form, the mails will be imported only for given _user_ (s). ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-p** _setting_**\=**_value_ Overrides the configuration _setting_ for source storage. **\-s** When the **\-s** option is present, _dest\_parent_ and all new mailboxes under it will be subscribed to. **\-U username** When the **\-U** option is present, the source box is opened with given username. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _dest\_parent_ The name of the destination mailbox, under which the mails should be imported. `[doveadm(1)](/main/core/man/doveadm.1.html)` will create the _dest\_parent_ mailbox if it doesn't exist. _search\_query_ Copy messages matching this search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. _source\_mail\_driver_:_source\_mail\_path_ This argument specifies the _mail\_driver_ and _mail\_path_ settings for the source location. The **\-p** parameter can optionally be used to specify additional settings. For example: **maildir:/backup/20101126/jane.doe/Maildir** or **\-p mail\_alt\_path=/nfsmount/john.doe/mdbox mdbox:/srv/mail/john.doe/mdbox** ## EXAMPLE This example imports all mails from a backup under a _backup-20101026_ mailbox: sh ``` doveadm import -u jane.doe@example.org \ mdbox:/backup/20101026/jane.doe/mdbox backup-20101026 all ``` Another example that imports only messages from [foo@example.org](mailto:foo@example.org) in the backup mdbox's INBOX to jane's INBOX: sh ``` doveadm import -u jane.doe@example.org \ mdbox:~/mdbox-backup "" mailbox INBOX from foo@example.org ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` # doveadm-index(1) - Index mailboxes ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **index** \[**\-S** _socket\_path_\] **\-A** \[**\-q**\] \[**\-n** _max\_recent_\] _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **index** \[**\-S** _socket\_path_\] **\-F** _file_ \[**\-q**\] \[**\-n** _max\_recent_\] _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **index** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** \[**\-q**\] \[**\-n** _max\_recent_\] _mailbox_ **doveadm** \[_GLOBAL OPTIONS_\] **index** \[**\-S** _socket\_path_\] **\-u** _user_ \[**\-q**\] \[**\-n** _max\_recent_\] _mailbox_ ## DESCRIPTION Add unindexed messages in a mailbox into index/cache file. If full text search is enabled, also add unindexed messages to the fts database. The caching adds only the fields that were previously added to the mailbox's caching decisions, so it won't do anything useful for mailboxes that user's client hasn't accessed yet. You can use **doveadm dump** command to show a specific mailbox's current caching decisions. Messages can also be added automatically to full text search index using: /etc/dovecot/conf.d/90-plugin.conf ``` fts_autoindex = yes ``` ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-n** _max\_recent_ An integer value, which specifies the maximum number of \\Recent messages in mailboxes. If the mailbox contains more than _max\_recent_ messages with \\Recent flag set, the mailbox will not be indexed. This may be useful to avoid unnecessary indexing for large mailboxes that are never opened. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-q** Queues the indexing to be run by indexer process. Without -q the indexing is done directly by the `[doveadm(1)](/main/core/man/doveadm.1.html)` process. Some drivers can't handle multiple processes updating the indexes simultaneously, so -q should usually be used on production. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _mailbox_ The name of the mailbox to index. ## EXAMPLE Index bob's INBOX: sh ``` doveadm index -u bob INBOX ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-indexer(1) - Commands related to managing the indexer process ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **indexer** _command_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION **doveadm indexer** can be used to manage the indexer process. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## COMMANDS ### indexer add **doveadm** \[_GLOBAL OPTIONS_\] indexer add \[**\-h**\] \[**\-n** _max\_recent_\] _user_ _mailbox_ Add indexing request for the given _user_ and the _mailbox_ to the indexer queue. It works the same as the **doveadm index -q** command. **\-h** Add the indexing request to the head of the queue. By default the request is added to the tail of the queue. **\-n** _max\_recent_ An integer value, which specifies the maximum number of \\Recent messages in mailboxes. If the mailbox contains more than _max\_recent_ messages with \\Recent flag set, the mailbox will not be indexed. This may be useful to avoid unnecessary indexing for large mailboxes that are never opened. ### indexer remove **doveadm** \[_GLOBAL OPTIONS_\] indexer remove _user\_mask_ \[_mailbox\_mask_\] Remove all indexer requests for the matching _user\_mask_ (and _mailbox\_mask_). It's possible to use wildcards. Requests that are currently processed by indexer-worker are not listed; use **doveadm kick** instead to kick them. ### indexer list **doveadm** \[_GLOBAL OPTIONS_\] indexer list _user\_mask_ List all the queued indexing requests matching _user\_mask_. It's possible to use wildcards. Requests that are currently processed by indexer-worker are not listed; use **doveadm who** instead to see them. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-instance(1) - Manage the list of running Dovecot instances ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **instance list** \[**\-c**\] \[_name_\] **doveadm** \[_GLOBAL OPTIONS_\] **instance remove** _name_ ## DESCRIPTION The **doveadm instance** commands are used to manage the list of Dovecot instances running on the server. In most installations there is only one Dovecot instance, but in some cases is may be useful to have more (e.g. running proxy and backend in the same server). Instances are added to the list automatically when Dovecot is started. Each instance is uniquely identified by its _base\_dir_ setting. Instances can be named by setting _instance\_name_ in each instance's _dovecot.conf_. When an instance is named, it can be accessed easily by giving **\-i** _instance\_name_ command line parameter for Dovecot binaries (e.g. doveadm). ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## ARGUMENTS _name_ The value of an instance's _instance\_name_ setting. Each instance can also be uniquely identified by its _base\_dir_ setting. ## COMMANDS ### instance list **doveadm** \[_GLOBAL OPTIONS_\] instance list \[**\-c**\] \[_name_\] This command lists the seen Dovecot instances. **\-c** Output the config path instead of instance information. ### instance remove **doveadm** \[_GLOBAL OPTIONS_\] instance remove _name_ This command removes the specified instance. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-kick(1) - Disconnect users by user name and/or IP address ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **kick** \[**\-a** _anvil\_socket\_path_\] \[**\-f** _passdb\_field_\] \[**\-h** _dest\_host_\] _user\_mask_ **doveadm** \[_GLOBAL OPTIONS_\] **kick** \[**\-a** _anvil\_socket\_path_\] \[**\-f** _passdb\_field_\] \[**\-h** _dest\_host_\] _ip_ \[**/** _bits_\] **doveadm** \[_GLOBAL OPTIONS_\] **kick** \[**\-a** _anvil\_socket\_path_\] \[**\-f** _passdb\_field_\] \[**\-h** _dest\_host_\] _user\_mask_ _ip_ \[**/** _bits_\] **doveadm** \[_GLOBAL OPTIONS_\] **kick** \[**\-a** _anvil\_socket\_path_\] \[**\-f** _passdb\_field_\] **\-h** _dest\_host_ ## DESCRIPTION **doveadm**'s **kick** command is used to disconnect users by _user\_mask_ and/or the _ip_ address, from which they are connected. In the first form, all users, whose login name matches the _user\_mask_ argument, will be disconnected. In the second form, all users, connected from the given IP address or network range, will be disconnected. In the third form, only users connected from the given IP address or networks range and a matching login name will be disconnected. In the last form, all proxy connections to the given destination host are disconnected. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-a** _anvil\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/anvil_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-f** _passdb\_field_ Alternative username field to use for kicking, as returned by passdb. Only the passdb fields beginning with the _user\__ prefix are tracked. **\-h** _dest\_host_ Disconnect proxy connections to the given _dest\_host_. ## ARGUMENTS _ip_ \[/_bits_\] _ip_ or _ip_ **/** _bits_ is the host or network, from which the users are connected. _user\_mask_ Is a user's login name, or the alternative username (user\_\* field) if the **\-f** parameter is used. Depending on the configuration, a login name may be for example **jane** or **[john@example.com](mailto:john@example.com)**. It's also possible to use '\*****' and '**?**' wildcards (e.g. -u \*@example.org). ## EXAMPLE If you don't want to disconnect all users at once, you can check who's currently logged in. The first example demonstrates how to disconnect all users whose login name is 3 characters long and begins with **ba**: sh ``` doveadm who -1 ja\* ``` ``` username service pid ip jane imap 8192 ::1 jano imap 8196 ::2 james imap 8203 2001:db8:543:2::1 ``` sh ``` doveadm kick jan? # The connections for jane and jano are kicked. ``` The next example shows how to kick user foo's connections from 192.0.2.\*: sh ``` doveadm who -1 foo ``` ``` username service pid ip foo imap 8135 fd95:4eed:38ba::25 foo imap 9112 192.0.2.53 foo imap 8216 192.0.2.111 ``` sh ``` doveadm kick foo 192.0.2.0/24 doveadm who f\* ``` ``` username # service (pids) (ips) foo 1 imap (8135) (fd95:4eed:38ba::25) ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-log(1) - Locate, test or reopen Dovecot's log files ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **log errors** \[**\-s** _min\_timestamp_\] **doveadm** \[_GLOBAL OPTIONS_\] **log find** \[_directory_\] **doveadm** \[_GLOBAL OPTIONS_\] **log reopen** **doveadm** \[_GLOBAL OPTIONS_\] **log test** ## DESCRIPTION The **doveadm log** _commands_ are used to locate and reopen the log files of `[dovecot(1)](/main/core/man/dovecot.1.html)`. It's also possible to test the configured targets of the _log\_path_ settings. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## COMMANDS ### log errors **doveadm** \[_GLOBAL OPTIONS_\] log errors \[**\-s** _min\_timestamp_\] The **log errors** command is used to show the last - up to 1,000 - errors and warnings. If no output is generated, no errors have occurred since the last start. **\-s** _min\_timestamp_ An integer value, representing seconds since the epoch - also known as Unix timestamp. When a min\_timestamp was given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will only show errors occurred since that point in time. ### log find **doveadm** \[_GLOBAL OPTIONS_\] log find \[_directory_\] The **log find** command is used to show the location of the log files, to which `[dovecot(1)](/main/core/man/dovecot.1.html)` logs its messages through **syslogd** (8) and `[doveadm(1)](/main/core/man/doveadm.1.html)` could not find any log files, you can specify the _directory_ where your syslogd writes its log files. ### log reopen **doveadm** \[_GLOBAL OPTIONS_\] log reopen This command causes **doveadm** to send a SIGUSR1 signal to master process, which causes it to reopen all log files configured in the _log\_path_, _info\_log\_path_ and _debug\_log\_path_ settings. These settings are configured in _/etc/dovecot/conf.d/10-logging.conf_. The master process also signals the log process to do the same. This is mainly useful after manually rotating the log files. ### log test **doveadm** \[_GLOBAL OPTIONS_\] log test This command causes **doveadm** to write the message "This is Dovecot's _priority_ log (_timestamp_)" to the configured log files. The used priorities are: **debug**, **info**, **warning**, **error** and **fatal**. ## EXAMPLE This example shows how to locate the log files used by `[dovecot(1)](/main/core/man/dovecot.1.html)`: sh ``` doveadm log find ``` ``` Looking for log files from /var/log Debug: /var/log/dovecot.debug Info: /var/log/mail.log Warning: /var/log/mail.log Error: /var/log/mail.log Fatal: /var/log/mail.log ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-dict(1) - Commands related to dictionary manipulation in user context ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **dict** _command_ \[**\-u** _user_\] \[_OPTIONS_\] _config-filter-name_ \[_args_\] **doveadm** \[_GLOBAL OPTIONS_\] **mail dict** _command_ \[_OPTIONS_\] _config-filter-name_ \[_args_\] ## DESCRIPTION **doveadm dict** and **doveadm mail dict** can be used to query and modify dictionary entries. The **doveadm mail dict** command can be used within a specific user context. This may be useful if e.g. userdb lookup returns user-specific dict settings. Otherwise the commands behave identically. _config-filter-name_ Filter name to use when looking up the dict settings. For example _mail\_attribute_, _quota\_clone_. It's also possible to use an empty filter name and specify the dict settings using the **\-o** parameters. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **doveadm dict** command-specific _options_: **\-u** _user_ The user to use. **doveadm mail dict** command-specific _options_: **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## COMMANDS ### get **doveadm** \[_GLOBAL OPTIONS_\] dict get \[**\-u** _user_\] _config-filter-name_ _key_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict get \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _key_ Fetch a key from a dictionary. _key_ The key to fetch. ### iter **doveadm** \[_GLOBAL OPTIONS_\] dict iter \[**\-u** _user_\] \[**\-1RV**\] _config-filter-name_ _prefix_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict iter \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-1RV**\] _config-filter-name_ _prefix_ Find the keys matching the a prefix in a dictionary. **\-1** Exact match. **\-R** Recurse. **\-V** Don't print values, just key names _prefix_ The key prefix to look for. List keys into the dictionary. ### inc **doveadm** \[_GLOBAL OPTIONS_\] dict inc \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ _diff_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict inc \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ _diff_ Increment the value of a numeric key in the keys into the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. _key_ The key to increment. _diff_ The amount of the increment. ### set **doveadm** \[_GLOBAL OPTIONS_\] dict set \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] \[**\-e** _expire-secs_\] _config-filter-name_ _key_ _value_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict set \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] \[**\-e** _expire-secs_\] _config-filter-name_ _key_ _value_ Set/create keys into the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. **\-e** _expire-secs_ Set the key duration also. _key_ The key to set. _value_ The value to set. ### unset **doveadm** \[_GLOBAL OPTIONS_\] dict unset \[**\-u** _user_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ **doveadm** \[_GLOBAL OPTIONS_\] mail dict unset \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-t** _timestamp-msecs_\] _config-filter-name_ _key_ Remove a key from the dictionary. **\-t** _timestamp-msecs_ Set the timestamp also. _key_ The key to unset. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-mail-fs(1) - Interact with the abstract mail storage filesystem in user context ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **fs** _command_ _config-filter-name_ \[_ARGUMENTS_\] **doveadm** \[_GLOBAL OPTIONS_\] **mail fs** _command_ \[_OPTIONS_\] _config-filter-name_ \[_ARGUMENTS_\] ## DESCRIPTION The **doveadm fs** _commands_ are used to abstractly interact with the storage driver defined in the Dovecot configuration. It allows access to the mailbox structure without needing to know details of how the storage driver is designed. _config-filter-name_ Filter name to use when looking up the fs settings. For example _mail\_attachments_, _obox_, _metacache_, _fts/dovecot_. It's also possible to use an empty filter name and specify the fs settings using the **\-o** parameter. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. ## COMMANDS ### copy **doveadm** \[_GLOBAL OPTIONS_\] fs copy _config-filter-name_ _source-path_ _dest-path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs copy \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _source-path_ _dest-path_ Copy source path to the destination path. ### delete **doveadm** \[_GLOBAL OPTIONS_\] fs delete \[**\-R**\] \[**\-n** _count_\] _config-filter-name_ _path_ \[_path_ ...\] **doveadm** \[_GLOBAL OPTIONS_\] mail fs delete \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-R**\] \[**\-n** _count_\] _config-filter-name_ _path_ \[_path_ ...\] Delete all data associated with the path provided. **\-R** Recursively delete files. **\-n** _count_ Maximum number of parallel operations to perform. ### get **doveadm** \[_GLOBAL OPTIONS_\] fs get _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs get \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Retrieve data associated with the path provided. ### iter **doveadm** \[_GLOBAL OPTIONS_\] fs iter _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs iter \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Iterate through all data files in the path provided. ### iter-dirs **doveadm** \[_GLOBAL OPTIONS_\] fs iter-dirs _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs iter-dirs \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Iterate through all directories in the path provided. ### put **doveadm** \[_GLOBAL OPTIONS_\] fs put \[**\-h** _hash_\] \[**\-m** _key_\=_value_\] _config-filter-name_ _input\_path_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs put \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] \[**\-h** _hash_\] \[**\-m** _key_\=_value_\] _config-filter-name_ _path_ Store data at the path provided. **\-h** _hash_ Save provided hash as content hash. Must be either MD5 or SHA256 in hexdigits. **\-m** _key=value_ Assign a value to a metadata. Can be provided as many times as needed. Requires either metawrap as driver or metadata capable FS-driver. ### stat **doveadm** \[_GLOBAL OPTIONS_\] fs stat _config-filter-name_ _path_ **doveadm** \[_GLOBAL OPTIONS_\] mail fs stat \[**\-A** | **\-u** _user_ | **\-F** _file_\] \[**\-S** _socket\_path_\] _config-filter-name_ _path_ Retrieve files status for the path provided. Currently, only the total size (in bytes) of the item is returned. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## EXAMPLE How to decompress a file. sh ``` doveadm -o fs=compress,posix -o fs/compress/fs_driver=compress -o fs/posix/fs_driver=posix -o fs_posix_prefix=/tmp/ fs get "" test.gz ``` Using a named filter. sh ``` doveadm mail fs -u testuser get obox path/to/file ``` ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-mailbox-cryptokey(1) - Mail crypt plugin management ## SYNOPSIS **doveadm** \[**\-o** _crypt\_user\_key\_password=password_\] \[_GLOBAL OPTIONS_\] _mailbox cryptokey export|generate|list|password_ \[_options_\] \[_arguments_\] ## DESCRIPTION Generate new keypair for user or folder. The new keypair is marked as active. ## OPTIONS **doveadm mailbox cryptokey** can be used to manage user's cryptographic keys. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. **\-o** _crypt\_user\_key\_password=password_ Dovecot option, needed if you use password protected keys ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## SUBCOMMANDS **export** \[**\-U**\] | _mailbox-mask_ **\-U** Operate on user keypair only Exports user's or folder's keypair(s) in PEM format. If the keys are password protected, -o is needed. **generate** \[**\-Rf** \[**\-U**\] | _mailbox-mask_\] **\-U** Operate on user keypair only **\-R** Re-encrypt all folder keys with current active user key **\-f** Force keypair creation, normally keypair is only created if none found Generates new keypair for user or folder. If you want to generate new user key and use it to secure your folder keys, use generate -u username -UR. If you want to password-protect your key here, use -o. **list** \[**\-U**\] | _mailbox-mask_ **\-U** Operate on user keypair only List all keys for user or folder. No password is required. **password** \[**\-N** | **\-n** _password_\] \[**\-O**|**\-o** _password_\] \[**\-C**\] **\-O** Ask for old password **\-o old-password** Provide old password **\-N** Ask for new password **\-n new-password** Provide new password **\-C** Clear (unset/remove) password. Your key will not be protected by password. Set, change or clear password from your user key. ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html)` # doveadm-mailbox(1) - Commands related to handling mailboxes ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] _mailbox\_cmd_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION **doveadm mailbox** can be used to query and modify mailboxes. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _mailbox_ Is the name of a _mailbox_, as visible to IMAP clients, except in UTF-8 format. The hierarchy separator between a parent and child mailbox is commonly '**/**' or '**.**', but this depends on your selected mailbox storage format and namespace configuration. The mailbox names may also require a namespace prefix. ## COMMANDS ### mailbox create **doveadm** \[_GLOBAL OPTIONS_\] mailbox create \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-g** _guid_\] \[**\-s**\] _mailbox_... This command is used to create one or more mailboxes. The mailbox format of the created mailboxes depends on the _mail\_driver_ setting, or the user's _mail_ field, returned by the userdb. **\-g** _guid_ Create the mailbox with the specified GUID. **\-s** When this option was given, the created mailboxes will be also added to the user's subscriptions. ### mailbox cryptokey **doveadm** \[_GLOBAL OPTIONS_\] mailbox cryptokey **export|generate|list|password** This command is used to manage mail crypt plugin cryptographic keys. Please see `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html)` for more details. ### mailbox delete **doveadm** \[_GLOBAL OPTIONS_\] mailbox delete \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] \[**\-r**\] \[**\-e**\] \[**\-Z**\] _mailbox_... This command deletes a mailbox and expunges all the messages it contains. If the mailbox has any children, they won't be deleted, unless **\-r** is given. **\-r** Delete mailboxes recursively **\-s** Unsubscribe deleted mailboxes. **\-e** Require mailboxes to be empty before deleting. **\-Z** Delete the mailbox as efficiently as possible, but the user may not be in fully consistent state afterwards. For example quota may be wrong. This option is mainly useful when deleting the entire user. ### mailbox list **doveadm** \[_GLOBAL OPTIONS_\] mailbox list \[**\-7** | **\-8**\] \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] \[_mailbox_ ...\] To get an overview of existing mailboxes use this command. It's also possible to use wildcards in the _mailbox_ name. When the **\-s** option is present, only subscribed mailboxes will be listed. Listed subscriptions may also contain mailboxes that are already deleted. **\-7** Lists the mailboxes with mUTF-7 encoding. **\-8** Lists the mailboxes with UTF-8 encoding. ### mailbox metadata list **doveadm** \[_GLOBAL OPTIONS_\] mailbox metadata list \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] \[**\-p**\] _mailbox_ \[_key-prefix_\] **\-s** Allows to specify _mailbox_ "", which points to the server metadata instead of mailboxes' matadata. It has no effect if mailbox is anything else than an empty string. **\-p** Prepend prefix. _mailbox_ The target mailbox (mandatory). If **\-s** is specified, the empty string "" can be used to indicate access to server metadata. _key-prefix_ The key prefix to look for. All keys will be listed if not provided. ### mailbox metadata get **doveadm** \[_GLOBAL OPTIONS_\] mailbox metadata get \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] _mailbox_ _key_ **\-s** Allows to specify _mailbox_ "", which points to the server metadata instead of mailboxes' matadata. It has no effect if mailbox is anything else than an empty string. _mailbox_ The target mailbox (mandatory). If **\-s** is specified, the empty string "" can be used to indicate access to server metadata. _key_ The key to retrieve. ### mailbox metadata set **doveadm** \[_GLOBAL OPTIONS_\] mailbox metadata set \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] _mailbox_ _key_ _value_ **\-s** Allows to specify _mailbox_ "", which points to the server metadata instead of mailboxes' matadata. It has no effect if mailbox is anything else than an empty string. _mailbox_ The target mailbox (mandatory). If **\-s** is specified, the empty string "" can be used to indicate access to server metadata. _key_ The key to set. _value_ The value to set. ### mailbox metadata unset **doveadm** \[_GLOBAL OPTIONS_\] mailbox metadata unset \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] _mailbox_ _key_ **\-s** Allows to specify _mailbox_ "", which points to the server metadata instead of mailboxes' matadata. It has no effect if mailbox is anything else than an empty string. _mailbox_ The target mailbox (mandatory). If **\-s** is specified, the empty string "" can be used to indicate access to server metadata. _key_ The key to unset. ### mailbox mutf7 **doveadm** \[_GLOBAL OPTIONS_\] mailbox mutf7 \[**\-7** | **\-8**\] _name_... The **mailbox mutf7** command may be used to convert the international mailbox _name_ into a modified version of the UTF-7 encoding and vice versa. See [RFC 3501 (section 5.1.3)](https://datatracker.ietf.org/doc/html/rfc3501#section-5.1.3) (Mailbox International Naming Convention). **\-7** Indicates that the _name_'s string representation is mUTF-7 encoded and it should be converted to UTF-8. **\-8** Indicates that the _name_'s is UTF-8 encoded and should be converted to mUTF-7 (default). _name_ One or more mailbox names that should be converted. ### mailbox path **doveadm** \[_GLOBAL OPTIONS_\] mailbox path \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] \[**\-t** _type_\] _mailbox_ The **mailbox path** command returns the filesystem path for the given mailbox. By default, this is the path to the "index" directory. **\-t** _type_ Output the path to this mailbox location type. One of: - **alt-dir** - **alt-mailbox** - **control** - **dir** - **index** (DEFAULT) - **index-cache** - **index-private** - **list-index** - **mailbox** _mailbox_ The mailbox to query (mandatory). ### mailbox rename **doveadm** \[_GLOBAL OPTIONS_\] mailbox rename \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-s**\] _old\_name_ _new\_name_ The **mailbox rename** command is used to rename the mailbox _old\_name_ to _new\_name_. When the **\-s** option is given, _old\_name_ will be unsubscribed and _new\_name_ will be subscribed. ### mailbox status **doveadm** \[_GLOBAL OPTIONS_\] mailbox status \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-t**\] _fields_ _mailbox_... Show the **status** of one or more mailboxes. The _mailbox_ name may also contain wildcards. This command uses by default the output _formatter_ **flow**. **\-t** Summarize the values of the status _fields_ **messages**, **recent**, **unseen** and/or **vsize** of multiple mailboxes to a sum (total). **fields** Specify the status _fields_ which should be shown. In order to specify multiple status _fields_, enclosed them in quotes. **all** This is a special status field name. It means show all of the following _fields_. When the **\-t** option is present, it means show only the **messages**, **recent**, **unseen** and **vsize**_fields_. **guid** The _mailbox_'s globally unique identifier. **highestmodseq** The highest mod-sequence value of all messages in the _mailbox_. **messages** The number of messages in the _mailbox_. **deleted** The number of messages with the \\Deleted flag set. **recent** The number of messages with the \\Recent flag set. **uidnext** The next unique identifier value. **uidvalidity** The unique identifier validity value. **unseen** The message sequence number of the first unseen message in the _mailbox_. **vsize** The _mailbox_'s virtual size, computed with CRLF line terminators. **firstsaved** Saved time of the first mail in the mailbox. ### mailbox subscribe **doveadm** \[_GLOBAL OPTIONS_\] mailbox subscribe \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox_ _..._ This command is used to subscribe one or more mailboxes. ### mailbox unsubscribe **doveadm** \[_GLOBAL OPTIONS_\] mailbox unsubscribe \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox_ _..._ This command is used to unsubscribe one or more mailboxes. ### mailbox update **doveadm** \[_GLOBAL OPTIONS_\] mailbox update \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\--mailbox-guid** _guid_\] \[**\--uid-validity** _uid_\] \[**\--min-next-uid** _uid_\] \[**\--min-first-recent-uid** _uid_\] \[**\--min-highest-modseq** _seq_\] \[**\--min-highest-pvt-modseq** _seq_\] _mailbox_ _..._ This command is used to set UID validity, next UID, first recent UID and modification sequence values. Usually this is only ever to be used during migration, or restoring mailbox after disaster. Settings these values is highly discouraged, and is not supported for all mail backends. ### mailbox cache purge **doveadm** \[_GLOBAL OPTIONS_\] mailbox cache purge \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _mailbox_ _..._ Purge the dovecot.index.cache file. Most importantly this frees up disk space from mails that were already deleted. Normally there i no need to run this command manually, because the compression is also run automatically. ### mailbox cache decision **doveadm** \[_GLOBAL OPTIONS_\] mailbox cache decision \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\--all**\] \[**\--fields** _list of fields_\] \[**\--last-used** _unix timestamp_\] \[**\--decision** _no|temp|yes_\] _mailbox_ _..._ This command is used to list or change caching decisions for field(s) in mailbox(es). You can list decisions by leaving out decision and last-used parameters. **\--all** List or change all fields. **\--fields** List or change these comma/space separated fields. **\--decision** Set field caching decision. Yes means it's always cached. Temp means it's provisionally cached. No means the field is not cached. **\--last-used** Set field's last used timestamp. NOTE: This command cannot be used to add new fields to cache! You need to first add them to configuration. Setting caching to no will not immediately drop field from cache, it will stop adding the field to cache. ### mailbox cache remove **doveadm** \[_GLOBAL OPTIONS_\] mailbox cache remove \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _search query_ Remove any matching mail(s) from cache. WARNING! This command can erase ALL cached data, causing system slowness. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for the search query syntax. ## EXAMPLE List subscribed mailboxes, beginning with 'dovecot', of user bob: sh ``` doveadm mailbox list -s -u bob dovecot\* ``` ``` dovecot dovecot/pigeonhole dovecot/pigeonhole/2.0 ``` Now have a look at the status of user bob's dovecot mailboxes: sh ``` doveadm -f table mailbox status -u bob "messages vsize" dovecot\* ``` ``` mailbox messages vsize dovecot 20501 93968492 dovecot/pigeonhole 0 0 dovecot/pigeonhole/2.0 47 323474 ``` Converting an internationalized mailbox name from mUTF-7 to UTF-8 and vice versa: sh ``` doveadm mailbox mutf7 -7 "~peter/mail/&U,BTFw-/&ZeVnLIqe-" ``` ``` ~peter/mail/台北/日本語 ``` sh ``` doveadm mailbox mutf7 ~peter/mail/台北/日本語 ``` ``` ~peter/mail/&U,BTFw-/&ZeVnLIqe- ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-move(1) - Move messages matching the given search query into another mailbox ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-A** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-F** _file_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **move** \[**\-S** _socket\_path_\] **\-u** _user_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-A** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-F** _file_ **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** **destination** \[**user** _source\_user_\] _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] **copy** \[**\-S** _socket\_path_\] **\-u** _user_ **destination** \[**user** _source\_user_\] _search\_query_ ## DESCRIPTION **doveadm move** can be used for moving mails between mailboxes for one or more users. The _search\_query_ is used to restrict which messages are moved into the _destination_ mailbox. **doveadm copy** behaves the same as **doveadm move**, except that copied messages will not be expunged after copying. In the first form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will iterate over all users, found in the configured _user\_db_ (s), and move or copy each user's messages, matching the given _search\_query_, into the user's _destination_ mailbox. In the second form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will iterate over all users, found in the given _file_, and move or copy each user's messages, matching the given _search\_query_, into the user's _destination_ mailbox. In the third form, matching mails will be moved or copied only for given _user_(s). ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _destination_ The name of the destination mailbox, into which the mails should be moved or copied. The _destination_ mailbox must exist, otherwise this command will fail. _search\_query_ Move/copy messages matching the given search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. For the _move_ command, the **mailbox** parameter is REQUIRED. For the _move_ command, a message range limiter is REQUIRED. In other words, you must have at least one query parameter that limits the message range. If all messages are needed, the "all" query parameter can be used. **user** _source\_user_ The keyword **user** followed by a valid user name. When this argument is present, `[doveadm(1)](/main/core/man/doveadm.1.html)` will apply the _search\_query_ to the _source\_user_'s mail location. **Limitation:** Currently the users, specified by **\-u** _user_ and **user** _source\_user_, must share the same system UID and GID. ## EXAMPLE Move jane's messages - received in September 2011 - from her INBOX into her archive: sh ``` doveadm move -u jane Archive/2011/09 mailbox INBOX BEFORE \ 2011-10-01 SINCE 01-Sep-2011 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-penalty(1) - Show current penalties ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **penalty** \[**\-a** _anvil\_socket\_path_\] \[_ip_ \[**/** _mask_\]\] ## DESCRIPTION The **doveadm penalty** command can be used to see the current penalties. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-a** _anvil\_socket\_path_ This option is used to specify an alternative socket. The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/anvil_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. ## ARGUMENTS _ip_ \[/_mask_\] To reduce/filter the output supply an IP address or a network range in CIDR notation (ip/mask). ## EXAMPLE sh ``` doveadm penalty ``` ``` IP penalty last_penalty last_update 192.0.2.222 3 2010-06-15 15:19:27 15:19:27 192.0.2.53 3 2010-06-15 15:19:34 15:19:34 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-process-status(1) - Show information about dovecot processes ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **process status** \[_service_ \[...\]\] ## DESCRIPTION **doveadm process status** produces a table with a line for each process, containing the following details: _name_ the name of the process _pid_ the pid of the process _available\_count_ the number of further clients that can connect to the process _total\_count_ the number of connected clients _idle\_start_ timestamp when the process entered the idle status, 0 if active _last\_status\_update_ timestamp of the latest update from the process _last\_kill\_sent_ timestamp of the latest SIGINT signal sent to the process ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## ARGUMENTS _service_ (optional) Filters the processes according to the specified service or services. By default, all dovecot processes are listed. ## EXAMPLES sh ``` doveadm process status ``` ``` name pid available_count total_count idle_start last_status_update last_kill_sent stats 132400 999 5 0 1685365436 0 log 132356 971 29 0 1685352909 0 config 132357 999 6 0 1685365436 0 anvil 132355 1000 0 1685352908 1685352908 0 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-service-status(1)](/main/core/man/doveadm-service-status.1.html)` # doveadm-proxy(1) - Handle Dovecot proxy connections (obsolete) ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **proxy** _kick_|_list_ \[_OPTIONS_\] \[_ARGUMENTS_\] ## DESCRIPTION These commands are aliases to the `[doveadm-kick(1)](/main/core/man/doveadm-kick.1.html)` and `[doveadm-who(1)](/main/core/man/doveadm-who.1.html)` commands. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-purge(1) - Remove messages with refcount=0 from mdbox files ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **purge** \[**\-S** _socket\_path_\] **\-A** **doveadm** \[_GLOBAL OPTIONS_\] **purge** \[**\-S** _socket\_path_\] **\-F** _file_ **doveadm** \[_GLOBAL OPTIONS_\] **purge** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** **doveadm** \[_GLOBAL OPTIONS_\] **purge** \[**\-S** _socket\_path_\] **\-u** _user_ ## DESCRIPTION The **doveadm purge** command is used to remove all messages with refcount=0 from a user's mail storage. The refcount of a message is decreased to 0 when the user (or some administration utility) has expunged all instances of a message from all mailboxes. In the first form, the command will be executed for all users. In the second form, the command will be executed for all users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the last form, only messages of the given _user_ (s) will be purged. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` Additional resources: - [dbox](/main/core/config/mailbox_formats/dbox.html) # doveadm-pw(1) - Dovecot's password hash generator and validator ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **pw -l** **doveadm** \[_GLOBAL OPTIONS_\] **pw** \[**\-p** _password_\] \[**\-r** _rounds_\] \[**\-s** _scheme_\] \[**\-u** _user_\] \[**\-V**\] **doveadm** \[_GLOBAL OPTIONS_\] **pw** **\-t** _hash_ \[**\-p** _password_\] \[**\-u** _user_\] ## DESCRIPTION **doveadm pw** is used to generate password hashes for different password _scheme_ s and optionally verify the generated hash. All generated password hashes have a {_scheme_} prefix, for example {**SHA512-CRYPT.HEX**}. All passdbs have a default scheme for passwords stored without the {_scheme_} prefix. The default scheme can be overridden by storing the password with the scheme prefix. If you want to use this feature to verify or generate passwords without configuring Dovecot first, you can use `doveadm -O pw` to do so. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-l** List all supported password _scheme_ s and exit successfully. There are up to three optional password _scheme_s: **BLF-CRYPT** (Blowfish crypt), **SHA256-CRYPT** and **SHA512-CRYPT**. Their availability depends on the system's currently used libc. **\-p** _password_ The plain text _password_ for which the hash should be generated. If no _password_ was given `[doveadm(1)](/main/core/man/doveadm.1.html)` will prompt interactively for one. (Beware that using this option means the plain text password will be in your shell history!) **\-r** _rounds_ The password _scheme_ s **BLF-CRYPT**, **SHA256-CRYPT** and **SHA512-CRYPT** supports a variable number of encryption _rounds_. The following table shows the minimum/maximum number of encryption _rounds_ per scheme. When the **\-r** option was omitted the default number of encryption rounds will be applied. | Scheme | Minimum | Maximum | Default | | --- | --- | --- | --- | | BLF-CRYPT | 4 | 31 | 5 | | SHA256-CRYPT | 1000 | 999999999 | 5000 | | SHA512-CRYPT | 1000 | 999999999 | 5000 | **\-s** _scheme_ The password _scheme_ which should be used to generate the hashed password. By default the **CRYPT** _scheme_ will be used (with the 2y bcrypt format). It is also possible to append an encoding suffix to the _scheme_. Supported encoding suffixes are: **.b64**, **.base64** and **.hex**. See also [Password Schemes](/main/core/config/auth/schemes.html) for more details about password schemes. **\-t** _hash_ Test if the given password _hash_ matches a given plain text password. You should enclose the password _hash_ in single quotes, if it contains one or more dollar signs (**$**). The plain text password may be passed using the **\-p** option. When no password was specified, `[doveadm(1)](/main/core/man/doveadm.1.html)` will prompt interactively for one. **\-u** _user_ When the **DIGEST-MD5** _scheme_ is used, the _user_ name must also be given, because the user name is a part of the generated hash. For more information about Digest-MD5 please read also [DIGEST-MD5](/main/core/config/auth/mechanisms/digest_md5.html). For other schemes, this is not required. **\-V** When this option is given, the hashed password will be internally verified. The result of the verification will be shown after the hashed password, enclosed in parenthesis. ## EXAMPLE An ARGON2ID hash (best security at time of this writing, though can be heavy on a busy server): sh ``` # doveadm pw -s ARGON2ID Enter new password: Retype new password: {ARGON2ID}$argon2id$v=19$m=65536,t=3,p=1$AOrrkaFmGxCFtX+NCSHFkg$N3rlzYFqyNkCwrOingnDJ/qDQ09yGHgQa8PQfbu7rIE ``` Alternatively, a SHA512-CRYPT hash: sh ``` # doveadm pw -s SHA512-CRYPT Enter new password: Retype new password: {SHA512-CRYPT}$6$qAvxfQ2UbA1QTXSg$SB2aMEK76DBObt.KqTjF5.yDMceaD3dkG2UvrKQD0rZ9PKii/VAn.VS0nBsDqJX18kXieMi8AWJr0f7Ae9dAp/ ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-quota(1) - Initialize/recalculate or show current quota usage ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **quota** \[**\-S** _socket\_path_\] _command_ **\-A** **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **quota** \[**\-S** _socket\_path_\] _command_ **\-F** _file_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **quota** \[**\-S** _socket\_path_\] _command_ **\--no-userdb-lookup** **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **quota** \[**\-S** _socket\_path_\] _command_ **\-u** _user_ ## DESCRIPTION In the first form, the command will be performed for all users. In the second form, the command will be performed for all users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the last form, the command will affect only the matching _user_(s). - The **quota get** and **quota recalc** commands are only available when the global _mail\_plugins_ setting contains the **quota** plugin. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## COMMANDS ### quota get **doveadm** \[_GLOBAL OPTIONS_\] **quota get** \[**\-A** | **\-u** _user_ | **\-F** _file_\] The **quota get** command is used to display the current quota usage. The storage values are reported in kilobytes. This command uses by default the output formatter **table**. ### quota recalc **doveadm** \[_GLOBAL OPTIONS_\] quota recalc \[**\-A** | **\-u** _user_ | **\-F** _file_\] The **quota recalc** command is used to recalculate the current quota usage. ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file, including the _dict_ section. _/etc/dovecot/conf.d/10-mail.conf_ Mailbox locations and namespaces. _/etc/dovecot/conf.d/90-quota.conf_ Quota configuration. ## EXAMPLE Get the current quota usage of user jane: sh ``` doveadm quota get -u jane ``` ``` Quota name Type Value Limit % user STORAGE 90099 102400 87 user MESSAGE 20548 30000 68 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-rebuild(1) - Commands related to rebuilding various aspects of mails matching given search query. ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **rebuild** \[**\-S** _socket\_path_\] **\-A** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **rebuild** \[**\-S** _socket\_path_\] **\-F** _file_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **rebuild** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **rebuild** \[**\-S** _socket\_path_\] **\-u** _user_ _search\_query_ ## DESCRIPTION The **rebuild attachments** command is used to rebuilds attachment presence. `[doveadm(1)](/main/core/man/doveadm.1.html)` will print the message's uid for each match. When used with the **\-A** or **\-u** _wildcard_ options, `[doveadm(1)](/main/core/man/doveadm.1.html)` will print the fields **username** and **uid** for each matching message. In the first form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will execute the **rebuild** action for all users. In the second form, the command will be performed for all users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the last form, only matching mails of the given _user_ (s) will be rebuilt ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. This command uses by default the output formatter **flow** (without the _key_ = prefix). ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _search\_query_ Resets attachment indicator for messages matching this search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## EXAMPLE Rebuild user bob's attachment status: sh ``` doveadm rebuild attachments -u bob ALL ``` ``` 1 2 3 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-save(1) - Save email to a user's mailbox ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **save** \[**\-S** _socket\_path_\] **\-A** \[_\-m_ _mailbox_\] \[_\-U_ _uid_\] \[_\-g_ _guid_\] \[_\-r_ _received-date_\] \[_mail-file_\] **doveadm** \[_GLOBAL OPTIONS_\] **save** \[**\-S** _socket\_path_\] **\-F** _file_ \[_\-m_ _mailbox_\] \[_\-U_ _uid_\] \[_\-g_ _guid_\] \[_\-r_ _received-date_\] \[_mail-file_\] **doveadm** \[_GLOBAL OPTIONS_\] **save** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** \[_\-m_ _mailbox_\] \[_\-U_ _uid_\] \[_\-g_ _guid_\] \[_\-r_ _received-date_\] \[_mail-file_\] **doveadm** \[_GLOBAL OPTIONS_\] **save** \[**\-S** _socket\_path_\] **\-u** _user_ \[_\-m_ _mailbox_\] \[_\-U_ _uid_\] \[_\-g_ _guid_\] \[_\-r_ _received-date_\] \[_mail-file_\] ## DESCRIPTION **doveadm save** can be used to save messages. This can be useful for scripts and for debugging. Sieve is not invoked for saved messages, but quota is enforced. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-m** _mailbox_ Store mail to specified mailbox instead of INBOX. **\-U** _uid_ Save the mail using the given UID, if possible. **\-g** _guid_ Save the mail using the given GUID. **\-r** _received-date_ Save the mail using the given received-date timestamp. This is in the "human timestamp" format as described by `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)`. ## ARGUMENTS _mail-file_ The message data to save. - If _mail-file_ is `-`, the message is read from stdin (default). - Otherwise, _mail-file_ resolves as a file path. ## EXAMPLE sh ``` echo "hello, world" | doveadm save -u testuser@testdomain ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-search-query(7) - Overview of search queries for doveadm mailbox commands ## DESCRIPTION Several `[doveadm(1)](/main/core/man/doveadm.1.html)` commands use a _search\_query_ in order to act only on matching messages. This manual page describes all _SEARCH KEYS_, which can be used in a _search\_query_. The query is mostly compatible with the IMAP SEARCH command parameters, although there are some differences. Each _search\_query_ consists at least of one _SEARCH KEY_. Most of the _SEARCH KEYS_ require an argument. All _SEARCH KEYS_ are case-insensitive. The shortest valid _search\_query_ is **ALL**. For example: sh ``` doveadm search ALL ``` Multiple search query expressions will be combined with the **AND** operator by default. To find all messages that are new and greater than 50 kilobyte, one can use: sh ``` doveadm search NEW LARGER 50k ``` **OR**\-ed _SEARCH KEYS_ have to be written in parenthesis, when mixing ANDs and ORs. Shells commonly require escaping for parentheses. To find messages that were saved on the "13th of April 2007" AND have the \\Seen and/or \\Flagged flag set, one can use for example: sh ``` doveadm search SAVEDON 2007-04-13 \( SEEN OR FLAGGED \) ``` It's also possible to specify the mailbox, in which should be searched. Use either the keyword **mailbox** and the name of the mailbox or the keyword **mailbox-guid** and the mailbox's globally unique identifier in the _search\_query_. To find all messages in the mailbox with the GUID "44f68b13ce97044b837f000035ca9452" use: sh ``` doveadm search mailbox-guid 44f68b13ce97044b837f000035ca9452 ALL ``` To list all deleted messages in the "Trash" folder use: sh ``` doveadm search mailbox Trash DELETED ``` ## SEARCH KEYS The following search keys from the specification of IMAP version 4 revision 1 (see: [RFC 3501 (section 6.4.4)](https://datatracker.ietf.org/doc/html/rfc3501#section-6.4.4)) are supported: _sequence-set_ Matches messages with the given sequence numbers. The _sequence-set_ may be a single sequence number. Can be a sequence range, written as _from_:_to_, e.g. **100**:**125**. As comma separated list of sequences, e.g. **11,50,4**. It's also possible to combine multiple sequences, e.g. **1,3,5,7,10:20**. Using \* selects the last mail in the mailbox. For example 1:100 matches the first 100 mails and 101:200 the next second hundred mails. 1,5,\* matches the first, the fifth and the last email. Use `UID` (see below) to search by unique ID. **ALL** Matches all messages. **ANSWERED** Matches messages with the IMAP flag \\Answered set. **BCC** _string_ Matches messages, which contain _string_ in the BCC field of the message's IMAP envelope structure. **BEFORE** _date specification_ Matches messages with an internal date before _date specification_. **BODY** _string_ Matches messages, which contain _string_ in the body part. **CC** _string_ Matches messages, which contain _string_ in the CC field of the message's IMAP envelope structure. **DELETED** Matches messages with the IMAP flag \\Deleted set. **DRAFT** Matches messages with the IMAP flag \\Draft set. **FLAGGED** Matches messages with the IMAP flag \\Flagged set. **FROM** _string_ Matches messages, which contain _string_ in the FROM field of the message's IMAP envelope structure. **HEADER** _field_ _string_ Matches messages, which either have the named header _field_, when empty _string_ was given. Or messages, where the given header _field_'s value contains the specified _string_. **KEYWORD** _keyword_ Matches messages with the given IMAP _keyword_ (e.g. $Forwarded) flag set. **LARGER** _size_ Matches messages that are larger than the specified _size_. **MAILBOX** _name_ Matches messages in the mailbox with the specified _name_. **MAILBOX-GUID** _guid_ Matches messages in the mailbox with the specified _guid_. **NEW** Matches messages, which have the IMAP flag \\Recent set **but not** the IMAP flag \\Seen. **NOT** _search key_ Inverse matching - matches massages, where the search doesn't match the specified _search key_ or its value. **OLD** Matches messages, which do not have the IMAP flag \\Recent set. **ON** _date specification_ Matches messages whose internal date matches the given _date specification_. _search key_ **OR** _search key_ Matches messages where one of the OR-ed search keys matches. Note: IMAP4rev1 uses the syntax: **OR** _search key search key_ **RECENT** Matches messages with the IMAP flag \\Recent set. **SEEN** Matches messages with the IMAP flag \\Seen set. **SENTBEFORE** _date specification_ Matches messages with a Date: header before _date specification_. **SENTON** _date specification_ Matches messages with a Date: header matching the given _date specification_. **SENTSINCE** _date specification_ Matches messages with a Date: header matching or after the given _date specification_. **SINCE** _date specification_ Matches messages whose internal date is within or after the given _date specification_. **SMALLER** _size_ Matches messages with a size smaller than the given _size_. **SUBJECT** _string_ Matches messages, which contain _string_ in the SUBJECT field of the message's IMAP envelope structure. **TEXT** _string_ Matches messages, which contain _string_ in the message headers or body. **TO** _string_ Matches messages, which contain _string_ in the TO field of the message's IMAP envelope structure. **UID** _sequence-set_ Matches messages with the given IMAP UID(s). See the **sequence-set** description for details on it. For example **1:10,100:200,**\* matches the UIDs from 1 to 10, 100 to 200 and also the last mail. **UNANSWERED** Matches messages, which do not have the IMAP flag \\Answered set. **UNDELETED** Matches messages, which do not have the IMAP flag \\Deleted set. **UNDRAFT** Matches messages, which do not have the IMAP flag \\Draft set. **UNFLAGGED** Matches messages, which do not have the IMAP flag \\Flagged set. **UNKEYWORD** _keyword_ Matches messages, which do not have the given IMAP _keyword_ flag set **UNSEEN** Matches messages, which do not have the IMAP flag \\Seen set. ## DOVEADM SEARCH KEYS Additional search keys, provided by `[doveadm(1)](/main/core/man/doveadm.1.html)`. **SAVEDBEFORE** _date specification_ Matches messages, which were saved before _date specification_. **SAVEDON** _date specification_ Matches messages whose save date matches the given _date specification_. **SAVEDSINCE** _date specification_ Matches messages with a save date matching or after the given _date specification_. ## DATE SPECIFICATION `[doveadm(1)](/main/core/man/doveadm.1.html)` supports a few additional _date specification_ formats. They can be used anywhere, where a _date specification_ value is obligatory. _dd-mon-yyyy_ Default IMAP4rev1 date format. Date components: _day_ the day of month: **1**\-**31**. _month_ the abbreviated month name: **Jan**, **Feb**, **Mar**, **Apr**, **May**, **Jun**, **Jul**, **Aug**, **Sep**, **Oct**, **Nov**, or **Dec**. _year_ four digits of year, e.g. **2007**. For example the "13th of April 2007" will be represented as **13-Apr-2007**. _dd-mon-yyyy hh:mm:ss \[+-\]zzzz_ Default IMAP4rev1 date-time format. See _dd-mon-yyyy_ above for the date format. Time format components: _hh_ the hour _mm_ the minute _ss_ the second _\[+-\]zzzz_ the timezone as hhmm, e.g. **+0530**, **\-0700** _interval_ Combination of a positive integer _number_ and a _time unit_. Available _time units_ are: **weeks** abbreviated: **w** **days** abbreviated: **d** **hours** abbreviated: **h** **mins** abbreviated: **m** **secs** abbreviated: **s** To match messages from last week, you may specify for example: **since 1w**, **since 1weeks** or **since 7days**. _Unix timestamp_ A 10 digit Unix timestamp, seconds since the 1st of January 1970, 00:00:00 UTC. For example the "13th of April 2007" will be represented as **1176418800**. _YYYY-MM-DD_ Extended ISO-8601 calendar date format. For example the "13th of April 2007" will be represented as **2007-04-13**. ## SIZE `[doveadm(1)](/main/core/man/doveadm.1.html)` provides also an additional _size_ representation format. The following formats can be used anywhere, where a _size_ value is obligatory. _octets_ The message size in octets, as specified in the IMAP4rev1 specification. _size_ The message size in **B** (byte), **k** (kilobyte), **M** (megabyte), **G** (gigabyte) or **T** (terabyte). To match messages, bigger than 1 megabyte, you may specify for example: **larger 1M** or **larger 1024k**. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-search(1) - Show a list of mailbox GUIDs and message UIDs matching given search query. ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **search** \[**\-S** _socket\_path_\] **\-A** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **search** \[**\-S** _socket\_path_\] **\-F** _file_ _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **search** \[**\-S** _socket\_path_\] **\--no-userdb-lookup** _search\_query_ **doveadm** \[_GLOBAL OPTIONS_\] \[**\-f** _formatter_\] **search** \[**\-S** _socket\_path_\] **\-u** _user_ _search\_query_ ## DESCRIPTION The **search** command is used to find matching messages. `[doveadm(1)](/main/core/man/doveadm.1.html)` will print the mailbox's guid and the message's uid for each match. When used with the **\-A** or **\-u** _wildcard_ options, `[doveadm(1)](/main/core/man/doveadm.1.html)` will print the fields **username**, **mailbox-guid** and **uid** for each matching message. In the first form, `[doveadm(1)](/main/core/man/doveadm.1.html)` will executed the **search** action will be performed for all users. In the second form, the command will be performed for all users listed in the given _file_. In the third form, the command will be performed for the user contained in the _USER_ environment variable. In the last form, only matching mails of the given _user_(s) will be searched. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. This command uses by default the output formatter **flow** (without the _key_\=prefix). ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _search\_query_ Show messages matching this search query. See `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` for details. ## EXAMPLE Search in user bob's dovecot mailboxes all messages which contains the word "todo" in the Subject: header: sh ``` doveadm search -u bob mailbox dovecot\* subject todo ``` ``` 3a94c928d66ebe4bda04000015811c6a 8 3a94c928d66ebe4bda04000015811c6a 25 3a94c928d66ebe4bda04000015811c6a 45 ``` The search command is mainly useful when used together with `[doveadm-fetch(1)](/main/core/man/doveadm-fetch.1.html)` command. For example to save message bodies of all messages from INBOX that have "todo" in subject, use: sh ``` doveadm search -u bob mailbox INBOX subject todo while read guid uid; do doveadm fetch -u bob body mailbox-guid $guid uid $uid > msg.$uid done ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-search-query(7)](/main/core/man/doveadm-search-query.7.html)` # doveadm-service-status(1) - Show information about dovecot services ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **service status** \[_service_ \[...\]\] ## DESCRIPTION **doveadm service status** produces a table with a line for each service, containing the following details: _name_ the name of the service _process\_count_ the number of processes actually running for the service _process\_avail_ the number of additional processes that can be spawned for the service _process\_limit_ the maximum number of processes that can be active for the service _client\_limit_ the maximum number of connections that the service will handle simultaneously _throttle\_secs_ seconds to wait before launching another process when processes are dying unexpectedly early for the service _exit\_failure\_last_ timestamp when the last process was terminated abnormally for the service _exit\_failures\_in\_sec_ number of abnormally terminated processes for the service in the last second _last\_drop\_warning_ timestamp when the last time a "dropping client connections" warning was logged _listen\_pending_ if the master process is queuing connections that need to be dispatched _listening_ if service is listening _doveadm\_stop_ if the service has been stopped by `[doveadm(1)](/main/core/man/doveadm.1.html#stop)` _process\_total_ the total number of processes forked for the service since the service start. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## ARGUMENTS _service_ (optional) Filters the list according to the specified service or services. By default, all dovecot services are listed. ## EXAMPLES sh ``` doveadm service status ``` ``` name: imap-login process_count: 0 process_avail: 0 process_limit: 1 client_limit: 1000 throttle_secs: 0 exit_failure_last: 0 exit_failures_in_sec: 0 last_drop_warning: 0 listen_pending: n listening: y doveadm_stop: n process_total: 0 ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-process-status(1)](/main/core/man/doveadm-process-status.1.html)` # doveadm-service-stop(1) - Stop Dovecot Services ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **service stop** _service_ \[_service_ \[...\]\] ## DESCRIPTION **doveadm service stop** stops the listed Dovecot service processes. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## ARGUMENTS _service_ The list of services to stop. ## EXAMPLES console ``` $ doveadm service stop stats ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-sieve(1) - Commands related to handling Sieve scripts ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] _sieve\_cmd_ \[_options_\] \[_arguments_\] ## DESCRIPTION The **doveadm sieve** commands are part of Pigeonhole (`[pigeonhole(7)](/main/core/man/pigeonhole.7.html)`), which adds Sieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) support to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). The **doveadm sieve** commands can be used to manage Sieve filtering. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). ## ARGUMENTS _scriptname_ Is the name of a _Sieve script_, as visible to ManageSieve clients. TIP For Sieve scripts that are stored on disk, this is the filename without the ".sieve" extension. ## COMMANDS ### sieve put **doveadm** \[_GLOBAL OPTIONS_\] sieve put \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-a**\] _scriptname_ This command puts one new Sieve script in the script storage. The script is read from standard input. If the script compiles successfully, it is stored under the provided _scriptname ._ If the **\-a** option is present, the Sieve script is subsequently marked as the active script for execution at delivery. ### sieve get **doveadm** \[_GLOBAL OPTIONS_\] sieve get \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _scriptname_ This command retrieves the Sieve script named _scriptname_. ### sieve delete **doveadm** \[_GLOBAL OPTIONS_\] sieve delete \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-a**\] _scriptname_ ... This command deletes one or more Sieve scripts. The deleted script may not be the active script, unless the **\-a** option is present. ### sieve list **doveadm** \[_GLOBAL OPTIONS_\] sieve list \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] List existing Sieve scripts, and their active state. ### sieve rename **doveadm** \[_GLOBAL OPTIONS_\] sieve rename \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _old\_name_ _new\_name_ The **sieve rename** command is used to rename the Sieve script _old\_name_ to _new\_name_. ### sieve activate **doveadm** \[_GLOBAL OPTIONS_\] sieve activate \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _scriptname_ This command marks the Sieve script named _scriptname_ as the active script for execution at delivery. ### sieve deactivate **doveadm** \[_GLOBAL OPTIONS_\] sieve deactivate \[**\-A** | **\-u** _user_ | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] _scriptname_ This command deactivates Sieve processing. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[pigeonhole(7)](/main/core/man/pigeonhole.7.html)` # doveadm-stats(1) - Inspect or reset stats ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **stats add** \[ **\--description** __ \] \[ **\--exporter** __ \] \[ **\--exporter-include** __ \] \[ **\--fields** __ \] \[**\--group\_by** __ \] _name_ __ **doveadm** \[_GLOBAL OPTIONS_\] **stats dump** \[ **\-s** __ \] \[ **\-r** \] \[ **\-f** __ \] **doveadm** \[_GLOBAL OPTIONS_\] **stats top** \[**\-s **\] \[**\-b**\] \[**sort** __\] **doveadm** \[_GLOBAL OPTIONS_\] **stats remove** \[ __ \] **doveadm** \[_GLOBAL OPTIONS_\] **stats reopen** ## DESCRIPTION Commands to inspect and edit Dovecot stats/metrics generation. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## COMMANDS ### stats add **doveadm** \[_GLOBAL OPTIONS_\] **stats add** \[ **\--description** __ \] \[ **\--exporter** __ \] \[ **\--exporter-include** __ \] \[ **\--fields** __ \] \[**\--group\_by** __ \] _name_ __ **doveadm stats add** is used to add metrics to statistics. #### OPTIONS **\--description** __ Human-readable description of the metric. This is included in the HELP text sent to OpenMetrics. **\--exporter** __ Export events matching the filter with this event exporter. If empty, the events are used only for statistics, and no exporting is done. **\--exporter-include** __ Specifies which parts of the event are exported to the serialized event. The fields are space-separated. **\--fields** __ A list of fields included in the metric. All events have a default `duration` field that does not need to be listed explicitly. **\--group-by** __ Creates a new group\_by for dynamically generating sub-metrics based on the specified field's values. #### ARGUMENTS _name_ Metric name. _filter_ Filter options: - **user=** Match user. - **domain=** Match DNS domain name. - **session=** Match session identifier. - **ip=\[/\]** Match local or remote IP. - **since=** Match session start time. - **connected** Show only connected sessions. ### stats dump **doveadm** \[_GLOBAL OPTIONS_\] **stats dump** \[ **\-s** __ \] \[ **\-r** \] \[ **\-f** __ \] **doveadm stats dump** is used to output statistics. #### OPTIONS **\-f** TODO **\-r** Resets statistics after dumping. **\-s** _socketpath_ Stats socket path. ### stats remove **doveadm** \[_GLOBAL OPTIONS_\] **stats remove** \[ __ \] **doveadm stats remove** is used to remove metrics from statistics. #### ARGUMENTS _name_ The metric name to remove. ### stats reopen **doveadm** \[_GLOBAL OPTIONS_\] **stats reopen** **doveadm stats reopen** is used to reopen any file exporter files. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-sync(1) - Dovecot's two-way mailbox synchronization feature ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **sync** \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-1fPR**\] \[**\-l** _secs_\] \[**\-r** _rawlog path_\] \[**\-m** _mailbox_\] \[**\-g** _mailbox guid_\] \[**\-n** _namespace_ | **\-N**\] \[**\-x** _exclude_\] \[**\-a** _all mailbox_\] \[**\-s** _state_\] \[**\-T** _secs_\] \[**\-t** _start date_\] \[**\-e** _end date_\] \[**\-O** _sync flag_\] \[**\-I** _max size_\] \[**\-p** _setting=value_\] _destination_ **doveadm** \[_GLOBAL OPTIONS_\] **backup** \[**\-u** _user_ | **\-A** | **\-F** _file_ | **\--no-userdb-lookup**\] \[**\-S** _socket\_path_\] \[**\-fPR**\] \[**\-l** _secs_\] \[**\-r** _rawlog path_\] \[**\-m** _mailbox_\] \[**\-g** _mailbox guid_\] \[**\-n** _namespace_ | **\-N**\] \[**\-x** _exclude_\] \[**\-a** _all mailbox_\] \[**\-s** _state_\] \[**\-T** _secs_\] \[**\-t** _start date_\] \[**\-e** _end date_\] \[**\-O** _sync flag_\] \[**\-I** _max size_\] \[**\-p** _setting=value_\] _destination_ ## DESCRIPTION dsync (short for doveadm sync) is Dovecot's mailbox synchronization feature. It can be used for several different use cases: Two-way synchronization of mailboxes, creating backups of mails, and convert mailboxes from/to different mailbox formats. All of these can be used within the same server or between different servers (via ssh(1) or tcp connections). Remote mailboxes can be accessed also via IMAP protocol, which allows using dsync for mailbox migration purposes. You can run dsync in one of three modes: - **doveadm backup** performs one-way synchronization. If there are any changes in the destination they will be reverted, so the destination will look exactly like the source. - **doveadm sync** performs two-way synchronization. It merges all changes without losing anything. Both the mailboxes will end up looking identical after the synchronization is finished. - **doveadm sync -1** performs one-way synchronization. If there are any changes in the destination, they will be preserved and the new changes will be merged on top of them. This merging doesn't currently work perfectly, so its use should be limited. Its main purpose is that during mailbox migration you can run **doveadm backup** multiple times, then switch mails to be delivered to the new mailbox and run **doveadm sync -1** once more to transfer any last new mails from the old mailbox. - The one-way algorithm is the same as two-way dsync algorithm except the source account is not modified. It fetches the message's GUID (Global UID), which is used to identify any conflicting UIDs in messages. As long as the source and destination side has matching UID<->GUID mapping, those emails are assumed to be synced correctly. Only after the first mismatch will changes begin. Example: Source mailbox has messages UID 1..5; source mailbox is synchronized using **doveadm backup** to the destination. Subsequently, UID 6 is delivered to the source mailbox and UID 1 is expunged from the destination mailbox. In this example, UID 1 is kept removed (in destination) because UID 1..5 have identical Date+Message-ID headers. UID 6 is not seen in destination so it's copied. If both source and destination have UID 6, but the messages are different, the headers don't match and both the messages are kept in the destination but they're given new UIDs 7 and 8 just to be sure any client didn't get confused about what UID 6 actually was. Thus, one-way sync begins to quickly diverge from the source mailbox once changes start to occur on either side; one-way sync should therefore normally only be used within a short period of time after a **doveadm backup** or **doveadm sync** command was used to synchronize the mailboxes. There are also three different synchronization algorithms: - Full synchronization (-f parameter) scans through all the messages in all the mailboxes. This guarantees that everything will be synchronized, but it's unnecessarily slow for incremental synchronization. - Fast synchronization (default) first attempts to find mailboxes that have changed, and synchronize only those. This is done by checking the mailboxes' metadata (NEXTUID and HIGHESTMODSEQ). Usually this works fine, especially with one-way synchronization, but if both sides do exactly the same number of changes, the metadata may end up containing the same values even if the changes were different. - Stateful synchronization (-s parameter) is the most efficient way to synchronize mailboxes. It relies on having the earlier dsync run's state saved somewhere and being passed to the next dsync run. Based on this state dsync can send only the changes that happened after the previous dsync run. As long as the state or the mailboxes aren't corrupted this algorithm should work perfectly. The syncing is done as perfectly as possible: an IMAP or a POP3 client shouldn't be able to notice any differences between the two mailboxes. Two-way syncing means that it's safe to do any kind of modifications in both sides, and dsync will merge the changes without losing any changes done on either side. This is possible because dsync can access Dovecot's index logs that keep track of changes. It's of course possible to have conflicts during merging, these are resolved in a safe way. See the _dsync design_ document for more information. dsync uses the same configuration files as the rest of Dovecot (via `[doveconf(1)](/main/core/man/doveconf.1.html)`). The entire configuration can be changed by giving -c parameter to another configuration file, or using -o parameter to override specific settings. When executing a remote dsync program it works the same way: it uses its own local configuration. dsync can be run completely standalone. It doesn't require any Dovecot server processes to be running, except when using -u parameter to do a _userdb_ lookup from auth process. dsync can sync either one or multiple users using the -u or -A parameters. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-A** If the **\-A** option is present, the _command_ will be performed for all users. Using this option in combination with system users from **userdb { driver = passwd }** is not recommended, because it contains also users with a lower UID than the one configured with the `first_valid_uid` setting. When the SQL userdb module is used, make sure that the `[userdb_sql_iterate_query](/main/core/summaries/settings.html#userdb_sql_iterate_query)` setting matches your database layout. When using the LDAP userdb module, make sure that the `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` and `[userdb_ldap_iterate_fields](/main/core/summaries/settings.html#userdb_ldap_iterate_fields)` settings match your LDAP schema. Otherwise `[doveadm(1)](/main/core/man/doveadm.1.html)` will be unable to iterate over all users. **\-F** _file_ Execute the _command_ for all the users in the _file_. This is similar to the **\-A** option, but instead of getting the list of users from the userdb, they are read from the given _file_. The _file_ contains one username per line. **\-1** Do one-way synchronization instead of two-way synchronization. **\-f** Do full synchronization. **\-N** Synchronize all the available namespaces. By default only the inbox=yes namespace is synchronized. **\--no-userdb-lookup** Do not perform userdb lookup. Use the `USER` environment variable to specify the username. **\-P** Run a `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html)` for the destination (remote) storage after synchronization. **\-R** Do a reverse sync. Normally, messages would be pushed from the local system to the destination (remote). This option reverses the flow, and will instead pull messages from the remote to the local storage. **\-S** _socket\_path_ The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. This allows an administrator to execute `[doveadm(1)](/main/core/man/doveadm.1.html)` mail commands through the given socket. **\-T** _secs_ Specify the time in seconds, how long `[doveadm(1)](/main/core/man/doveadm.1.html)` should wait for stalled I/O operations. The default timeout is 600 seconds. **\-p** _setting_**\=**_value_ Overrides the configuration _setting_ for source storage. **\-g** _mailbox\_guid_ Same as -m, but find the mailbox to be synchronized by its GUID instead of by name. **\-l** _secs_ Lock the dsync for this user. Wait for maximum _secs_ before giving up. This parameter should be used to avoid broken synchronization if it's possible that dsync is being run concurrently for the same user. **\-m** _mailbox_ Synchronize only this mailbox name. **\-n** _namespace_ Synchronize only the specified namespace. This parameter can be used multiple times. **\-a** _all mailbox_ Name for the "All mails" virtual mailbox. If specified, mails are attempted to be copied from this mailbox instead of being saved separately. This may reduce the total disk space usage as well as disk IO. **\-t** _start date_ Skip any mails whose received-timestamp is older than the specified time. **\-e** _end date_ Skip any mails whose received-timestamp is newer than the specified time. **\-O** _sync flag_ Sync only mails that have the specified flag. If the flag name begins with "**\-**", sync all mails except the ones with the specified flag. **\-I** _max size_ Skip any mails larger than the specified size. **\-r** _rawlog\_path_ Running dsync remotely, write the remote input/output traffic to the specified log file. **\-s** _previous\_state_ Use stateful synchronization. This allows for optimized incremental synchronization by providing a "sync state string" from the previous dsync run. dsync will use this state to only synchronize the changes that have occurred since that state was captured, avoiding a full mailbox scan. If the previous state is unknown, use an empty string. The new state is always printed to standard output if this parameter is specified. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-x** _mailbox\_mask_ Exclude the specified mailbox name/mask. The mask may contain "**?**" and "\*\*\*\*\*" wildcards. The mask can also be a special-use name (e.g. \\Trash). This parameter can be used multiple times. ## ARGUMENTS _destination_ This argument specifies the synchronized destination. It can be one of: _mail\_driver:mail\_path_ Uses the storage specified by _mail\_driver_ and _mail\_path_. Use the **\-p** parameter to specify additional settings. **remote:** _login@host_ Uses _dsync\_remote\_cmd_ setting to connect to the remote host (usually via ssh) _remoteprefix:login@host_ This is the same as remote, except "user@domain\\n" is sent before dsync protocol starts. This allows implementing a trusted wrapper script that runs doveadm dsync-server by reading the username from the first line. **tcp:** _host\[:port\]_ Connects to remote doveadm server via TCP. The default port is specified by _doveadm\_port_ setting. **tcps:** _host\[:port\]_ This is the same as tcp, but with SSL. **command \[arg1 \[, arg2, ...\]\]** Runs a local command that connects its standard input & output to a dsync server. ## EXIT STATUS **dsync** will exit with one of the following values: **0** Synchronization was done perfectly. **2** Synchronization was done without errors, but some changes couldn't be done, so the mailboxes aren't perfectly synchronized. Running dsync again usually fixes this. Typically this occurs for message modification sequences with newly created mailboxes. It can also occur if one of the mailboxes change during the syncing. **1, >2** Synchronization failed. See also `[doveadm(1)](/main/core/man/doveadm.1.html)` for other possible error codes. ## EXAMPLE ### SYNCHRONIZATION Synchronize mailboxes with a remote server. Any errors are written to stderr: sh ``` doveadm sync -u username@example.com remote:server-replica.example.com ``` If you need more complex parameters to ssh, you can use e.g.: sh ``` doveadm sync -u username@example.com ssh -i id_dsa.dovecot \ mailuser@example.com doveadm dsync-server -u username@example.com ``` ### CONVERTING Example to convert mails from Maildir format to the format specified in the configuration file: sh ``` doveadm backup -u user maildir:~/Maildir ``` If you want to do this without any downtime, you can do the conversion one user at a time. We consider this scenario: - Configuration uses **mail\_driver = maildir** and **mail\_path = ~/Maildir** - We want to convert to the mdbox format: **mail\_driver = mdbox** and **mail\_path = ~/mdbox** Initially: - Set up the possibility of doing per-user mail location using _userdb_ extra fields. Then for each user: 1. Run _doveadm sync mdbox:~/mdbox_ once to do the initial conversion. 2. Run _doveadm sync mdbox:~/mdbox_ again, because the initial conversion could have taken a while and new changes could have occurred during it. This second time only applies changes, so it should be fast. 3. Update userdb to return the wanted new mail format configuration. In this scenario: **mail\_driver = mdbox** and **mail\_path = ~/mdbox**. If you're using auth cache, you need to flush it, e.g. **doveadm auth cache flush**. 4. Wait for a few seconds and then kill (doveadm kick) the user's all existing imap and pop3 sessions (that are still using maildir). 5. Run _doveadm sync maildir:~/Maildir_ with the old location to apply final changes that were possibly done. After this there should be no changes to Maildir, because the user's mail location has been changed and all existing processes using it have been killed. Once all users have been converted, you can update the global _mail\_driver_ and _mail\_path_ settings and remove the per-user mail locations from _userdb_. ## SECURITY This command cannot be used safely by untrusted users, unless the input is heavily sanitized. The destination allows running arbitrary commands. If using _dsync\_remote\_cmd_, the username and hostname may contain spaces or begin with a dash, which can allow passing unsafe parameters to `ssh`. Also since `ssh` runs commands via shell, parameters containing e.g. `;` or `&&` can result in running multiple commands. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html)` # doveadm-user(1) - Perform a user lookup in Dovecot's userdbs ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **user** \[**\-a** _userdb\_socket\_path_\] \[**\-e** _template_\] \[**\-f** _field_\] \[**\-u**\] \[**\-x** _auth\_info_\] _user_... ## DESCRIPTION The **user** command is used to perform a user lookup - to show what information Dovecot sees about the _user_ (s), or if it exists at all in the configured _userdb_ (s). The _auth\_info_ may be useful when the userdb is for example a SQL database and you are using %variables, like **%{protocol}** or **%{local\_ip}**, in the _userdb\_sql\_query_ setting. Or when you have configured the userdb in a way like this: doveconf ``` userdb passwd-file { passwd_file_path = /etc/%{protocol}.passwd } ``` ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. ## OPTIONS **\-a** _userdb\_socket\_path_ This option is used to specify an absolute path to an alternative UNIX domain socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/auth-userdb_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-e** _template_ When this option and a string with %variables is given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will show the expanded string for the user. This option can't be used together with the `-f` or `-u` options. **\-f** _field_ When this option and the name of a userdb field is given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will show only the value of the specified field. This option can't be used together with the `-e` or `-u` options. **\-u** When this option is given, `[doveadm(1)](/main/core/man/doveadm.1.html)` will only show values from the _userdb_. Without `-u` parameter if any of the _uid_, _gid_, _home_ or _mail\_path_ fields are missing, their defaults are taken from configuration file. This option can't be used together with the `-e` or `-f` options. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _user_ Is a _user_'s login name. Depending on the configuration, a login name may be for example **jane** or **[john@example.com](mailto:john@example.com)**. It's also possible to use '**\***' and '**?**' wildcards (e.g. `-u \*@example.org`). ## EXAMPLE Perform a user lookup for the users jane and [john@example.com](mailto:john@example.com): sh ``` doveadm user jane john@example.com ``` ``` userdb: jane uid : 8001 gid : 8001 home : /home/jane mail_path : ~/sdbox plugins : sieve quota_storage_size: 150M userdb: john@example.com home : /srv/mail/8/70312/79832 uid : 79832 gid : 70312 mail_path : ~/mdbox ``` User lookup, using wildcards: sh ``` doveadm user \*.?oe@example.net ``` ``` jane.doe@example.net judy.roe@example.net john.doe@example.net ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm-who(1) - Show who is logged into the Dovecot server ## SYNOPSIS **doveadm** \[_GLOBAL OPTIONS_\] **who** \[**\-1**\] \[**\-f** _passdb\_field_\] \[**\-a** _anvil\_socket\_path_\] \[_user\_mask_\] \[_ip_ \[**/** _bits_\]\] ## DESCRIPTION The **who** command is used to show which users from which hosts are currently connected to which service. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. This command uses by default the output formatter **table**. ## OPTIONS **\-1** Print one line per user and connection. Otherwise the connections are grouped by the username. **\-a** _anvil\_socket\_path_ This option is used to specify an alternative socket. The option's argument is either an absolute path to a local UNIX domain socket, or a hostname and port (_hostname_:_port_), in order to connect a remote host via a TCP socket. By default `[doveadm(1)](/main/core/man/doveadm.1.html)` will use the socket _/rundir/anvil_. The socket may be located in another directory, when the default _base\_dir_ setting was overridden in _/etc/dovecot/dovecot.conf_. **\-f** _passdb\_field_ Alternative username field to use for querying, as returned by passdb. Only the passdb fields beginning with the _user\__ prefix are tracked. ## ARGUMENTS _ip_ \[**/** _bits_\] Specify an _ip_ address or network range, in CIDR notation, to reduce the result to matching connections. _user\_mask_ List only users whose login name matches the _user\_mask_, or the alternative username (user\_\* field) if the **\-f** parameter is used. It's also possible to use wildcards in the _user_ name. ## EXAMPLE Show authenticated sessions, filtered by the client's IP address: sh ``` doveadm who ::1 ``` ``` username # proto (pids) (ips) jane 2 imap (30155 30412) (::1) $ doveadm who 192.0.2.0/24 username # proto (pids) (ips) john@example.com 1 imap (30257) (192.0.2.34) ``` Show authenticated sessions, filtered by username: sh ``` doveadm who pvo ``` ``` username # proto (pids) (ips) pvo 1 sieve (30343) (fd95:4eed:38ba::25) pvo 4 imap (25693 25686 25679 25669) (fd95:4eed:38ba::25) $ doveadm who ja\* username # proto (pids) (ips) james 1 imap (30091) (127.0.0.1) jane 2 imap (30155 30412) (::1) ``` ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # doveadm(1) - Dovecot's administration utility ## SYNOPSIS **doveadm** \[**\-DOkv**\] \[**\-i** _instance-name_\] \[**\-c** _config-file_\] \[**\-o** _key_**\=**_value_ \[ .. \]\] \[**\-f** _formatter_\] _command_ \[_command\_options_\] \[_command\_arguments_\] ## DESCRIPTION **doveadm** is the Dovecot administration tool. It can be used to manage various parts of Dovecot, as well as access users' mailboxes. Execute **doveadm help**, for a command usage listing. ## GLOBAL OPTIONS Global `[doveadm(1)](/main/core/man/doveadm.1.html)` _options_: **\-D** Enables _verbosity_ and debug messages. **\-O** Do not read any config file, just use defaults. The `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` defaults to the latest version, but can be overridden with **\-o**. **\-k** Preserve entire environment for doveadm, not just `[import_environment](/main/core/summaries/settings.html#import_environment)`. **\-v** Enables verbosity, including progress counter. **\-i** _instance-name_ If using multiple Dovecot instances, choose the config file based on this instance name. See `[instance_name](/main/core/summaries/settings.html#instance_name)` for more information. **\-c** _config-file_ Read configuration from the given _config-file_. By default it first reads config socket, and then falls back to _/etc/dovecot/dovecot.conf_. You can also point this to config socket of some instance running compatible version. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-f** _formatter_ Specifies the _formatter_ for formatting the output. Supported formatters are: **flow** prints each line with _key_**\=**_value_ pairs. **json** prints a JSON array of JSON objects. **pager** prints each _key_: _value_ pair on its own line and separates records with form feed character (**^L**). **tab** prints a table header followed by tab separated value lines. **table** prints a table header followed by adjusted value lines. ## COMMANDS These commands work directly with Dovecot's master process. ### doveadm reload Force `[dovecot(1)](/main/core/man/dovecot.1.html)` to reload the configuration. ### doveadm stop Stop `[dovecot(1)](/main/core/man/dovecot.1.html)` and all its child processes. ## ADDITIONAL MASTER COMMANDS ### doveadm config `[doveconf(1)](/main/core/man/doveconf.1.html)`, Dump Dovecot configuration. ### doveadm exec `[doveadm-exec(1)](/main/core/man/doveadm-exec.1.html)`, Easily execute commands from Dovecot's libexec\_dir. ### doveadm instance `[doveadm-instance(1)](/main/core/man/doveadm-instance.1.html)`, Manage the list of running Dovecot instances. ### doveadm kick `[doveadm-kick(1)](/main/core/man/doveadm-kick.1.html)`, Disconnect users by user name and/or IP address. ### doveadm log `[doveadm-log(1)](/main/core/man/doveadm-log.1.html)`, Locate, test or reopen Dovecot's log files. ### doveadm penalty `[doveadm-penalty(1)](/main/core/man/doveadm-penalty.1.html)`, Show current penalties. ### doveadm proxy `[doveadm-proxy(1)](/main/core/man/doveadm-proxy.1.html)`, Handle Dovecot proxy connections. ### doveadm who `[doveadm-who(1)](/main/core/man/doveadm-who.1.html)`, Show who is logged into the Dovecot server. ## AUTHENTICATION COMMANDS ### doveadm auth `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html)`, Test authentication for a user. ### doveadm pw `[doveadm-pw(1)](/main/core/man/doveadm-pw.1.html)`, Dovecot's password hash generator. ### doveadm user `[doveadm-user(1)](/main/core/man/doveadm-user.1.html)`, Perform a user lookup in Dovecot's userdbs ## MAILBOX COMMANDS ### doveadm acl `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html)`, Manage Access Control List (ACL). ### doveadm altmove `[doveadm-altmove(1)](/main/core/man/doveadm-altmove.1.html)`, Move matching mails to the alternative storage. ### doveadm backup `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html)`, Dovecot's one-way mailbox synchronization utility. ### doveadm copy `[doveadm-move(1)](/main/core/man/doveadm-move.1.html)`, Copy/move messages matching the given search query into another mailbox. ### doveadm deduplicate `[doveadm-deduplicate(1)](/main/core/man/doveadm-deduplicate.1.html)`, Expunge duplicate messages. ### doveadm dump `[doveadm-dump(1)](/main/core/man/doveadm-dump.1.html)`, Dump the content of Dovecot's binary mailbox index/log. ### doveadm expunge `[doveadm-expunge(1)](/main/core/man/doveadm-expunge.1.html)`, Expunge messages matching given search query. ### doveadm fetch `[doveadm-fetch(1)](/main/core/man/doveadm-fetch.1.html)`, Fetch messages matching given search query. ### doveadm flags `[doveadm-flags(1)](/main/core/man/doveadm-flags.1.html)`, Add, remove or replace messages' flags. ### doveadm force-resync `[doveadm-force-resync(1)](/main/core/man/doveadm-force-resync.1.html)`, Repair broken mailboxes, in case Dovecot doesn't automatically do that. ### doveadm fs `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html)`, Interact with the abstract mail storage filesystem. ### doveadm fts `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html)`, Manipulate the Full Text Search (FTS) index. ### doveadm import `[doveadm-import(1)](/main/core/man/doveadm-import.1.html)`, Import messages matching given search query. ### doveadm index `[doveadm-index(1)](/main/core/man/doveadm-index.1.html)`, Index messages in a given mailbox. ### doveadm mail fs `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html)`, Interact with the abstract mail storage filesystem in a user context. ### doveadm mailbox `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html)`, Various commands related to handling mailboxes. ### doveadm mailbox cryptokey `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html)`, Mail crypt plugin management. ### doveadm move `[doveadm-move(1)](/main/core/man/doveadm-move.1.html)`, Move messages matching the given search query into another mailbox. ### doveadm purge `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html)`, Remove messages with refcount=0 from mdbox files. ### doveadm quota `[doveadm-quota(1)](/main/core/man/doveadm-quota.1.html)`, Initialize/recalculate or show current quota usage. ### doveadm rebuild `[doveadm-rebuild(1)](/main/core/man/doveadm-rebuild.1.html)`, Rebuild index metadata from message data. ### doveadm save `[doveadm-save(1)](/main/core/man/doveadm-save.1.html)`, Save email to users' mailboxes. ### doveadm search `[doveadm-search(1)](/main/core/man/doveadm-search.1.html)`, Show a list of mailbox GUIDs and message UIDs matching given search query. ### doveadm stats `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html)`, Inspect or reset stats. ### doveadm sync `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html)`, Dovecot's two-way mailbox synchronization utility. ## EXIT STATUS **doveadm** will exit with one of the following values: **0** Selected command was executed successful. **64** Command line usage error. **65** Data format error or operation is not possible. **67** User does not exist. **68** Input file, address or other resource does not exist. **73** Cannot create output file. **75** There was some temporary error, check logs. **76** Protocol error during remote host connection. **77** Permission error. **78** Configuration error. **127** Unknown error. ## ENVIRONMENT **USER** This environment variable is used to determine the _user_ if a command accepts a _user_ but none was specified. ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/10-mail.conf_ Mailbox locations and namespaces. _/etc/dovecot/conf.d/90-plugin.conf_ Plugin specific settings. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm-help(1)](/main/core/man/doveadm-help.1.html)` # doveconf(1) - Dovecot's configuration dumping utility ## SYNOPSIS **doveconf** \[**\-aCdFInPNUwx**\] \[**\-c** _config-file_\] \[**\-f** _filter_\] **doveconf** \[**\-n**\] \[**\-c** _config-file_\] _section\_name_ ... **doveconf** \[**\-h**\] \[**\-c** _config-file_\] \[**\-f** _filter_\] _setting\_name_ ... ## DESCRIPTION **doveconf** reads and parses Dovecot's configuration files and converts them into a simpler format used by the rest of Dovecot. All standalone programs, such as `[dovecot(1)](/main/core/man/dovecot.1.html)`, will first get their settings by executing doveconf, unless they can get the settings by connecting to the config UNIX socket. For system administrators, **doveconf** is mainly useful for inspecting the current configuration in an easy human readable format, checking individual settings, and comparing configured values against their defaults. It can also be used to inspect configuration @groups by querying them directly. ## OPTIONS **\-a** Show all settings with their currently configured values. **\-C** TODO (check full config). **\-c** _config-file_ Read configuration from the given _config-file_. By default _/etc/dovecot/dovecot.conf_ will be used. **\-d** Show the setting's default value instead of the one currently configured. This can also be used to show the contents of default setting groups by querying the group name directly, for example: `doveconf -d @mailbox_defaults/english` **\-F** Show the configuration in a filter-based format, which is how Dovecot internally accesses it. This can be useful for debugging why configuration is not working as expected. The settings are grouped into different "structs", which are all accessed independently. A new struct is started in the output as `# struct_name`. Next is the list of filters, which begin with `:FILTER` followed by the filter in the event filter syntax. An empty filter matches everything. The filters are processed from end to beginning. The settings are taken from the first matching filter (i.e. the last in the output). Since not all filters have all settings defined, the processing continues until all settings have been found. Named list filter such as `protocols = imap pop3` are shown as `protocol/imap=yes` and `protocol/pop3=yes # stop list`. The "stop list" means that the value is not modified by any following filters that match. If the setting was defined as `protocols { imap=yes, pop3=yes }`, the "stop list" would be missing, because this setting is only adding the protocols, not replacing the list. Settings groups are included in `:INCLUDE` lines. The includes are processed last, after all filters have been applied, so all settings inside the groups can be overridden. **\-f** _filter_ Show the matching configuration for the specified _filter_ condition. The _filter_ option string has to be given as _name_ **\=** _value_ pair. For multiple filter conditions the **\-f** option can be supplied multiple times. Possible names for the _filter_ are: **protocol** The protocol, e.g. imap or pop3 **local\_name** The local hostname for TLS SNI matching, e.g. mail.example.com. This matches filters which were configured like: **local\_name mail.example.com { # special settings }** **local** The local hostname or IP address. This matches filters which were configured like: **local 1.2.3.0/24 { # special settings }** **remote** The client's hostname or IP address. This matches filters which were configured like: **remote 1.2.3.0/24 { # special settings }** **\-h** Hide the setting's name, show only the setting's value. **\-I** TODO (dump config import). **\-n** Show only settings with non-default values. This is the default behavior when no other parameters are given. **\-N** Show settings with non-default values and explicitly set default values. **\-s** Show also hidden settings. The hidden settings should not be changed normally. **\-P** Show passwords and other sensitive values. **\-U** Ignore all unknown settings in config file. **\-w** TODO (hide obsolete warnings). **\-x** Expand configuration variables (e.g. `$ENV:foo`) and show file contents (from e.g. `ssl_server_key_password = -.tar.gz in the current path. **\-k|--keeptemp** If set, temp files would not be deleted at the end. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) # dovecot(1) - A secure and highly configurable IMAP and POP3 server ## SYNOPSIS **dovecot** \[**\-Fp**\] \[**\-c** _config-file_\] **dovecot -a** \[**\-c** _config-file_\] **dovecot -n** \[**\-c** _config-file_\] **dovecot --build-options** **dovecot --help** **dovecot --hostdomain** **dovecot --version** **dovecot reload** **dovecot stop** ## DESCRIPTION Dovecot is an open source IMAP and POP3 server for Linux/UNIX-like systems, written with security primarily in mind. Dovecot is an excellent choice for both small and large installations. It's fast, simple to set up, requires no special administration and it uses very little memory. ## OPTIONS **\-a** Dump all configuration settings to stdout and exit successfully. The same as _doveconf -a_. **\-c** _config-file_ Start **dovecot** with an alternative configuration. **\-F** Run **dovecot** in foreground, do not daemonize. **\-n** Dump non-default settings to stdout and exit successfully. The same as _doveconf -n_. **\-p** Prompt for the ssl key password for the configured _ssl\_server\_key_ on startup. **\--build-options** Show Dovecot's build options and exit successfully. **\--help** Print a usage message to stdout and exit successfully. **\--hostdomain** Shows the current _host_._domain_ name of the system. If the domain lookup should fail for some reason, only the hostname will be shown. **\--version** Show Dovecot's version and exit successfully. ## COMMANDS **reload** Force **dovecot** to reload its configuration. **stop** Shutdown **dovecot** and all its child processes. When _shutdown\_clients_ is set to **no**, existing sessions will continue to use the old settings, after a **dovecot reload**. Also all sessions will keep alive after a **dovecot stop**. By default all active sessions will be shut down. ## SIGNALS Dovecot handles the following _signals_ as described: **HUP** Force **dovecot** to reload its configuration. **INT** Shutdown **dovecot** and all its child processes. **TERM** Shutdown **dovecot** and all its child processes. **USR1** Force **dovecot** to reopen all configured log files (_log\_path_, _info\_log\_path_ and _debug\_log\_path_). The _signals_ **ALARM** and **PIPE** are ignored. ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/_.conf\* Configuration files of different services and settings. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[doveadm(1)](/main/core/man/doveadm.1.html)` # pigeonhole(7) - Overview of the Pigeonhole Sieve support for Dovecot ## DESCRIPTION Pigeonhole adds support for the Sieve language ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and the ManageSieve protocol ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). In the literal sense, a pigeonhole is a a hole or recess inside a dovecot for pigeons to nest in. It is, however, also the name for one of a series of small, open compartments in a cabinet used for filing or sorting mail. As a verb, it describes the act of putting an item into one of those pigeonholes. The name "Pigeonhole" therefore well describes an important part of the functionality that it adds to Dovecot: sorting and filing e-mail messages. The Sieve language is used to specify how e-mail needs to be processed. By writing Sieve scripts, users can customize how messages are delivered, e.g. whether they are forwarded or stored in special folders. Unwanted messages can be discarded or rejected, and, when the user is not available, the Sieve interpreter can send an automated reply. Above all, the Sieve language is meant to be simple, extensible and system independent. And, unlike most other mail filtering script languages, it does not allow users to execute arbitrary programs. This is particularly useful to prevent virtual users from having full access to the mail store. The intention of the language is to make it impossible for users to do anything more complex (and dangerous) than write simple mail filters. Using the ManageSieve protocol, users can upload their Sieve scripts remotely, without needing direct filesystem access through FTP or SCP. Additionally, a ManageSieve server always makes sure that uploaded scripts are valid, preventing compile failures at mail delivery. Pigeonhole provides the following items: - The LDA Sieve plugin for Dovecot's Local Delivery Agent (LDA) (`[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`) that facilitates the actual Sieve filtering upon delivery. - The ManageSieve service that implements the ManageSieve protocol through which users can remotely manage Sieve scripts on the server. - A plugin for Dovecot's `[doveadm(1)](/main/core/man/doveadm.1.html)` command line tool that adds new `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html)` commands for management of Sieve filtering. - The [imap-sieve plugin](/main/core/plugins/imap_sieve.html) plugin, which provides the ability to attach Sieve scripts that are run for IMAP events in their mailboxes. - The [FILTER=SIEVE IMAP capability](/main/core/plugins/imap_filter_sieve.html) that allows refiltering mails in a mailbox using Sieve scripts. The functionality and configuration of the LDA Sieve plugin and the ManageSieve service is described in detail in the README and INSTALL files contained in the Pigeonhole package and in [Sieve](/main/core/config/sieve/overview.html). The following command line tools are available outside of **doveadm**: `[sievec(1)](/main/core/man/sievec.1.html)` Compiles Sieve scripts into a binary representation for later execution. `[sieve-test(1)](/main/core/man/sieve-test.1.html)` The universal Sieve test tool for testing the effect of a Sieve script on a particular message. `[sieve-filter(1)](/main/core/man/sieve-filter.1.html)` Filters all messages in a particular source mailbox through a Sieve script. `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)` Dumps the content of a Sieve binary file for (development) debugging purposes. ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[dovecot(1)](/main/core/man/dovecot.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[doveadm(1)](/main/core/man/doveadm.1.html)`, `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html)`, `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`, `[sieve-test(1)](/main/core/man/sieve-test.1.html)`, `[sieve-filter(1)](/main/core/man/sieve-filter.1.html)`, `[sievec(1)](/main/core/man/sievec.1.html)` Additional resources: - [Sieve](/main/core/config/sieve/overview.html) # sieve-dump(1) - Pigeonhole's Sieve script binary dump tool ## SYNOPSIS **sieve-dump** \[_options_\] _sieve-binary_ \[_out-file_\] ## DESCRIPTION The **sieve-dump** command is part of Pigeonhole (`[pigeonhole(7)](/main/core/man/pigeonhole.7.html)`), which adds Sieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) support to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). Using the **sieve-dump** command, Sieve binaries, which are produced for instance by `[sievec(1)](/main/core/man/sievec.1.html)`, can be transformed into a human-readable textual representation. This can provide valuable insight in how the Sieve script is executed. This is also particularly useful to view corrupt binaries that can result from bugs in the Sieve implementation. This tool is intended mainly for development purposes, so normally system administrators and users will not need to use this tool. The format of the output is not explained here in detail, but it should be relatively easy to understand. The Sieve binaries comprise a set of data blocks, each of which can contain arbitrary data. For the base language implementation two blocks are used: the first containing a specification of all required language extensions and the second containing the main Sieve program. Compiled Sieve programs are represented as flat byte code and therefore the dump of the main program is a disassembly listing of the interpreter operations. Extensions can define new operations and use additional blocks. Therefore, the output of **sieve-dump** depends greatly on the language extensions used when compiling the binary. ## OPTIONS **\-c** _config-file_ Alternative Dovecot configuration file path. **\-D** Enable Sieve debugging. **\-h** Produce per-block hexdump output of the whole binary instead of the normal human-readable output. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _sieve-binary_ Specifies the Sieve binary file that needs to be dumped. _out-file_ Specifies where the output must be written. This argument is optional. If omitted, the output is written to **stdout**. ## EXIT STATUS **sieve-dump** will exit with one of the following values: **0** Dump was successful. (EX\_OK, EXIT\_SUCCESS) **1** Operation failed. This is returned for almost all failures. (EXIT\_FAILURE) **64** Invalid parameter given. (EX\_USAGE) ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/90-sieve.conf_ Sieve interpreter settings (included from Dovecot's main configuration file) ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[dovecot(1)](/main/core/man/dovecot.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[sieve-filter(1)](/main/core/man/sieve-filter.1.html)`, `[sieve-test(1)](/main/core/man/sieve-test.1.html)`, `[sievec(1)](/main/core/man/sievec.1.html)`, `[pigeonhole(7)](/main/core/man/pigeonhole.7.html)` # sieve-filter(1) - Pigeonhole's Sieve mailbox filter tool WARNING This tool is still experimental. Read this manual carefully, and backup any important mail before using this tool. Also note that some of the features documented here are not actually implemented yet; this is clearly indicated where applicable. ## SYNOPSIS **sieve-filter** \[_options_\] _script-file_ _source-mailbox_ \[_discard-action_\] ## DESCRIPTION The **sieve-filter** command is part of Pigeonhole (`[pigeonhole(7)](/main/core/man/pigeonhole.7.html)`), which adds Sieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) support to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). The Sieve language was originally meant for filtering messages upon delivery. However, there are occasions when it is desirable to filter messages that are already stored in a mailbox, for instance when a bug in a Sieve script caused many messages to be delivered incorrectly. Using the sieve-filter tool it is possible to apply a Sieve script on all messages in a particular _source-mailbox_, making it possible to delete messages, to store them in a different mailbox, to change their content, and to change the assigned IMAP flags and keywords. Attempts to send messages to the outside world are ignored by default for obvious reasons, but, using the proper command line options, it is possible to capture and handle outgoing mail as well. If no options are specified, the sieve-filter command runs in a simulation mode in which it only prints what would be performed, without actually doing anything. Use the **\-e** option to activate true script execution. Also, the _source-mailbox_ is opened read-only by default, meaning that it normally always remains unchanged. Use the **\-W** option to allow changes in the _source-mailbox_. Even with the **\-W** option enabled, messages in the _source-mailbox_ are only potentially modified or moved to a different folder. Messages are never lost unless a _discard-action_ argument other than **keep** (the default) is specified. If the Sieve filter decides to store the message in the _source-mailbox_, where it obviously already exists, it is never duplicated there. In that case, the IMAP flags of the original message can be modified by the Sieve interpreter using the _imap4flags_ extension, provided that **\-W** is specified. If the message itself is modified by the Sieve interpreter (e.g. using the _editheader_ extension), a new message is stored and the old one is expunged. However, if **\-W** is omitted, the original message is left untouched and the modifications are discarded. ## CAUTION Although this is a very useful tool, it can also be very destructive when used improperly. A small bug in your Sieve script in combination with the wrong command line options could cause it to discard the wrong e-mails. And, even if the _source-mailbox_ is opened in read-only mode to prevent such mishaps, it can still litter other mailboxes with spurious copies of your e-mails if your Sieve script decides to do so. Therefore, users are advised to read this manual carefully and to use the simulation mode first to check what the script will do. And, of course: **MAKING A BACKUP IS IMPERATIVE FOR ANY IMPORTANT MAIL!** ## OPTIONS **\-c** _config-file_ Alternative Dovecot configuration file path. **\-C** Force compilation. By default, the compiled binary is stored on disk. When this binary is found during the next execution of **sieve-filter** and its modification time is more recent than the script file, it is used and the script is not compiled again. This option forces the script to be compiled, thus ignoring any present binary. Refer to `[sievec(1)](/main/core/man/sievec.1.html)` for more information about Sieve compilation. **\-d** _dump-file_ Causes a dump of the generated code to be written to the specified file. This is identical to the dump produced by `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`. Using '-' as filename causes the dump to be written to **stdout**. **\-D** Enable Sieve debugging. **\-e** Turns on execution mode. By default, the sieve-filter command runs in simulation mode in which it changes nothing, meaning that no mailbox is altered in any way and no actions are performed. It only prints what would be done. Using this option, the sieve-filter command becomes active and performs the requested actions. **\-m** _default-mailbox_ The mailbox where the (implicit) **keep** Sieve action stores messages. This is equal to the _source-mailbox_ by default. Specifying a different folder will have the effect of moving (or copying if **\-W** is omitted) all kept messages to the indicated folder, instead of just leaving them in the _source-mailbox_. Refer to the explanation of the _source-mailbox_ argument for more information on mailbox naming. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-q** _output-mailbox_ **\[not implemented yet\]** Store outgoing e-mail into the indicated _output-mailbox_. By default, the sieve-filter command ignores Sieve actions such as redirect, reject, vacation and notify, but using this option outgoing messages can be appended to the indicated mailbox. This option has no effect in simulation mode. Flags of redirected messages are not preserved. **\-Q** _mail-command_ **\[not implemented yet\]** Send outgoing e-mail (e.g. as produced by redirect, reject and vacation) through the specified program. By default, the sieve-filter command ignores Sieve actions such as redirect, reject, vacation and notify, but using this option outgoing messages can be fed to the **stdin** of an external shell command. This option has no effect in simulation mode. Unless you really know what you are doing, **DO NOT USE THIS TO FEED MAIL TO SENDMAIL!**. **\-s** _script-file_ **\[not implemented yet\]** Specify additional scripts to be executed before the main script. Multiple **\-s** arguments are allowed and the specified scripts are executed sequentially in the order specified at the command line. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-v** Produce verbose output during filtering. **\-W** Enables write access to the _source-mailbox_. This allows (re)moving the messages from the _source-mailbox_, changing their contents, and changing the assigned IMAP flags and keywords. **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _script-file_ Specifies the Sieve script to (compile and) execute. Note that this tool looks for a pre-compiled binary file with a _.svbin_ extension and with basename and path identical to the specified script. Use the **\-C** option to disable this behavior by forcing the script to be compiled into a new binary. _source-mailbox_ Specifies the source mailbox containing the messages that the Sieve filter will act upon. This is the name of a mailbox, as visible to IMAP clients, except in UTF-8 format. The hierarchy separator between a parent and child mailbox is commonly '**/**' or '**.**', but this depends on your selected mailbox storage format and namespace configuration. The mailbox names may also require a namespace prefix. This mailbox is not modified unless the **\-W** option is specified. _discard-action_ Specifies what is done with messages in the _source-mailbox_ that where not kept or otherwise stored by the Sieve script; i.e. those messages that would normally be discarded if the Sieve script were executed at delivery. The _discard-action_ parameter accepts one of the following values: : **keep** (default) : Keep discarded messages in source mailbox. : **move** _mailbox_ : Move discarded messages to the indicated _mailbox_. This is for instance useful to move messages to a Trash mailbox. Refer to the explanation of the _source-mailbox_ argument for more information on mailbox naming. : **delete** : Flag discarded messages as \\DELETED. : **expunge** : Expunge discarded messages, meaning that these are removed irreversibly when the tool finishes filtering. When the **\-W** option is not specified, the _source-mailbox_ is immutable and the specified _discard-action_ has no effect. This means that messages are at most _copied_ to a new location. In contrast, when the **\-W** is specified, messages that are successfully stored somewhere else by the Sieve script are **always** expunged from the _source-mailbox_, with the effect that these are thus _moved_ to the new location. This happens irrespective of the specified _discard-action_. Remember: only discarded messages are affected by the specified _discard-action_. ## EXIT STATUS **sieve-filter** will exit with one of the following values: **0** Sieve filter applied successfully. (EX\_OK, EXIT\_SUCCESS) **64** Command line usage error. **65** Data format error or operation is not possible. **67** User does not exist. **68** Input file, address or other resource does not exist. **73** Cannot create output file. **75** There was some temporary error, check logs. **76** Protocol error during remote host connection. **77** Permission error. **78** Configuration error. **127** Unknown error. ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/90-sieve.conf_ Sieve interpreter settings (included from Dovecot's main configuration file) ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[dovecot(1)](/main/core/man/dovecot.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`, `[sieve-test(1)](/main/core/man/sieve-test.1.html)`, `[sievec(1)](/main/core/man/sievec.1.html)`, `[pigeonhole(7)](/main/core/man/pigeonhole.7.html)` # sieve-test(1) - Pigeonhole's Sieve script tester ## SYNOPSIS **sieve-test** \[_options_\] _script-file_ _mail-file_ ## DESCRIPTION The **sieve-test** command is part of Pigeonhole (`[pigeonhole(7)](/main/core/man/pigeonhole.7.html)`), which adds Sieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) support to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). Using the **sieve-test** command, the execution of Sieve scripts can be tested. This evaluates the script for the provided message, yielding a set of Sieve actions. Unless the **\-e** option is specified, it does not actually execute these actions, meaning that it does not store or forward the message anywhere. Instead, it prints a detailed list of what actions would normally take place. Note that, even when **\-e** is specified, no messages are ever transmitted to remote SMTP recipients. The outgoing messages are always printed to **stdout** instead. This is a very useful tool to debug the execution of Sieve scripts. It can be used to verify newly installed scripts for the intended behaviour and it can provide more detailed information about script execution problems that are reported by the Sieve plugin, for example by tracing the execution and evaluation of commands and tests respectively. ## OPTIONS **\-a** _orig-recipient-address_ The original envelope recipient address. This is what Sieve's envelope test will compare to when the "to" envelope part is requested. Some tests and actions will also use this as the script owner's e-mail address. If this option is omitted, the recipient address is retrieved from the "Envelope-To:", or "To:" message headers. If none of these headers is present either, the recipient address defaults to _[recipient@example.com](mailto:recipient@example.com)_. **\-c** _config-file_ Alternative Dovecot configuration file path. **\-C** Force compilation. By default, the compiled binary is stored on disk. When this binary is found during the next execution of **sieve-test** and its modification time is more recent than the script file, it is used and the script is not compiled again. This option forces the script to be compiled, thus ignoring any present binary. Refer to `[sievec(1)](/main/core/man/sievec.1.html)` for more information about Sieve compilation. **\-D** Enable Sieve debugging. **\-d** _dump-file_ Causes a dump of the generated code to be written to the specified file. This is identical to the dump produced by `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`. Using '-' as filename causes the dump to be written to **stdout**. **\-e** Enables true execution of the set of actions that results from running the script. Use he **\-o** parameter to specify the mail\_driver, mail\_path and other necessary storage settings. This allows actual delivery of messages can be tested. Note that this will not transmit any messages to remote SMTP recipients. Such actions only print the outgoing message to **stdout**. **\-f** _envelope-sender_ The envelope sender address (return path). This is what Sieve's envelope test will compare to when the "from" envelope part is requested. Also, this is where response messages are 'sent' to. If this option is omitted, the sender address is retrieved from the "Return-Path:", "Sender:" or "From:" message headers. If none of these headers is present either, the sender envelope address defaults to _[sender@example.com](mailto:sender@example.com)_. **\-m** _default-mailbox_ The mailbox where the keep action stores the message. This is "INBOX" by default. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-r** _recipient-address_ The final envelope recipient address. Some tests and actions will use this as the script owner's e-mail address. For example, this is what is used by the vacation action to check whether a reply is appropriate. If the **\-r** option is omitted, the original envelope recipient address will be used instead (see **\-a** option for more info). **\-s** _script-file_ Specify additional scripts to be executed before the main script. Multiple **\-s** arguments are allowed and the specified scripts are executed sequentially in the order specified at the command line. **\-t** _trace-file_ Enables runtime trace debugging. Trace debugging provides detailed insight in the operations performed by the Sieve script. Refer to the runtime trace debugging section below. The trace information is written to the specified file. Using '-' as filename causes the trace data to be written to **stdout**. **\-T** _trace-option_ Configures runtime trace debugging, which is enabled with the **\-t** option. Refer to the runtime trace debugging section below. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _script-file_ Specifies the script to (compile and) execute. Note that this tool looks for a pre-compiled binary file with a _.svbin_ extension and with basename and path identical to the specified script. Use the **\-C** option to disable this behavior by forcing the script to be compiled into a new binary. _mail-file_ Specifies the file containing the e-mail message to test with. ## USAGE ### RUNTIME TRACE DEBUGGING Using the **\-t** option, the **sieve-test** tool can be configured to print detailed trace information on the Sieve script execution to a file or standard output. For example, the encountered commands, the performed tests and the matched values can be printed. The runtime trace can be configured using the **\-T** option, which can be specified multiple times. It can be used as follows: **\-T level=...** Set the detail level of the trace debugging. One of the following values can be supplied: : _actions_ (default) : Only print executed action commands, like keep, fileinto, reject and redirect. : _commands_ : Print any executed command, excluding test commands. : _tests_ : Print all executed commands and performed tests. : _matching_ : Print all executed commands, performed tests and the values matched in those tests. **\-T debug** Print debug messages as well. This is usually only useful for developers and is likely to produce messy output. **\-T addresses** Print byte code addresses for the current trace output. Normally, only the current Sieve source code position (line number) is printed. The byte code addresses are equal to those listed in a binary dump produced using the **\-d** option or by the `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)` command. ### DEBUG SIEVE EXTENSION To improve script debugging, this Sieve implementation supports a custom Sieve language extension called 'vnd.dovecot.debug'. It adds the **debug\_log** command that allows logging debug messages. Example: ``` require "vnd.dovecot.debug"; if header :contains "subject" "hello" { debug_log "Subject header contains hello!"; } ``` Tools such as **sieve-test**, `[sievec(1)](/main/core/man/sievec.1.html)` and `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)` have support for the vnd.dovecot.debug extension enabled by default and it is not necessary to enable nor possible to disable the availability of the debug extension with the **\-x** option. The logged messages are written to **stdout** in this case. In contrast, for the actual Sieve plugin for the Dovecot LDA (`[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`) the vnd.dovecot.debug extension needs to be enabled explicitly using the `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` or `[sieve_global_extensions](/main/core/summaries/settings.html#sieve_global_extensions)` setting. The messages are then logged to the user's private script log file. If used in a global script, the messages are logged through the default Dovecot logging facility. ## EXIT STATUS **sieve-test** will exit with one of the following values: **0** Execution was successful. (EX\_OK, EXIT\_SUCCESS) **1** Operation failed. This is returned for almost all failures. (EXIT\_FAILURE) **64** Invalid parameter given. (EX\_USAGE) ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/90-sieve.conf_ Sieve interpreter settings (included from Dovecot's main configuration file) ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[dovecot(1)](/main/core/man/dovecot.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`, `[sieve-filter(1)](/main/core/man/sieve-filter.1.html)`, `[sievec(1)](/main/core/man/sievec.1.html)`, `[pigeonhole(7)](/main/core/man/pigeonhole.7.html)` # sievec(1) - Pigeonhole's Sieve script compiler ## SYNOPSIS **sievec** \[_options_\] _script-file_ \[_out-file_\] ## DESCRIPTION The **sievec** command is part of Pigeonhole (`[pigeonhole(7)](/main/core/man/pigeonhole.7.html)`), which adds Sieve ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and ManageSieve ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) support to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). Using the **sievec** command, Sieve scripts can be compiled into a binary representation. The resulting binary can be used directly to process e-mail messages during the delivery process. The delivery of mail messages and - by means of the LDA Sieve plugin - also the execution of Sieve scripts is performed by Dovecot's local delivery agent (LDA) called `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`. Usually, it is not necessary to compile the Sieve script manually using **sievec**, because `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)` will do this automatically if the binary is missing. However, in some cases `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)` does not have permission to write the compiled binary to disk, forcing it to recompile the script every time it is executed. Using the **sievec** tool, this can be performed manually by an authorized user to increase performance. The Pigeonhole Sieve implementation recognizes files with a **.sieve** extension as Sieve scripts and corresponding files with a **.svbin** extension as the associated compiled binary. This means for example that Dovecot's LDA process will first look for a binary file "dovecot.svbin" when it needs to execute "dovecot.sieve". It will compile a new binary when it is missing or outdated. The **sievec** command is also useful to verify Sieve scripts before using. Additionally, with the **\-d** option it can output a textual (and thus human-readable) dump of the generated Sieve code to the specified file. The output is then identical to what the `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)` command produces for a stored binary file. This output is mainly useful to find bugs in the compiler that yield corrupt binaries. ## OPTIONS **\-c** _config-file_ Alternative Dovecot configuration file path. **\-d** Don't write the binary to _out-file_, but write a textual dump of the binary instead. In this context, the _out-file_ value '-' has special meaning: it causes the textual dump to be written to **stdout**. The _out-file_ argument may also be omitted, which has the same effect as '-'. The output is identical to what the `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)` command produces for a compiled Sieve binary file. Note that this option is not allowed when the _out-file_ argument is a directory. **\-D** Enable Sieve debugging. **\-o** _setting_**\=**_value_ Overrides the configuration _setting_ from _/etc/dovecot/dovecot.conf_ and from the userdb with the given _value_. In order to override multiple settings, the **\-o** option may be specified multiple times. **\-u** _user/mask_ Run the _command_ only for the given _user_. It's also possible to use '**\***' and '**?**' wildcards (e.g. -u \*@example.org). **\-x** _auth\_info_ _auth\_info_ specifies additional conditions for the **user** command. The _auth\_info_ option string has to be given as _name_ **\=** _value_ pair. For multiple conditions the **\-x** option could be supplied multiple times. Possible names for the _auth\_info_ are: **service** The service for which the userdb lookup should be tested. The value may be the name of a service, commonly used with Dovecot. For example: **imap**, **pop3** or **smtp**. **session** Session identifier. **lip** The local IP address (server) for the test. **rip** The remote IP address (client) for the test. **lport** The local port, e.g. 143 **rport** The remote port, e.g. 24567 **real\_lip** The local IP to which the client connected on this host. **real\_rip** The remote IP where client connected from to this host. **real\_lport** The local port to which client connected to to this host. **real\_rport** The remote port from where the client connected from to this host. **forward\_** Field to forward as %{forward:field} to auth process. ## ARGUMENTS _script-file_ Specifies the script to be compiled. If the _script-file_ argument is a directory, all files in that directory with a _.sieve_ extension are compiled into a corresponding _.svbin_ binary file. The compilation is not halted upon errors; it attempts to compile as many scripts in the directory as possible. Note that the **\-d** option and the _out-file_ argument are not allowed when the _script-file_ argument is a directory. _out-file_ Specifies where the (binary) output is to be written. This argument is optional. If this argument is omitted, a binary compiled from .sieve is saved as .svbin. If this argument is omitted and **\-b** is specified, the binary dump is output to **stdout**. ## EXIT STATUS **sievec** will exit with one of the following values: **0** Compile was successful. (EX\_OK, EXIT\_SUCCESS) **1** Operation failed. This is returned for almost all failures. (EXIT\_FAILURE) **64** Invalid parameter given. (EX\_USAGE) **67** User does not exist. **68** Input file, address or other resource does not exist. **73** Cannot create output file. **77** Permission error. **78** Configuration error. **127** Unknown error. ## FILES _/etc/dovecot/dovecot.conf_ Dovecot's main configuration file. _/etc/dovecot/conf.d/90-sieve.conf_ Sieve interpreter settings (included from Dovecot's main configuration file) ## REPORTING BUGS Report bugs, including _doveconf -n_ output, to the Dovecot Mailing List [dovecot@dovecot.org](mailto:dovecot@dovecot.org). Information about reporting bugs is available at: [https://dovecot.org/bugreport.html](https://dovecot.org/bugreport.html) ## SEE ALSO `[dovecot(1)](/main/core/man/dovecot.1.html)`, `[dovecot-lda(1)](/main/core/man/dovecot-lda.1.html)`, `[sieve-dump(1)](/main/core/man/sieve-dump.1.html)`, `[sieve-filter(1)](/main/core/man/sieve-filter.1.html)`, `[sieve-test(1)](/main/core/man/sieve-test.1.html)`, `[pigeonhole(7)](/main/core/man/pigeonhole.7.html)` # ACL: Access Control List Plugin (`acl`) This page talks mainly about how ACLs work, for more general description of how shared mailboxes work, see [shared mailboxes](/main/core/config/shared_mailboxes.html). Dovecot supports both administrator-configured ACL files and the IMAP ACL extension (see [imap-acl plugin](/main/core/plugins/imap_acl.html), which allows users to change ACLs themselves). The ACL code was written to allow multiple ACL drivers, but currently Dovecot supports only virtual ACL files. WARNING Using ACLs doesn't grant mail processes any extra filesystem permissions that they already don't have. [You must make sure that the processes have enough permissions](/main/core/config/shared_mailboxes.html#filesystem-permissions) to be able to access the mailboxes. To test, you can first try accessing shared/public mailboxes without ACL plugin enabled. ## Settings (dovecot.conf) ### `acl` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[acl_id](/main/core/summaries/settings.html#acl_id)` - `[acl_rights](/main/core/summaries/settings.html#acl_rights)` **Changes:** - Added: 2.4.0 Specifies an ACL entry on global, namespace or mailbox level. The filter name refers to the `[acl_id](/main/core/summaries/settings.html#acl_id)` setting. Has two settings: `[acl_id](/main/core/summaries/settings.html#acl_id)` The identifier in ACL. `[acl_rights](/main/core/summaries/settings.html#acl_rights)` The permission to grant - or deny - for this user. ### `acl_defaults_from_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, the default ACLs for private and shared namespaces (but not public namespaces) are taken from the INBOX. This means that giving somebody access to your INBOX will give them access to all your other mailboxes as well, unless the specific mailboxes' ACLs override the INBOX's. ### `acl_dict_index` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.2 Should ACL dict updates assume that there is a reverse lookup index. This should be used with SQL/CQL based dicts. ### `acl_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 The ACL driver to use. This setting is **REQUIRED** - if empty, the acl plugin is disabled. Currently, there is a single driver available: `vfile`. This driver supports two ways of defining the ACL configuration: - _global_: ACL rules are applied to all users. - _per-mailbox_: Each mailbox has separate ACL rules. They are stored in a `dovecot-acl` file in each mailbox (or `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)`) directory. This is the default. ### `acl_global_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` **Changes:** - Removed: 2.4.3 Location of global ACL configuration file. This option is deprecated, you should use `[acl](/main/core/summaries/settings.html#acl)` instead. ### `acl_globals_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, don't try to find `dovecot-acl` files from mailbox directories. This reduces unnecessary disk I/O when only global ACLs are used. ### `acl_groups` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) A comma-separated string which contains all the groups the user belongs to. A user's UNIX groups have no effect on ACLs (you can enable them by using a special [post-login scripting](/main/core/config/post_login_scripting.html). The default ACL for mailboxes is to give the mailbox owner all permissions and other users none. Mailboxes in public namespaces don't have owners, so by default no one can access them. ### `acl_id` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` - [ACL File Format](/main/core/plugins/acl.html#acl-file-format) **Changes:** - Added: 2.4.0 Specifies identity to match. See [ACL File Format](/main/core/plugins/acl.html#acl-file-format) for values. The `[acl](/main/core/summaries/settings.html#acl)` filter name refers to this setting. ### `acl_ignore` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 Can be used in global config, namespace, or mailbox level to ignore ACLs. ``` namespace ignore { acl_ignore = yes } ``` ### `acl_rights` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` - [ACL File Format](/main/core/plugins/acl.html#acl-file-format) **Changes:** - Added: 2.4.0 Specifies rights for this acl. See [ACL File Format](/main/core/plugins/acl.html#acl-file-format) for values. This is usually used in `[acl](/main/core/summaries/settings.html#acl)` block. ### `acl_sharing_map` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [dictionary](/main/core/config/dict.html) **Changes:** - Added: 2.4.0 A shared mailbox dictionary that defines which users may LIST mailboxes shared by other users. Not every dictionary driver is supported here. The map needs dict\_iterate which is not supported by redis and ldap. See [shared mailbox listing](/main/core/config/shared_mailboxes.html#shared-mailbox-listing) for further details on the contents of the dictionary entries. Example: ``` acl_sharing_map { dict file { path = /var/lib/dovecot/shared-mailboxes } } ``` ### `acl_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [ACL Master Users](/main/core/config/auth/master_users.html#acls) See `[auth_master_user_separator](/main/core/summaries/settings.html#auth_master_user_separator)` for the format of this setting. ## Configuration ### Groups The `[acl_groups](/main/core/summaries/settings.html#acl_groups)` setting can be dynamically set via [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). ### IMAP ACLs To enable the IMAP ACL commands, you must load the [imap\_acl plugin](/main/core/plugins/imap_acl.html). This plugin should only be loaded inside a `protocol imap {}` block. ### Sample Configuration doveconf ``` # Enable internal ACL support mail_plugins { acl = yes } # Enable the IMAP ACL commands protocol imap { mail_plugins { imap_acl = yes } } acl_driver = vfile # If enable, don't try to find dovecot-acl files from mailbox directories. # This reduces unnecessary disk I/O when only global ACLs are used. # (v2.2.31+) acl_globals_only = yes namespace inbox [ inbox = yes mailbox Foo { acl owner { rights = lr } } acl user=admin { rights = lwristepai } ## Set this to yes to ignore ACLS for this namespace #acl_ignore = yes } ## setting ACLs here will affect all shared mailboxes namespace shared { mailbox Public { acl anyone { rights = lr } } } # Dict for mapping which users have shared mailboxes to each other. #acl_sharing_map { # dict file { # path = /var/lib/dovecot/dovecot-acl.db # } #} # ACL username # defaults to master_user, but if it expands to empty, will use current user. #acl_username = %{master_user} ``` ### Creating rule sets with group You can also simplify ACL rule set management by defining rule sets. Group settings expand as configuration where they are used, so they can include values from other filters too. ``` # define rule sets group @acl_rule_set set1 { acl user=user1 { rights = lri } } group @acl_rule_set set2 { acl user=user2 { rights = lri } } group @acl_rule_set set3 { acl user=user3 { rights = lri } } group @acl_rule_set default { acl user=admin { rights = lwristepai } } namespace public { @acl_rule_set = default mailbox "Secret" { @acl_rule_set = set1 } mailbox "TopSecret" { @acl_rule_set = set2 } mailbox "Foo*" { @acl_rule_set = set3 } mailbox "FooBar" { @acl_rule_set = set1 } } ``` With this configuration user `admin` will have full rights to all folders under public unless negated. Folders `Secret` and `FooBar` will have `user1` with rights, while folders `TopSecret` has `user2` with rights. This includes user `admin` from namespace level. User `user3` will have rights on anything that starts with `Foo`, including `FooBar`. ## Master Users INFO Deprecated: 2.4.0: This setting is deprecated in favor of configuration-file embedded settings. Master users have their own ACLs. They're not the mailbox owners, so by default they have no permissions to any of the mailboxes. See [ACL Master Users](/main/core/config/auth/master_users.html#acls) for more information. ## ACL vfile Driver `vfile` driver supports per-mailbox ACLs and global ACLs. Per-mailbox ACLs are stored in `dovecot-acl` named file, which exists in: - Maildir:: The Maildir's mail directory (e.g., `~/Maildir`, `~/Maildir/.folder/`). - mbox: Control directory. You should explicitly specify `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)`. - dbox: dbox's mail directory (e.g., `~/dbox/INBOX/dbox-Mails/`). ## ACL File Format The ACL files are in format: ``` [:] ``` Where **identifier** is one of: - `group-override=` - `user=` - `owner` - `group=` - `authenticated` - `anyone` (or `anonymous`) - Negative rights can be given by prepending the identifier with `-` The ACLS are processed in the precedence given above, so for example if you have given read-access to a group, you can still remove that from specific users inside the group. Group-override identifier allows you to override users' ACLs. Probably the most useful reason to do this is to temporarily disable access for some users. For example: ``` user=foo rw group-override=tempdisabled ``` Now if foo is in tempdisabled group, he has no access to the mailbox. This wouldn't be possible with a normal group identifier, because the `user=foo` would override it. Negative rights can be used to remove rights. For example a user may be given full rights to all mailboxes, except some of the rights removed from some specific mailboxes. ### Supported ACLs | ID | Type | Description | | --- | --- | --- | | `l` | lookup | Mailbox is visible in mailbox list. Mailbox can be subscribed to. | | `r` | read | Mailbox can be opened for reading. | | `w` | write | Message flags and keywords can be changed, except \\Seen and \\Deleted | | `s` | write-seen | \\Seen flag can be changed | | `t` | write-deleted | \\Deleted flag can be changed | | `i` | insert | Messages can be written or copied to the mailbox | | `p` | post | Messages can be posted to the mailbox by [LDA](/main/core/config/delivery/lda.html), e.g. from Sieve | | `e` | expunge | Messages can be expunged | | `k` | create | Mailboxes can be created (or renamed) directly under this mailbox (but not necessarily under its children, see [ACL Inheritance](#acl-inheritance-and-default-acls)) (renaming also requires delete rights) | | `x` | delete | Mailbox can be deleted | | `a` | admin | Administration rights to the mailbox (currently: ability to change ACLs for mailbox) | The ACLs are compatible with [RFC 4314](https://datatracker.ietf.org/doc/html/rfc4314) (IMAP ACL extension). Unknown ACL letters are complained about, but unknown named ACLs are ignored. Named ACLs are mostly intended for future extensions. TIP The file is rather picky about formatting; using a tab (or multiple spaces) instead of a space character between fields may not work. If you are having problems, make sure to check for tabs, extra spaces and other unwanted characters. ### Examples Mailbox owner has all privileges, `foo` has list-read privileges: ``` owner lrwstipekxa user=foo lr ``` Allow everyone to list and read a public mailbox (public namespace has no owner): ``` anyone lr ``` Prevent all users from deleting their Spam folder (notice no x flag): ``` INBOX.Spam owner lrwstipeka ``` Allow a masteruser full access to all mailboxes, except no access to INBOX: ``` * user=masteruser lrwstipekxa INBOX -user=masteruser lrwstipekxa ``` ## ACL Inheritance and Default ACLs Every time you create a new mailbox, it gets its ACLs from the parent mailbox. If you're creating a root-level mailbox, it uses the namespace's default ACLs. There is no actual inheritance, however: If you modify parent's ACLs, the child's ACLs stay the same. There is currently no support for ACL inheritance. There are default ACLs though: - In private namespace, the owner has all ACL rights for mailboxes in the namespace. - In shared and public namespaces, there are no ACL rights by default. - However, optionally the default ACLs can be taken from the INBOX for private and shared namespaces. See `[acl_defaults_from_inbox](/main/core/summaries/settings.html#acl_defaults_from_inbox)`. WARNING Currently the default ACLs are merged with the mailbox-specific ACLs. So if a default ACL gives access to `user1` and a per-mailbox ACL gives access to `user2`, the `user1` still has access to that mailbox. ## Global ACLs Global ACLs can be used to apply ACLs globally to all user's specific mailboxes. They are used mainly for two purposes: 1. Removing some permissions from users' personal mailboxes. For example each user might have an `Invoices` mailbox which will be read-only. 2. Giving permissions to master user logins. See [ACL Master Users](/main/core/config/auth/master_users.html#acls) for more information. If a mailbox has both global ACLs and the per-mailbox ACL file, both of them are read and the ACLs are merged. If there are any conflicts, the global ACL file overrides per-mailbox ACL file. This is because users can modify their own per-mailbox ACL files via IMAP ACL extension. Global ACLs can only be modified by administrator, so users shouldn't be able to override them. ### Global ACL File Global ACL file path is specified as a parameter to vfile driver in `[acl](/main/core/summaries/settings.html#acl)`, `/etc/dovecot/dovecot-acl` in the above example. The file contains otherwise the same data as regular per-mailbox `dovecot-acl` files, except each line is prefixed by the mailbox name pattern. The pattern may contain `*` and `?` wildcards that do the shell-string matching, not stopping at any boundaries. Example: ``` * user=foo lrw Public user=bar lrwstipekxa Public/* user=bar lrwstipekxa ``` The first line shares every mailbox of every user to the user `foo` with a limited set of rights, and the last line shares every folder below `Public` of every user to the user `bar`. ## List Cache `dovecot-acl-list` file lists all mailboxes that have `l` rights assigned. If you manually add/edit `dovecot-acl` files, you may need to delete the `dovecot-acl-list` to get the mailboxes visible. ## Dictionaries In order for an ACL to be fully useful, it has to be communicated to IMAP clients. For example, if you use ACL to share a mailbox to another user, the client has to be explicitly told to check out the other user's mailbox too, as that one is shared. Placing the ACL file makes the ACL effective, but Dovecot doesn't take care of the user to shared mailboxes mapping out of the box, and as a result, it won't publish shared mailboxes to clients if this is not set up. You have to configure this manually by defining an appropriate [dictionary](/main/core/config/dict.html) to store the map using `[acl_sharing_map](/main/core/summaries/settings.html#acl_sharing_map)`. doveconf ``` acl_sharing_map { dict file { path = /var/lib/dovecot/dovecot-acl.db } } ``` # AppArmor Plugin (`apparmor`) [AppArmor](https://www.wikipedia.org/wiki/AppArmor) plugin, which allows changing "hat" (apparmor context) when user is loaded. Context is changed back to default on user deinit. Multiple hats are supported and passed to [`aa_change_hatv()`](https://gitlab.com/apparmor/apparmor/-/wikis/manpage_aa_change_hat.2) function. ## Settings ### `apparmor_hats` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of AppArmor "hats" to change to when a user is loaded. Example: ``` apparmor_hats { hat_name = yes another_hat = yes } ``` ## Settings: Extra Fields You can also specify hats from user or password database extra fields. ### Password Database If you provide from [passdb](/main/core/config/auth/passdb.html), use `userdb_apparmor_hats=hat`. ### User Database If you provide from [userdb](/main/core/config/auth/userdb.html), use `apparmor_hats=hat`. ## Sample Configuration doveconf ``` mail_plugins { apparmor = yes } apparmor_hats = hat_name ``` ## Debugging Enable `[log_debug](/main/core/summaries/settings.html#log_debug)` to see context changes. # Charset Alias Plugin (`charset-alias`) This plugin allows treating the specified source charset as a different charset when decoding to UTF-8. Example: when decoding from shift\_jis to UTF-8, using cp932 (or sjis-win) instead of shift\_jis may be preferable to handle Microsoft extended chars properly. ## Settings ### `charset_aliases` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) List of `=` charsets. The "from" charsets will be treated as "to" charsets when decoding to UTF-8. Example: ``` charset_aliases { shift_jis = sjis-win euc-jp = eucjp-win iso-2022-jp = iso-2022-jp-3 } ``` ## Sample Configuration doveconf ``` mail_plugins { charset_alias = yes } charset_aliases { shift_jis = sjis-win euc-jp = eucjp-win iso-2022-jp = iso-2022-jp-3 } ``` # Compression Plugin (`fs-compress`) `fs-compress` plugin is used to wrap other data streams in a compression layer. It can be used by any of the settings using the [FS drivers](/main/core/config/fs.html). The exact location where to set it in the FS driver hierarchy depends on what other FS drivers are being used. The important rules are: - Must be set before the final storage driver (`s3`, `sproxyd`, ...) - Should be set after `fscache` (you generally don't want `fscache` to be compressed for performance reasons). - Must be set before [fs\_crypt](/main/core/plugins/mail_crypt.html#fs-crypt), because encrypted data compresses poorly. ## Settings ### `fs_compress_read_plain_fallback` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [fs-compress plugin](/main/core/plugins/fs_compress.html) By default [fs-compress plugin](/main/core/plugins/fs_compress.html) fails if the file wasn't compressed. If this setting is enabled the file is returned as-is (i.e. allows reading plaintext files). ### `fs_compress_write_method` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Compression Methods](/main/core/plugins/mail_compress.html#compression-methods) Which [Compression Method](/main/core/plugins/mail_compress.html#compression-methods) to use for writing new files. Empty value means that compression is disabled. # Dovecot Flatcurve FTS Plugin (`fts-flatcurve`) Added: 2.4.0 This is a Dovecot FTS plugin to enable message indexing using the [Xapian](https://xapian.org/) Open Source Search Engine Library. WARNING Requires Xapian 1.4+. The plugin relies on Dovecot to do the necessary stemming. It is intended to act as a simple interface to the Xapian storage/search query functionality. This driver supports match scoring and substring matches (on by default), which means it is [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501) (IMAP4rev1) compliant. This driver does not support fuzzy searches. The driver passes all of the [ImapTest search tests](https://github.com/dovecot/imaptest/). TIP This plugin requires the [fts plugin](/main/core/plugins/fts.html) to be activated and configured Enabling flatcurve is designed to be as easy as adding this to configuration: doveconf ``` mail_plugins { fts = yes fts_flatcurve = yes } fts flatcurve { } ``` ## Settings TIP The default settings should be fine in most scenarios. ### `fts_flatcurve_commit_limit` | Key 1 | | --- | | Default | `500` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Commit database changes after this many documents are updated. Higher commit limits will result in faster indexing for large transactions (i.e. indexing a large mailbox) at the expense of high memory usage. The default value should be sufficient to allow indexing in a 256 MB maximum size process. Set to `0` to use the Xapian default. ### `fts_flatcurve_min_term_size` | Key 1 | | --- | | Default | `2` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | The minimum number of characters in a term to index. ### `fts_flatcurve_optimize_density_percentage` | Key 1 | | --- | | Default | `25` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)` | | Advanced Setting; this should not normally be changed. | When an index shard's message count falls below this percentage of `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)`, the mailbox is scheduled for deferred optimization. This helps prevent accumulation of sparse shards that degrade mailbox open performance. Set to `0` to disable density-based optimization. This optimization requires `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)` to be enabled. ### `fts_flatcurve_optimize_limit` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Once the database reaches this number of shards, automatically optimize the DB at shutdown. Set to `0` to disable auto-optimization. ### `fts_flatcurve_rotate_count` | Key 1 | | --- | | Default | `5000` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | When the "current" fts database reaches this number of messages, it is rotated to a read-only database and replaced by a new write DB. Most people should not change this setting. Set to `0` to disable rotation. ### `fts_flatcurve_rotate_time` | Key 1 | | --- | | Default | `5s` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Advanced Setting; this should not normally be changed. | When the "current" fts database exceeds this length of time (in msecs) to commit changes, it is rotated to a read-only database and replaced by a new write DB. Most people should not change this setting. Set to `0` to disable rotation. ### `fts_flatcurve_substring_search` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, allows substring searches ([RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501) compliant). However, this requires significant additional storage space. Many users today expect "Google-like" behavior, which is prefix searching, so substring searching is arguably not the modern expected behavior anyway. Therefore, even though it is not strictly RFC compliant, prefix (non-substring) searching is enabled by default. ## Configuration Example doveconf ``` mail_plugins { fts = yes fts_flatcurve = yes } fts flatcurve { # All of these are optional, and indicate the default values. # They are listed here for documentation purposes; most people should not # need to define/override in their config. commit_limit = 500 min_term_size = 2 optimize_limit = 10 rotate_count = 5000 rotate_time = 5000 substring_search = no } ``` ## Data Storage Xapian search data is stored separately for each mailbox. The data is stored under a 'fts-flatcurve' directory in the Dovecot index file location for the mailbox. The Xapian library is responsible for all data stored in that directory - no Dovecot code directly writes to any file. ## Logging/Events INFO This plugin emits with category `fts-flatcurve`, a child of the category `fts` (see [Events Design](/main/core/admin/events.html)). ### `fts_flatcurve_expunge` Added: 2.4.0 Emitted when a message is expunged from a mailbox. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The UID that was expunged from FTS index | ### `fts_flatcurve_index` Added: 2.4.0 Emitted when a message is indexed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The UID that was added to the FTS index | ### `fts_flatcurve_last_uid` Added: 2.4.0 Emitted when the system queries for the last UID indexed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The last UID contained in the FTS index | ### `fts_flatcurve_optimize` Added: 2.4.0 Emitted when a mailbox is optimized. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | ### `fts_flatcurve_query` Added: 2.4.0 Emitted when a query is completed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `count` | The number of messages matched | | `mailbox` | The mailbox name | | `maybe` | Are the results uncertain? \[yes | no\] | | `query` | The query text sent to Xapian | | `uids` | The list of UIDs returned by the query | ### `fts_flatcurve_rescan` Added: 2.4.0 Emitted when a rescan is completed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `expunged` | The list of UIDs that were expunged during rescan | | `mailbox` | The mailbox name | | `status` | Status of the rescan \[expunge\_msgs | missing\_msgs | ok\] | | `uids` | The list of UIDs that triggered a non-ok status response | ### `fts_flatcurve_rotate` Added: 2.4.0 Emitted when a mailbox has its underlying Xapian DB rotated. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | # Apache Solr FTS Plugin (`fts-solr`) [Solr](https://solr.apache.org/) is a Lucene indexing server. Dovecot communicates to it using HTTP/XML queries. TIP This documentation is written for Solr 7.7.0. You will need to adapt if using a different version. Installation and operation of a Solr system is outside the scope of this documentation. Refer to the Solr documentation for further information. This documentation focuses on Dovecot-specific tasks that need to be done to configure the system for FTS support. ## Compiling Dovecot is not compiled with Solr FTS support by default. To enable it, you need to add the `--with-solr` parameter to your invocation of the `configure` script. You will also need to have libexpat installed, including development headers (typically from a separate development package). Configuration will fail if `--with-solr` is enabled while libexpat headers cannot be found. ## Server Setup ### Solr Installation Using this FTS engine requires Solr to be installed. Most OS distributions have packages for this. To use Solr with Dovecot, it needs to configured specifically for use with Dovecot. sh ``` sudo -u solr /opt/solr/bin/solr create -c dovecot ``` Once the instance is created, you can start the Solr service. By default, the Solr administration page for the newly created instance is located at [https://localhost:8983/solr/#/~cores/dovecot](https://localhost:8983/solr/#/~cores/dovecot). It can be used to check the status of the Solr instance. Configuration errors are often most conveniently viewed here. Solr also writes log files, which can be used for debugging. ### Solr Configuration There are three primary configuration files that need to be changed to accommodate the Dovecot FTS needs: the instance configuration file `solrconfig.xml` and the schema files `schema.xml` and `managed-schema` used by the instance. These files are both located in the `conf` directory of the Solr instance (e.g., `/var/solr/data/dovecot/conf/`). #### Remove default core configuration files sh ``` rm -f /var/solr/data/dovecot/conf/schema.xml rm -f /var/solr/data/dovecot/conf/managed-schema rm -f /var/solr/data/dovecot/conf/solrconfig.xml ``` #### Install schema.xml and solrconfig.xml Copy [`doc/solr-config-7.7.0.xml`](https://raw.githubusercontent.com/dovecot/core/main/doc/solr-config-7.7.0.xml) and [`doc/solr-schema-7.7.0.xml`](https://raw.githubusercontent.com/dovecot/core/main/doc/solr-schema-7.7.0.xml) to `/var/solr/data/dovecot/conf/` as `solrconfig.xml` and `schema.xml`. The `managed-schema` file is generated based on `schema.xml`. ## Dovecot Setup TIP This plugin requires the [fts plugin](/main/core/plugins/fts.html) to be activated and configured ## Dovecot Settings ### `fts_solr_batch_size` **Default:** `1000` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Configures the number of mails sent to Solr in a single request. - With `[fts_autoindex = yes](/main/core/summaries/settings.html#fts_autoindex)` each new mail gets separately indexed on arrival, so `fts_solr_batch_size` only matters during the initial indexing of a mailbox. - With `[fts_autoindex = no](/main/core/summaries/settings.html#fts_autoindex)` new mails don't get indexed on arrival, so `fts_solr_batch_size` is used when indexing is triggered. ### `fts_solr_soft_commit` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [FTS Solr: Soft Commits](/main/core/plugins/fts_solr.html#soft-commits) Controls whether new mails are immediately searchable via Solr. ### `fts_solr_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Required base URL for Solr. INFO Remember to add your core name if using solr 7+: `/solr/dovecot`. Example: ``` fts_solr_url = http://solr.example.org:8983/solr/dovecot/ fts_solr_batch_size = 1000 ``` ## Example Configuration doveconf ``` mail_plugins { fts = yes fts_solr = yes } fts solr { url = url=https://solr.example.org:8983/solr/ batch_size = 1000 } ``` Important notes: - Some mail clients will not submit any search requests for certain fields if they index things locally eg. Thunderbird will not send any requests for fields such as sender/recipients/subject when Body is not included as this data is contained within the local index. ## Solr Commits & Optimization Solr indexes should be optimized once in a while to make searches faster and to remove space used by deleted mails. Dovecot never asks Solr to optimize, so you should do this yourself. Perhaps a cronjob that sends the optimize-command to Solr every n hours. Dovecot only does soft commits to the Solr index to improve performance. You must run a hard commit once in a while or Solr will keep increasing its transaction log sizes. For example send the commit command to Solr every few minutes. sh ``` Optimize should be run somewhat rarely, e.g. once a day curl https://:/solr/dovecot/update?optimize=true # Commit should be run pretty often, e.g. every minute curl https://:/solr/dovecot/update?commit=true ``` You may not need those if you are using a recent Solr (7+) or SolrCloud. The default configuration of Solr is to auto-commit every once in a while (~15sec) so commit is not necessary. Also, the default TieredMergePolicy in Solr will automatically purge removed documents later, so optimize is not necessary. ### Soft Commits If soft commits are enabled, dovecot will perform a soft commit to Solr at the end of transaction. This has the benefit that search results are immediately available. You can also enable automatic commits in SOLR config, with xml ``` 60000 ``` or setting it in `solrconfig.xml` with xml ``` ${solr.autoSoftCommit.maxTime:60000} ``` ## Re-index Mailbox If you require Dovecot to reindex a whole mailbox, you can run `[doveadm fts rescan]()`. This command will only take action when a search is done and will apply to the whole mailbox. If you want to index a single mailbox/all mailboxes you can run `[doveadm index](/main/core/summaries/doveadm.html#index)`.This command will happen immediately and will block until the indexing is completed. ## Sorting by Relevancy Solr/Lucene supports returning a relevancy score for search results. If you want to sort the search results by the score, use Dovecot's non-standard X-SCORE sort key: ``` 1 SORT (X-SCORE) UTF-8 ``` ## Solr Indexes Dovecot creates the following fields: - id: Unique ID consisting of uid/uidv/user/box. - Note that your user names really shouldn't contain '/' character. - uid: Message's IMAP UID. - uidv: Mailbox's UIDVALIDITY. This changes if mailbox gets recreated. - box: Mailbox name - user: User name who owns the mailbox, or empty for public namespaces - hdr: Indexed message headers - body: Indexed message body - any: "Copy field" from hdr and body, i.e. searching based on this will search from both headers and bodies. Lucene/Solr does duplicate suppression based on the "id" field, so even if Dovecot sends the same message multiple times to Solr it gets indexed only once. This might happen currently if multiple searches are started at the same time. You might want to build a cronjob to go through the Solr/Lucene indexes once in a while to delete indexed messages (or entire mailboxes) that no longer exist on the filesystem. It shouldn't normally find any such messages though. ## Testing ``` # telnet localhost imap * OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS STARTTLS AUTH=PLAIN AUTH=LOGIN] I am ready. 1 login username password 2 select Inbox 3 SEARCH text "test" ``` ## Sharding If you have more users than fit into a single Solr box, you can split users off to different servers. A couple of different ways you could do it are: - Have some HTTP proxy redirecting the connections based on the URL - Configure Dovecot's userdb lookup to return a different host for `[fts_solr_url](/main/core/summaries/settings.html#fts_solr_url)` setting using [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). - MySQL: `query = SELECT concat('https://', solr_host, ':8983/solr/dovecot/') AS fts_solr_url, ...` ``` ... concat('https://', solr_host, ':8983/solr/dovecot/') AS fts_solr_url, ... ``` - LDAP: dovecot.conf doveconf ``` userdb ldap { ... fields { fts_solr_url = https://%{ldap:solrHost}:8983/solr/dovecot/ } } ``` You can also use [SolrCloud](https://lucene.apache.org/solr/guide/7_6/solrcloud.html), the clustered version of Solr, that allows you to scale up, and adds failover / high availability to your FTS system. Dovecot-solr works fine with a SolrCloud cluster as long as the solr schema is the right one. ## External Tutorials External sites with tutorials on using Solr under Dovecot - [FreeBSD](https://mor-pah.net/2016/08/15/dovecot-2-2-with-solr-6-or-5/) - [Substring searches with ngrams](https://dovecot.org/list/dovecot/2011-May/059338.html) ## Tips Some additional things which might help you configuring Solr search: - If you are using Tomcat: Set `maxHttpHeaderSize="65536"` (connector definition for port 8080 in `/etc/tomcat7/server.xml`) to accept long search query strings (iPhones tend to send multi-kilobyte-sized queries) - Set `df` to `hdr` in `/etc/solr/conf/solrconfig.xml` (`/select` request handler) to avoid strange `undefined field text` errors. - Please keep in mind that you will have to change the Solr URL to include the core name (ie: `dovecot`: `https://localhost:8939/solr/dovecot`). - To troubleshoot the settings you can enable debug logs using: doveconf ``` log_debug = category=fts-solr ``` and rawlogs using: doveconf ``` fts_solr { http_client_rawlog_dir = /path/to/writable/directory/solr-rawlogs } ``` # FTS: Full Text Search Plugin (`fts`) As the amount and importance of information stored in email messages is increasing in people’s everyday lives, searching through those messages is becoming ever more important. At the same time mobile clients add their own restrictions for what can be done on the client side. The ever diversifying mail client software also tests the limits of the IMAP protocol and current server implementations. Furthermore, the IMAP protocol requires some rather complicated and expensive searching capabilities. For example, the protocol requires arbitrary substring matching. Some newer mobile clients (e.g. Apple iOS) rely on this functionality. Without a high-performance index, Dovecot must fall back to a slow sequential search through all messages (default behavior). If storage latencies are high, this searching may not be completed in a reasonable time, or resource utilization may be too large, especially in mailboxes with large messages. ## Dovecot CE FTS Drivers Dovecot CE contains these FTS indexing drivers: | Name | Description | | --- | --- | | [fts\_solr plugin](/main/core/plugins/fts_solr.html) | Interface to [Apache Solr](https://solr.apache.org/); stores data remotely. | | [fts\_flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | [Xapian](https://xapian.org/) based driver; stores data locally. | There are also 3rd party provided FTS plugins: | Name | Description | | --- | --- | | fts-xapian | [https://github.com/grosjo/fts-xapian](https://github.com/grosjo/fts-xapian) | If you want your plugin here, please open pull request to [dovecot/documentation](https://github.com/dovecot/documentation/pulls/new) repository. ## Searching In Dovecot When a FTS indexing driver is not present, searches use a slow sequential search through all message data. This is both computationally and time expensive. It is desirable to pre-index data so that searches can be executed against this index. There is a subtle but important distinction between searching through message headers and searching through message bodies. Searching through message bodies (via the standard IMAP 'SEARCH TEXT/BODY' commands) makes use of the FTS indexes. On the other hand, searching through message headers benefits from Dovecot's standard index and cache files (`dovecot.index` and `dovecot.index.cache`), which often contain the necessary information. It is possible to redirect header searches to FTS indexes via a configuration option (`[fts_search_add_missing](/main/core/summaries/settings.html#fts_search_add_missing)`). Triggers for FTS indexing are configurable. It can be started on demand when searching, or automatically when new messages arrive or as a batch job. By default the FTS indexes are updated only while searching, so neither LDA/LMTP nor an IMAP 'APPEND' command updates the indexes immediately. This means that if a user has received a lot of mail since the last indexing (i.e., the last search operation), it may take a while to index all the new mails before replying to the search command. Dovecot sends periodic "\* OK Indexed n% of the mailbox" updates which can be caught by client implementations to implement a progress bar. Updating the FTS index as messages arrive makes for a more responsive user experience, especially for users who don’t search often, but have a lot of mail. On the other hand, it increases overall system load regardless of whether or not the indexes will ever be used by the user. ## Dovecot FTS Architecture Dovecot splits the full text search functionality into two parts: a common tokenization library (lib-language) and driver indexing engine responsible for storing the tokens produced by the common library persistently. Some of the FTS drivers do their own internal tokenization, although it's possible to configure them to use the lib-language tokenization as well. See [Tokenization](#tokenization) for more details about configuring the tokenization. All drivers are implemented as plugins that extend the base fts plugin's functionality. ## Settings ### `fts` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) Configures the used fts driver to perform [fts plugin](/main/core/plugins/fts.html) indexing. If not specified, FTS is disabled. The filter name refers to the `[fts_driver](/main/core/summaries/settings.html#fts_driver)` setting. Example: ``` fts solr { # ... } ``` ### `fts_autoindex` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[fts_autoindex_max_recent_msgs](/main/core/summaries/settings.html#fts_autoindex_max_recent_msgs)` If enabled, index mail as it is delivered or appended. It can be overridden at the mailbox level, e.g. you can disable autoindexing for selected mailboxes using this setting: Example: ``` fts_autoindex = yes # ... mailbox trash { special_use = Trash fts_autoindex = no } mailbox spam { special_use = Junk fts_autoindex = no } mailbox storage/* { fts_autoindex = no } ``` ### `fts_autoindex_max_recent_msgs` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[fts_autoindex](/main/core/summaries/settings.html#fts_autoindex)` To exclude infrequently accessed mailboxes from automatic indexing, set this value to the maximum number of `Recent` flagged messages that exist in the mailbox. A value of `0` means to ignore this setting. Mailboxes with more flagged `Recent` messages than this value will not be autoindexed, even though they get deliveries or appends. This is useful for, e.g., inactive Junk folders. Any folders excluded from automatic indexing will still be indexed, if a search on them is performed. Example: ``` fts_autoindex_max_recent_msgs = 999 ``` ### `fts_decoder_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** ```script``tika` Optional setting. If set, decode attachments to plaintext using the selected service and index the resulting plaintext. ### `fts_decoder_script_socket_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Changed: 2.4.0 Renamed from `fts_decoder`. Name of the script service used to decode the attachments. We strongly recommend using `[fts_decoder_driver = tika](/main/core/summaries/settings.html#fts_decoder_driver)` instead. Example: ``` fts_decoder_driver = script fts_decoder_script_socket_path = fts-decoder service fts-decoder { executable = script /path/to/fts-decoder.sh user = vmail unix_listener fts-decoder { mode = 0666 } } ``` ### `fts_decoder_tika` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[fts_decoder_tika_url](/main/core/summaries/settings.html#fts_decoder_tika_url)` Named filter used for [Apache Tika](https://tika.apache.org/) HTTP lookups. For example: ``` fts_decoder_tika { http_client_request_timeout = 10s } ``` ### `fts_decoder_tika_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fts_decoder_tika](/main/core/summaries/settings.html#fts_decoder_tika)` **Changes:** - Added: 2.4.0 Basic authentication support (via URL) is added. - Changed: 2.4.0 Renamed from `fts_tika`. URL for [Apache Tika](https://tika.apache.org/) decoder for attachments. Example: ``` fts_decoder_driver = tika fts_decoder_tika_url = http://tikahost:9998/tika/ ``` ### `fts_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `dovecot``solr``flatcurve` Configures the used fts driver to perform [fts plugin](/main/core/plugins/fts.html) indexing. The `[fts](/main/core/summaries/settings.html#fts)` filter name refers to this setting. ### `fts_header_excludes` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The list of headers to include or exclude. - The default is the preexisting behavior, i.e. index all headers. - `includes` take precedence over `excludes`: if a header matches both, it is indexed. - The terms are case insensitive. - An asterisk `*` at the end of a header name matches anything starting with that header name. - The asterisk can only be used at the end of the header name. Prefix and infix usage of asterisk are not supported. Example: ``` fts_header_excludes { Received = yes DKIM-* = yes X-* = yes Comments = yes } fts_header_includes { X-Spam-Status = yes Comments = yes } ``` - `Received` headers, all `DKIM-` headers and all `X-` experimental headers are excluded, with the following exceptions: - `Comments` and `X-Spam-Status` are indexed anyway, as they match **both** `excludes` and `includes` lists. - All other headers are indexed. Example: ``` fts_header_excludes { * = yes } fts_header_includes { From = yes To = yes Cc = yes Bcc = yes Subject = yes Message-ID = yes In-* = yes X-CustomApp-* = yes } ``` - No headers are indexed, except those specified in the `includes`. ### `fts_header_includes` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[fts_header_excludes](/main/core/summaries/settings.html#fts_header_excludes)` ### `fts_message_max_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **Changes:** - Added: 2.4.0 Maximum body size that is processed by fts. ### `fts_search_add_missing` **Default:** `body-search-only` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `body-search-only``yes` Should missing mails be added to FTS indexes before search? With `body-search-only` this is done only when the search query requests searching message bodies, i.e. header searches are not updating the FTS index. The unindexed mails are searched without FTS, i.e. either getting the headers from `dovecot.index.cache` or by opening the emails if the headers aren't in cache. This may be a useful optimization if the user's client only uses header searches. INFO Only the `yes` option guarantees consistent search results. Otherwise it's possible that the search results will be different depending on whether the search was performed via FTS index or not. ### `fts_search_read_fallback` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) If FTS lookup or indexing fails, fall back to searching without FTS (i.e. possibly opening all emails). This may timeout for large mailboxes and/or slow storage. ### `fts_search_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) When the full text search driver detects that the index isn't up-to-date, the indexer is told to index the messages and is given this much time to do so. If this time limit is reached, an error is returned, indicating that the search timed out during waiting for the indexing to complete: `NO [INUSE] Timeout while waiting for indexing to finish`. A value of `0` means no timeout. ### `language` **Default:** `` **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[language_default](/main/core/summaries/settings.html#language_default)` **See Also:** - `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)` - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Defines a language to be used in tokenization. At least one language must be specified and one single language must be flagged as the default language using `[language_default = yes](/main/core/summaries/settings.html#language_default)`. The language listed first is the default and is used when language recognition fails. The filters used for stemming and stopwords are language dependent. TIP For better performance it's recommended to synchronize this setting with the textcat configuration file; see `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)`. Example: ``` language en { default = yes } language de { } ``` ### `language_default` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Dependencies:** - `[language](/main/core/summaries/settings.html#language)` **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) The language marked as default will be used when language detection cannot identify the proper language of the text being processed. Exactly one language must be marked with this flag. ### `language_filter_normalizer_icu_id` **Default:** `Any-Lower; NFKD; [: Nonspacing Mark :] Remove; [\x20] Remove` **Value:** [string](/main/core/settings/types.html#string) Description of the normalizing/transliterating rules to use. See [Normalizer Format](https://unicode-org.github.io/icu/userguide/transforms/general/#transliterator-identifiers) for syntax. ### `language_filter_stopwords_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Path to the directory containing stopword files. The files inside the directory have names with the form `/stopwords_.txt`. See [Languages](/main/core/plugins/fts.html#languages) for the list of stopword files that are currently distributed with Dovecot. More languages can be obtained from: - [Apache Lucene](https://lucene.apache.org/), - [Snowball stemmer](https://snowballstem.org/), or - [GitHub - Stopwords ISO](https://github.com/stopwords-iso/). ### `language_filters` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) - [Filter Configuration](/main/core/plugins/fts.html#filter-configuration) The list of filters to apply. See [Filter Configuration](/main/core/plugins/fts.html#filter-configuration) for configuration information. ### `language_tokenizer_address_token_maxlen` **Default:** `250` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizer_generic_algorithm` **Default:** `simple` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `simple``tr29` **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Defines the method for finding word boundaries. | Value | Description | | --- | --- | | `simple` | Faster algorithm that works for many texts, especially using the latin alphabets, but leaves corner cases. | | `tr29` | Implements a version of Unicode technical report 29 word boundary lookup. It might work better with texts containing e.g. Katakana or Hebrew characters, but it is not possible to use a single algorithm for all existing languages. | ### `language_tokenizer_generic_token_maxlen` **Default:** `30` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizer_generic_wb5a` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Unicode TR29 rule WB5a setting to the tr29 tokenizer. Splits prefixing contracted words from base word. E.g. `l'homme` -> `l` and `homme`. Together with a language specific stopword list unnecessary contractions can thus be filtered away. This is disabled by default and only works with the TR29 algorithm. Enable by declaring: - `[language_tokenizer_generic_algorithm = tr29](/main/core/summaries/settings.html#language_tokenizer_generic_algorithm)` and - `[language_tokenizer_generic_wb5a = yes](/main/core/summaries/settings.html#language_tokenizer_generic_wb5a)`. ### `language_tokenizer_kuromoji_icu_id` **Default:** `Any-NFKC` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Description of the normalizing/transliterating rules to use. See [Normalizer Format](https://unicode-org.github.io/icu/userguide/transforms/general/#transliterator-identifiers) for syntax. Defaults to `Any-NFKC` which is quite good for CJK text mixed with latin alphabet languages. It transforms CJK characters to full-width encoding and transforms latin ones to half-width. The NFKC transformation is described above. WARNING If this setting is changed, existing FTS indexes will produce unexpected results. The FTS indexes should be recreated in this case. ### `language_tokenizer_kuromoji_split_compounds` | Key 1 | | --- | | Default | `yes` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) | | Advanced Setting; this should not normally be changed. | This setting enables `search mode` in the Atilika Kuromoji library. The setting defaults to enabled and should not be changed unless there is a compelling reason. WARNING If this setting is changed, existing FTS indexes will produce unexpected results. The FTS indexes should be recreated in this case. ### `language_tokenizer_kuromoji_token_maxlen` **Default:** `1024` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizers` **Default:** `generic email-address` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) - [Tokenizer Configuration](/main/core/plugins/fts.html#tokenizer-configuration) The list of tokenizers to use. See [Tokenizer Configuration](/main/core/plugins/fts.html#tokenizer-configuration) for configuration information. ### `textcat_config_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[language](/main/core/summaries/settings.html#language)` Path to the textcat/exttextcat configuration file, which lists the supported languages. This is recommended to be changed to point to a minimal version of a configuration that supports only the languages listed in `[language](/main/core/summaries/settings.html#language)`. Doing this improves language detection performance during indexing and also makes the detection more accurate. Example: ``` textcat_config_path = /usr/share/libexttextcat/fpdb.conf ``` ## Configuration ### FTS Indexing Triggers Missing mails are always added to FTS indexes when using IMAP SEARCH command that attempts to access the FTS indexes. Automatic FTS indexing can also be done during mail delivery, IMAP APPEND and other ways of adding mails to mailboxes using `[fts_autoindex](/main/core/summaries/settings.html#fts_autoindex)`. Indexing can also be triggered manually: sh ``` doveadm index -u user@domain -q INBOX ``` ### Enforce FTS When FTS indexing fails, Dovecot falls back on using the built-in search, which does not have indexes for mail bodies. This could end up opening all the mails in the mailbox, which often isn't wanted. To disable this functionality, enable `[fts_search_add_missing](/main/core/summaries/settings.html#fts_search_add_missing)`. ## Indexing Attachments Attachments can be indexed either via a script that translates the attachment to UTF-8 plaintext or [Apache Tika](https://tika.apache.org/) server. - `[fts_decoder_driver](/main/core/summaries/settings.html#fts_decoder_driver)` - `[fts_decoder_script_socket_path](/main/core/summaries/settings.html#fts_decoder_script_socket_path)` - `[fts_decoder_tika_url](/main/core/summaries/settings.html#fts_decoder_tika_url)` ## Rescan Dovecot keeps track of indexed messages in the `dovecot.index files`. If this becomes out of sync with the actual FTS indexes (either too many or too few mails), you'll need to do a rescan and then index missing mails: sh ``` doveadm fts rescan -u user@domain doveadm index -u user@domain -q '*' ``` Note that currently most FTS drivers don't implement the rescan. Instead, they simply delete all the FTS indexes. This may change in the future versions. ## Languages Language names are given as ISO 639-1 alpha 2 codes. Stemming support indicates whether the `snowball` filter can be used. Stopwords support indicates whether a stopwords file is distributed with Dovecot. Currently supported languages: | Language Code | Language | Stemming | Stopwords | | --- | --- | --- | --- | | da | Danish | Yes | Yes | | de | German | Yes | Yes | | en | English | Yes | Yes | | es | Spanish | Yes | Yes | | fi | Finnish | Yes | Yes | | fr | French | Yes | Yes | | it | Italian | Yes | Yes | | ja | Japanese (Requires Dovecot Pro) | No | No | | nl | Dutch | Yes | Yes | | no | Norwegian (Bokmal & Nynorsk detected) | Yes | Yes | | pt | Portuguese | Yes | Yes | | ro | Romanian | Yes | Yes | | ru | Russian | Yes | Yes | | sv | Swedish | Yes | Yes | | tr | Turkish | Yes | Yes | ## Tokenization Dovecot contains tokenization support that can be used by FTS drivers. The lib-language tokenization library works in the following way: 1. Language detection: When indexing, the text language is attempted to be detected. If the detection fails, the first listed language is used. When searching, the search is done using all the configured languages. 2. Tokenization: The text is split to tokens (individual words). - Whitespace and other nonindexable characters are dropped. - Base64 sequences are looked for and skipped. 3. Filtering: Tokens are normalized: - Normalization / lowercasing - Stemming 4. Stopwords: A configurable list of words not to be indexed ### Language Definition The `[language](/main/core/summaries/settings.html#language)` setting declares the languages that need to be detected. At least one language must be listed. The first language is the default language used in case detection fails. Each added language makes the indexing and searching slightly slower, so it's recommended not to add too many languages unnecessarily. The language detection performance can be improved by limiting the number of languages available for textcat, see `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)`. Example: doveconf ``` language en { default = yes } language de { } ``` ### Filter and Tokenizer Order The filters and tokenizers are created in the order they are declared in their respective settings in the configuration file. They form a chain, where the first filter or tokenizer is the parent or grandparent of the rest. The direction of the data flow needs some special attention. In filters, the data flows from parent to child, so tokens are first passed to the grandparent of all filters and then further down the chain. For some filtering chains the order is important. E.g. the snowball stemmer wants all input in lower case, so the filter lower casing the tokens will need to be listed before it. In tokenizers however, the data however flows from child to parent. This means that the tokenizer listed 'last' gets the processed data 'first'. So, for filters data flows "left to right" through the filters listed in the configuration. In tokenizers the order is "right to left". ### Base64 Detection Base64 sequences are looked for in the tokenization buffer and skipped when detected. A base64 sequence is detected by: - An optional leader character comprised in `leader-characters` set, - A run of characters, all comprised in the `base64-characters` set, at least `minimum-run-length` long, - An end-of-buffer, or a trailer character comprised in `trailer-characters` set, where: - `leader-characters` are: `[ \t\r\n=:;?]` - `base64-characters` are: `[0-9A-Za-z/+]` - `trailer-characters` are: `[ \t\r\n=:;?]` - `minimum-run-length` is: `50` - `minimum-run-count` is: `1` Thus, (even single) 50-chars runs of characters in the base64 set are recognized as base64 and ignored in indexing. If a base64 sequence happens to be split across different chunks of data, part of it might not be detected as base64. In this case, the undetected base64 fragment is still indexed. However, this happens rarely enough that it does not significantly impact the quality of the filter. So far the above rule seems to give good results in base64 indexing avoidance. It also performs well in removing base64 fragments inside headers, like ARC-Seal, DKIM-Signature, X-SG-EID, X-SG-ID, including header-encoded parts (e.g. `=?us-ascii?Q?...?=` sequences). ## Filter Configuration Filters affect how data is indexed. They are configured through `[language_filters](/main/core/summaries/settings.html#language_filters)`. Example: doveconf ``` language_filters = normalizer-icu snowball stopwords language en { language_filters = lowercase snowball english-possessive stopwords } ``` Available filters: ### `lowercase` Change all text to lower case. Supports UTF8, when compiled with libicu and the library is installed. Otherwise only ASCII characters are lowercased. ### `stopwords` Filter certain common and short words, which are usually useless for searching. WARNING Using stopwords with multiple languages configured WILL cause some searches to fail. The recommended solution is to NOT use the stopword filter when multiple languages are present in the configuration. #### Settings ### `snowball` Stemming tries to convert words to a common base form. A simple example is converting `cars` to `car` (in English). This stemmer is based on the [Snowball stemmer](https://snowballstem.org/) library. ### `normalizer-icu` Normalize text using libicu. This is potentially very resource intensive. WARNING There is a caveat for the Norwegian language: The default normalizer filter does not modify `U+00F8` (Latin Small Letter O with Stroke). In some configurations it might be desirable to rewrite it to, e.g., `o`. Same goes for the upper case version. This can be done by passing a modified `id` setting to the normalizer filter. Similar cases can exist for other languages as well. #### Settings ##### `language_filter_normalizer_icu_id` **Default:** `Any-Lower; NFKD; [: Nonspacing Mark :] Remove; [\x20] Remove` **Value:** [string](/main/core/settings/types.html#string) Description of the normalizing/transliterating rules to use. See [Normalizer Format](https://unicode-org.github.io/icu/userguide/transforms/general/#transliterator-identifiers) for syntax. ### `english-possessive` Remove trailing `'s` from English possessive form tokens. Any trailing single `'` characters are already removed by tokenizing, whether this filter is used or not. The `snowball` filter also removes possessive suffixes from English, so if using `snowball` this filter is not needed. TIP `snowball` likely produces better results, so this filter is advisable only when `snowball` is not available or cannot be used due to extreme CPU performance requirements. ### `contractions` Removes certain contractions that can prefix words. The idea is to only index the part of the token that conveys the core meaning. Only works with French, so the language of the input needs to be recognized by textcat as French. It filters `qu'`, `c'`, `d'`, `l'`, `m'`, `n'`, `s'` and `t'`. Do not use at the same time as `generic` tokenizer with both - `[language_tokenizer_generic_algorithm = tr29](/main/core/summaries/settings.html#language_tokenizer_generic_algorithm)` and - `[language_tokenizer_generic_wb5a = yes](/main/core/summaries/settings.html#language_tokenizer_generic_wb5a)`. ## Tokenizer Configuration Tokenizers affect how input data is parsed. Available tokenizers: ### `generic` Input data, such as email text and headers, need to be divided into words suitable for indexing and searching. The generic tokenizer does this. #### Settings ##### `language_tokenizer_generic_algorithm` **Default:** `simple` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `simple``tr29` **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Defines the method for finding word boundaries. | Value | Description | | --- | --- | | `simple` | Faster algorithm that works for many texts, especially using the latin alphabets, but leaves corner cases. | | `tr29` | Implements a version of Unicode technical report 29 word boundary lookup. It might work better with texts containing e.g. Katakana or Hebrew characters, but it is not possible to use a single algorithm for all existing languages. | ##### `language_tokenizer_generic_token_maxlen` **Default:** `30` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Maximum length of token, before an arbitrary cut off is made. ##### `language_tokenizer_generic_wb5a` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Unicode TR29 rule WB5a setting to the tr29 tokenizer. Splits prefixing contracted words from base word. E.g. `l'homme` -> `l` and `homme`. Together with a language specific stopword list unnecessary contractions can thus be filtered away. This is disabled by default and only works with the TR29 algorithm. Enable by declaring: - `[language_tokenizer_generic_algorithm = tr29](/main/core/summaries/settings.html#language_tokenizer_generic_algorithm)` and - `[language_tokenizer_generic_wb5a = yes](/main/core/summaries/settings.html#language_tokenizer_generic_wb5a)`. ### `email-address` This tokenizer preserves email addresses as complete search tokens, by bypassing the generic tokenizer, when it finds an address. It will only work as intended if it is listed **after** other tokenizers. #### Settings ##### `language_tokenizer_address_token_maxlen` **Default:** `250` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) Maximum length of token, before an arbitrary cut off is made. # IMAP ACL Plugin (`imap-acl`) This plugin implements the IMAP ACL ([RFC 4314](https://datatracker.ietf.org/doc/html/rfc4314)) extension. ## Settings ### `imap_acl_allow_anyone` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) By default Dovecot doesn't allow using the IMAP `anyone` or `authenticated` identifier, because it would be an easy way to spam other users in the system. If [mail-crypt plugin](/main/core/plugins/mail_crypt.html) is used, users who have different set of encryption keys cannot share mails, but sharing is possible within the scope of a key. ## Configuration Configuration and further details can be found on the [acl plugin](/main/core/plugins/acl.html) page. # IMAP FILTER=SIEVE Plugin (`imap-filter-sieve`) Normally, Sieve filters can either be applied at initial mail delivery or triggered by certain events in the Internet Message Access Protocol (IMAPSIEVE; [RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)). The user can configure which Sieve scripts to run at these instances, but it is not possible to trigger the execution of Sieve scripts manually. However, this could be very useful; e.g, to test new Sieve rules and to re-filter messages that were erroneously handled by an earlier version of the Sieve scripts involved. Pigeonhole provides the `imap_filter_sieve` plugin, which provides a vendor-defined IMAP extension called `FILTER=SIEVE`. This adds a new `FILTER` command that allows applying a mail filter (a Sieve script) on a set of messages that match the specified IMAP searching criteria. This plugin implements the latest draft of the [FILTER=SIEVE Sieve Extension](https://github.com/dovecot/pigeonhole/blob/master/doc/rfc/draft-bosch-imap-filter-sieve-00.txt). This plugin is experimental and the specification is likely to change. Use the specification included in your current release to obtain the matching specification for your release. The plugin is included in the Pigeonhole package and are therefore implicitly compiled and installed with Pigeonhole itself. ## Settings There are no `dovecot.conf` settings for this plugin. ## Configuration The IMAP FILTER Sieve plugin is activated by adding it to the `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` setting for the imap protocol: doveconf ``` protocol imap { mail_plugins { imap_filter_sieve = yes } } ``` Note that enabling this plugin allows users to specify the Sieve script content as a parameter to the `FILTER` command, not just run existing stored scripts. This plugin uses the normal configuration settings used by the [LDA](/main/core/config/delivery/lda.html) Sieve plugin at delivery. The [before](/main/core/plugins/sieve.html#script-storage-type-before), [after](/main/core/plugins/sieve.html#script-storage-type-after) and [discard](/main/core/plugins/sieve.html#script-storage-type-discard) Sieve script storage types are currently ignored by this plugin. # IMAP Quota Plugin (`imap-quota`) This plugin implements the IMAP command for requesting current quota information. It requires that the [quota plugin](/main/core/plugins/quota.html) be activated and configured in Dovecot. It implements the IMAP commands defined in [RFC 2087](https://datatracker.ietf.org/doc/html/rfc2087) ## Settings There are no `dovecot.conf` settings for this plugin. # IMAPSieve Plugin (`imap-sieve`) As defined in the base specification ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)), the Sieve language is used only during delivery. However, in principle, it can be used at any point in the processing of an email message. [RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785) defines the use of Sieve filtering in IMAP, operating when messages are created or their attributes are changed. This feature extends both Sieve and IMAP. Therefore, Pigeonhole provides both an IMAP plugin and a Sieve plugin. The `sieve_imapsieve` plugin implements the `imapsieve` extension for the Sieve filtering language, adding functionality for using Sieve scripts from within IMAP. The `imap_sieve` plugin for IMAP adds the `IMAPSIEVE` capability to the `imap` service. The basic `IMAPSIEVE` capability allows attaching a Sieve script to a mailbox for any mailbox by setting a special IMAP METADATA entry. This way, users can configure Sieve scripts that are run for IMAP events in their mailboxes. Beyond the standard, the Pigeonhole implementation also adds the ability for administrators to configure Sieve scripts outside the user's control, that are run either before or after a user's script if there is one. WARNING The `imapsieve` extension can only be used in a Sieve script that is invoked from IMAP. When it is used in the active delivery script, it will cause runtime errors. To make a Sieve script suitable for both delivery and IMAP, the availability of the extension can be tested using the `ihave` test ([RFC 5463](https://datatracker.ietf.org/doc/html/rfc5463)) as usual. ## Configuration ### imap-sieve The IMAP plugin is activated by adding it to the `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` setting for the imap protocol: doveconf ``` protocol imap { mail_plugins { imap_sieve = yes } } ``` This will only enable support for administrator scripts. User scripts are only supported when a Sieve URL is additionally configured using the `[imapsieve_url](/main/core/summaries/settings.html#imapsieve_url)` setting. This URL points to the [ManageSieve](/main/core/config/sieve/managesieve.html) server that users need to use to upload their Sieve scripts. This URL will be shown to the client in the IMAP CAPABILITY response as `IMAPSIEVE=`. User scripts are retrieved from the user's [personal](/main/core/plugins/sieve.html#script-storage-type-personal) storage by name ([RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)), which requires no additional configuration. Script storages for administrator scripts are defined in `[sieve_script](/main/core/summaries/settings.html#sieve_script)` blocks with `[sieve_script_type](/main/core/summaries/settings.html#sieve_script_type)`[before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after). These execute administrator scripts before or after the user's personal script, respectively. The `[sieve_script_cause](/main/core/summaries/settings.html#sieve_script_cause)` setting for the administrator storages used by the `imap_sieve` plugin must include the cause of the IMAP event ([RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)): either `append`, `copy`, or `flag`. The `[sieve_script_cause](/main/core/summaries/settings.html#sieve_script_cause)` setting can list several causes together, including the default `delivery`, which in that case means that those administrator scripts are also executed at delivery. The applicability of administrator scripts can be limited to a destination mailbox by placing the corresponding `[sieve_script](/main/core/summaries/settings.html#sieve_script)` blocks inside a `[mailbox](/main/core/summaries/settings.html#mailbox)` block for that mailbox. For a source mailbox, limiting the applicability of administrator scripts can similarly be achieved by placing the corresponding `[sieve_script](/main/core/summaries/settings.html#sieve_script)` blocks inside a `[imapsieve_from](/main/core/summaries/settings.html#imapsieve_from)` block with that mailbox name. The `[mailbox](/main/core/summaries/settings.html#mailbox)` and `[imapsieve_from](/main/core/summaries/settings.html#imapsieve_from)` blocks can be nested when both are required. The `imap_sieve` plugin defines an additional [script storage type](/main/core/plugins/sieve.html#script-storage-types) called `copy-source-after`. Administrator scripts in such storages only apply when the cause is `copy` and are executed for the message in the source mailbox after the Sieve scripts for the corresponding message in the destination mailbox successfully finish executing. This does not apply to moved messages, since the message is removed from the source mailbox in that case. ### sieve-imapsieve The Sieve plugin is activated by adding it to the `[sieve_plugins](/main/core/summaries/settings.html#sieve_plugins)` setting: doveconf ``` sieve_plugins { sieve_imapsieve = yes } ``` This plugin registers the `imapsieve` extension with the Sieve interpreter. This extension is enabled implicitly, which means that it does not need to be added to the `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting. ## Example Configuration doveconf ``` imapsieve_from Spam { sieve_script ham { type = before cause = copy path = /etc/dovecot/sieve/ham.sieve } } mailbox Spam { sieve_script spam { type = before cause = copy path = /etc/dovecot/sieve/spam.sieve } } ``` ## Settings ### imap-sieve #### `imapsieve_expunge_discarded` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) This setting determines whether de IMAPSieve plugin implicitly also expunges messages that were discarded by the executed Sieve script sequence; i.e., Sieve yielded no (implicit) keep. #### `imapsieve_from` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) Only execute the administrator Sieve scripts for the mailbox configured with `[sieve_script](/main/core/summaries/settings.html#sieve_script)` and type [before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after) when the message originates from the indicated mailbox. The filter name refers to the `[imapsieve_from_name](/main/core/summaries/settings.html#imapsieve_from_name)` setting. Therefore, the contained `[sieve_script](/main/core/summaries/settings.html#sieve_script)` blocks only apply when the source mailbox of the IMAP action match this filter. #### `imapsieve_from_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) The name of the source mailbox for IMAPSieve [before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after) scripts. The `[imapsieve_from](/main/core/summaries/settings.html#imapsieve_from)` filter refers to this setting. #### `imapsieve_url` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) If set, support for user Sieve scripts in IMAP is enabled. The value is an URL pointing to the ManageSieve server that users must use to upload their Sieve scripts. Leave this setting empty if you don't want users to have the ability to associate Sieve scripts with mailboxes. This has no effect on the administrator-controlled Sieve scripts. ``` imapsieve_url = sieve://sieve.example.com ``` #### `sieve_script_cause` **Default:** `delivery` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The causes for executing Sieve scripts from this storage. This is currently only relevant for the IMAPSieve plugin. For standard Sieve execution at message delivery the cause is "delivery". Other causes can be `append`, `copy` and `flag`. ### sieve-imapsieve There are no `dovecot.conf` settings for this plugin. # Last Login Plugin (`last-login`) This plugin can be used to update user's last-login timestamp in a configured dictionary. Last login information is useful for troubleshooting scenarios, which usually starts with end user contacting customer care agent that the mailbox is unreachable or empty. Amongst the first things is to check when the customer last successfully did login to the mailbox and using which protocol, as this might indicate that there might be some device with POP3 configured thus emptying the mailbox. Last login feature is designed for this use case, to allow easy way to search per any account the timestamp of last login or last mail delivery to the mailbox. Last login information is also useful in determining system usage statistics. ## Settings ### `last_login` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) Named filter for initializing dictionary used to store last login information. Example: ``` redis_host = 127.0.0.1 redis_port = 6379 last_login { dict redis { } } ``` ### `last_login_dict_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **Changes:** - Added: 2.4.5 List of additional key/value pairs to be updated in the dictionary. Both keys and values support [variable expansion](/main/core/settings/variables.html). ### `last_login_key` **Default:** `last-login/%{user}` **Value:** [string](/main/core/settings/types.html#string) The key that is updated in the dictionary with the last login timestamp. ### `last_login_precision` **Default:** `s` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `s``ms``us``ns` Precision for last login timestamp. ## Example Configuration doveconf ``` protocol imap { mail_plugins { last_login = yes } } protocol pop3 { mail_plugins { last_login = yes } } redis_host = 127.0.0.1 redis_port = 6379 last_login { dict redis { } #key = last-login/%{user} # default } ``` In this example, last\_login plugin is explicitly enabled only for imap & pop3 protocols. If enabled globally, it'll also update the timestamp whenever new mails are delivered via lda/lmtp or when doveadm is run for the user. This can also be thought of as a feature, so if you want to update a different timestamp for user when new mails are delivered, you can do that by enabling the last\_login plugin also for lda/lmtp and changing the `[last_login_key](/main/core/summaries/settings.html#last_login_key)` setting to include `%{service}`. ### MySQL Example This includes the service and remote IP address as well. Using `[last_login_dict_fields](/main/core/summaries/settings.html#last_login_dict_fields)` ensures that both the timestamp and the IP address are updated if a login record for that user/service already exists. dovecot.confSQL Schema doveconf ``` last_login { dict proxy { name = sql } key = last-login/%{service}/%{user}/access last_login_dict_fields { ip = %{remote_ip} } precision = ms } dict_server { dict sql { sql_driver = mysql mysql sql.example.com { dbname = mails user = dovecot password = pass } dict_map shared/last-login/$service/$user/access { sql_table = last_login value_field last_access { type = uint } key_field userid { value = $user } key_field service { value = $service } } dict_map shared/last-login/$service/$user/ip { sql_table = last_login value_field last_ip { } key_field userid { value = $user } key_field service { value = $service } } } } ``` sql ``` CREATE TABLE last_login ( userid VARCHAR(255) NOT NULL, service VARCHAR(10) NOT NULL, last_access BIGINT NOT NULL, last_ip VARCHAR(40) NOT NULL, PRIMARY KEY (userid, service) ); ``` ### Cassandra Example This includes the service and remote IP address as well. dovecot.confCassandra Schema doveconf ``` last_login { dict proxy { name = cassandra socket_path = dict-async } key = last-login/%{service}/%{user}/access last_login_dict_fields { ip = %{remote_ip} } precision = ms } dict_server { dict cassandra { driver = sql sql_driver = cassandra cassandra { hosts = cassandra.example.com keyspace = mails user = dovecot password = pass } dict_map shared/last-login/$service/$user/access { sql_table = last_login value_field last_access { type = uint } key_field userid { value = $user } key_field service { value = $service } } dict_map shared/last-login/$service/$user/ip { sql_table = last_login value_field last_ip { } key_field userid { value = $user } key_field service { value = $service } } } } ``` cql ``` CREATE TABLE last_login ( userid TEXT, service TEXT, last_access TIMESTAMP, last_ip TEXT, PRIMARY KEY ((userid), service) ); ``` ### Alternative Schema Cassandra Example Instead of using a separate last\_login table, add different services as separate fields to the main users table. dovecot.confCassandra Schema doveconf ``` last_login { dict proxy { name = cassandra socket_path = dict-async } key = last-login/%{service}/%{user}/%{remote_ip} precision = ms } dict_server { dict cassandra { driver = sql sql_driver = cassandra cassandra { hosts = cassandra.example.com keyspace = mails user = dovecot password = pass } dict_map shared/last-login/imap/$user/$remote_ip { sql_table = users value_field last_imap_access { type = uint } key_field userid { value = $user } key_field last_imap_ip { value = $remote_ip } } dict_map shared/last-login/pop3/$user/$remote_ip { sql_table = users value_field last_pop3_access { type = uint } key_field userid { value = $user } key_field last_pop3_ip { value = $remote_ip } } dict_map shared/last-login/lmtp/$user/$remote_ip { sql_table = users value_field last_lmtp_access { type = uint } key_field userid { value = $user } key_field last_lmtp_ip { value = $remote_ip } } } } ``` cql ``` CREATE TABLE users ( userid TEXT, last_imap_access TIMESTAMP, last_pop3_access TIMESTAMP, last_lmtp_access TIMESTAMP, last_imap_ip TEXT, last_pop3_ip TEXT, last_lmtp_ip TEXT, PRIMARY KEY ((userid)) ); ``` # Lazy Expunge Plugin (`lazy-expunge`) The lazy expunge plugin provides a "second-chance" to recover messages that would otherwise be deleted from a mailbox by user action. It does this by moving the message to a defined location when a user deletes the message from a mailbox. This behavior is useful for a variety of reasons: 1. Protect against misconfigured clients (e.g. POP3 client that deletes all messages) 2. Protect against accidental deletion (user error) 3. Archiving Generally, lazy-expunge is configured so that the expunged mails are not counted in the user's quota. Unless being used for archiving, autoexpunge should be used to prune the mailbox to control storage usage. ## Settings ### `lazy_expunge_mailbox` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [lazy-expunge storage locations](/main/core/plugins/lazy_expunge.html#storage-locations) The mailbox to move messages to when expunged. This setting MUST be defined or else lazy-expunge plugin will not be active. ``` lazy_expunge_mailbox = .EXPUNGED namespace inbox { mailbox Drafts { lazy_expunge_mailbox = } namespace "External accounts" { lazy_expunge_mailbox = } } ``` ### `lazy_expunge_only_last_instance` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Changed: 2.4.3 Changed from `no` to `yes`. If `yes`, only move to expunged storage if this is the last copy of the message in the user's account. This prevents the same mail from being duplicated in the lazy-expunge folder as the mail becomes expunged from all the folders it existed in. This setting prevents copying mail to the lazy-expunge folder when using the IMAP MOVE command. When using COPY/EXPUNGE, this setting prevents duplicates only with the following mailbox formats: - [Maildir](/main/core/config/mailbox_formats/maildir.html) (with `[maildir_copy_with_hardlinks = yes](/main/core/summaries/settings.html#maildir_copy_with_hardlinks)`, which is the default) - [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox)/[mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) ## Configuration ### Storage Location Messages that are expunged are moved to a single mailbox. The mailbox is created automatically. You probably also want to hide it with an [ACL](/main/core/plugins/acl.html) from the user, if recovery is only expected to be an action performed by an admin/operator. To move to a mailbox, do NOT add a trailing delimiter to the `[lazy_expunge_mailbox](/main/core/summaries/settings.html#lazy_expunge_mailbox)` setting. #### Example Configuration dovecot.conf doveconf ``` namespace inbox { mailbox .EXPUNGED { autoexpunge = 7days autoexpunge_max_mails = 100000 # Define ACL so that user cannot list the .EXPUNGED mailbox acl owner { rights = rwstipekxa } } } mail_plugins { lazy_expunge = yes acl = yes } acl_driver = vfile # Move messages to an .EXPUNGED mailbox lazy_expunge_mailbox = .EXPUNGED mailbox .EXPUNGED { # Expunged messages most likely don't want to be included in quota: quota_ignore = yes } ``` You could also leave the permissions empty if you don't want to allow clients to access it at all. ### Copy Only the Last Instance If a mail has multiple copies within a user account, each copy is normally moved to the lazy expunge storage when it's expunged. Example: this may happen when moving a message to Trash, as clients can issue IMAP COPY command to copy the message to Trash before expunging the message from the original mailbox. Deleting later from Trash would result in two copies of the same message in the lazy expunge storage. You can enable `[lazy_expunge_only_last_instance](/main/core/summaries/settings.html#lazy_expunge_only_last_instance)` to copy only the last instance to the expunge storage. This ensures that only a single copy of a message will appear in the expunge storage. WARNING This feature only works with certain storage setups. See `[lazy_expunge_only_last_instance](/main/core/summaries/settings.html#lazy_expunge_only_last_instance)` for further information. ### Quota Generally, it is desired that messages in expunge storage are NOT counted towards user quota, as the messages seen by the user will not match-up with the size of the quota otherwise (especially if expunge storage is hidden from users via ACL). Example to exclude expunged storage from the quota: doveconf ``` quota "User quota" { quota_storage_size = 1GB } mailbox .EXPUNGED { # Exclude .EXPUNGED mailbox from the quota quota_ignore = yes } ``` See [quota plugin](/main/core/plugins/quota.html). ## Cleaning Up ### Doveadm Doveadm can be used to manually clean expunge storage. Example to delete all messages in `.EXPUNGED` mailbox older than one day: sh ``` doveadm expunge mailbox '.EXPUNGED' savedsince 1d ``` ### Autoexpunge Set `[mailbox_autoexpunge](/main/core/summaries/settings.html#mailbox_autoexpunge)` configuration to automatically clean old messages. # Mail Compression Plugin (`mail-compress`) This plugin can be used to read compressed mbox, maildir or dbox files. It can also be used to write (via IMAP, [LDA](/main/core/config/delivery/lda.html) and/or [LMTP Server](/main/core/config/delivery/lmtp.html)) compressed messages to [dbox](/main/core/config/mailbox_formats/dbox.html) or [Maildir](/main/core/config/mailbox_formats/maildir.html) mailboxes. ## Compression Methods The following algorithms are supported: | Name | Library (algorithm) | Dovecot Support | | --- | --- | --- | | `bz2` | [libbzip2 (bzip2)](https://sourceware.org/bzip2/) | v2.0+ | | `gz` | [zlib (gzip)](https://www.zlib.net/) | v2.0+ | | `deflate` | [zlib (gzip)](https://www.zlib.net/) | v2.0+ | | `lz4` | [liblz4](https://www.lz4.org/) | v2.2.11+ | | `zstd` | [Zstandard](https://facebook.github.io/zstd/) | v2.3.12+ | ## Settings ### `compress_bz2_block_size_100k` **Default:** `9` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The compression block size to use. Must be between `1` (`100 000` bytes) and `9` (`900 000` bytes). ### `compress_deflate_level` **Default:** `6` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The compression level to use for deflate compression. Must be between `0` (no compression) and `9`. ### `compress_gz_level` **Default:** `6` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The compression level to use for gz compression. Must be between `0` (no compression) and `9`. ### `compress_zstd_level` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The compression level to use for zstd compression. Must be between `1` and `22`. ### `mail_compress_write_method` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The [Compression Method](/main/core/plugins/mail_compress.html#compression-methods) to use for writing new mails. If empty, new mails are written without compression. Old mails can still be read. ## Example Configuration doveconf ``` # Enable compression plugin globally for reading/writing: mail_plugins { mail_compress = yes } # Enable compressing the mails while saving (not just reading): mail_compress_write_method = zstd ``` ## Interaction with Mailbox Formats ### mbox Compressed mbox files can be accessed only as read-only. The compression is detected based on the file name, so your compressed mboxes should end with .gz or .bz2 extension. There is no support for compression during saving. ### dbox Mails can be stored as compressed. Existing uncompressed mails can't currently be directly compressed (or vice versa). You could, however, use `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html)` to copy all mails to another location (which saves them compressed) and then replace the original location with the new compressed location. You can do this by treating the operation the same as if you were migrating from one mailbox format to another (see the dsync page examples). ### Maildir When this plugin is loaded Dovecot can read both compressed and uncompressed files from Maildir. The files within a Maildir can use any supported compression algorithm (e.g., some can be compressed using gzip, while others are compressed using zstd). The algorithm is detected by reading the first few bytes from the file and figuring out if it's a valid compressed header. The file name doesn't matter. To avoid IMAP clients attempting to exploit security holes in the compression algorithm libraries (e.g., bzlib) by writing specially crafted mails using IMAP's APPEND command, Dovecot will not allow clients to save mails that are detected as compressed. All mails must have `,S=` in their filename where contains the original uncompressed mail size, otherwise there will be problems with quota calculation as well as other potential random failures. Note that if the filename doesn't contain the `,S=` before compression, adding it afterwards changes the base filename and thus the message UID. The safest thing to do is simply to not compress such files. You should also preserve the file's mtime so INTERNALDATE doesn't change. If you want to use dsync to convert to a compressed Maildir you may need `-o``maildir_copy_with_hardlinks=no` (this is set to yes by default and will prevent compression). ## Compress Existing Mails To compress existing mails, the supported way is to use local dsync migration. See [migrating mailboxes](/main/core/admin/migration.html). You'll probably want to use some cronjob to compress old mails. However note that to avoid seeing duplicate mails in rare race conditions you'll have to use the [maildirlock utility](https://github.com/dovecot/tools/blob/main/README.maildirlock). The idea is to: 1. Find the mails you want to compress in a single maildir. - Skip files that don't have `,S=` in the filename. 2. Compress the mails to `tmp/` - Update the compressed files' mtimes to be the same as they were in the original files (e.g. touch command) 3. Run `maildirlock `. It writes PID to stdout, save it. - `` is path to the directory containing Maildir's dovecot-uidlist (the control directory, if it's separate) - `` specifies how long to wait for the lock before failing. 4. If maildirlock grabbed the lock successfully (exit code 0) you can continue. 5. For each mail you compressed: 1. Verify that it still exists where you last saw it. 2. If it doesn't exist, delete the compressed file. Its flags may have been changed or it may have been expunged. This happens rarely, so just let the next run handle it. 3. If the file does exist, `rename() (mv)` the compressed file over the original file. Dovecot can now read the file, but to avoid compressing it again on the next run, you'll probably want to rename it again to include e.g. a `Z` flag in the file name to mark that it was compressed (e.g. `1223212411.M907959P17184.host,S=3271:2,SZ`). Remember that the [Maildir specifications](https://cr.yp.to/proto/maildir.html) require that the flags are sorted by their ASCII value, although Dovecot itself doesn't care about that. Unlock the maildir by sending a TERM signal to the maildirlock process (killing the PID it wrote to stdout). ## Benchmarking INFO A simple benchmarking exercise to compare compression ratios/speed between the various supported compression algorithms. ### Setup - Compression of a real-world corpus of mails of various lengths, compositions, and types - 128,788 messages - Messages imported via `[doveadm-import(1)](/main/core/man/doveadm-import.1.html)` into a single [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) mailbox - Mailbox storage in tmpfs partition, so drive performance should be irrelevant - Time is total clock time (real + sys) to compress the entire mailbox - Size is the total size of the sdbox mail data directory ONLY - Dovecot indexes are not included in size ### Results | Algorithm | Size (GB) | Compression | Time (MM:SS) | | --- | --- | --- | --- | | None | 7.99 | 0% | 0:21 | | `bz2` | 3.41 | 57% | 7:08 | | `gz` | 3.44 | 57% | 2:30 | | `deflate` | 3.44 | 57% | 2:34 | | `lz4` | 4.76 | 40% | 0:23 | | `zstd` | 3.41 | 57% | 0:34 | # Mail Crypt Plugin (`mail-crypt`) The mail crypt plugin is used to secure email messages stored in a Dovecot system. Messages are encrypted before written to storage and decrypted after reading. Both operations are transparent to the user. In case of unauthorized access to the storage backend, the messages will, without access to the decryption keys, be unreadable to the offending party. There can be a single encryption key for the whole system or each user can have a key of their own. The used cryptographical methods are widely used standards and keys are stored in portable formats, when possible. ## Functional Overview The use of mail crypt plugin depends on a user having a keypair, a private and a public key, for asymmetric cryptography. These keys are provisioned in a variable via the user database or directly from Dovecot configuration files. The public half of the provisioned keypairs are used to generate and encrypt keys for symmetric encryption. The symmetric keys are used to encrypt and decrypt individual files. Symmetric encryption is faster and more suitable for block mode storage encryption. The symmetric key used to encrypt a file is stored, after being encrypted with the public asymmetric key, together with the file. ## Encryption Technologies The mail crypt plugin provides encryption at rest for emails. Encryption of the messages is performed using the symmetric Advanced Encryption Standard (AES) algorithm in Galois/Counter Mode (GCM) with 256 bit keys. Integrity of the data is ensured using Authenticated Encryption with Associated Data (AEAD) with SHA256 hashing. The encryption keys for the symmetric encryption are randomly generated. These keys in turn are encrypted using a key derived with from the provisioned private key. Provisioned keys can be Elliptic Curve (EC) keys or RSA keys. Changed: 2.4.5 ML-KEM keys are also supported with OpenSSL 3.3+. Encryption is done using the Integrated Encryption Scheme (IES). This algorithm is usable both with EC and RSA keys. ## Limitations By default, AES-GCM is used. This algorithm is limited to 64 GiB input, after potential compression. If you expect to run to this limitation, you should use AES-CBC or ChaCha20-Poly1305 algorithm. Also, particularly the AES-XTS or AES-CCM cannot be used due to the way they work. ## Technical Requirements Using per-folder keys is not considered production quality, but global keys are fine. WARNING Improper configuration or use can make your emails unrecoverable! Treat encryption with care and backup encryption keys! This page assumes you are using configuring mail encryption from scratch with a recent version of Dovecot. ### Supported symmetric algorithms While mail crypt plugin does not support setting encryption algorithm, it is possible to specify one with FS crypt. Dovecot supports all algorithms in OpenSSL that have an OID assigned, and additionally few more (with official OIDs). In particular, XTS, CCM and CTR modes are not supported, due to the way they would need to be handled. Some operating systems limit the available algorithms with policies. It is recommended to use AES-GCM or ChaCha20-Poly1305 algorithm, with SHA256 or greater. Algorithm setting format is . E.g. `aes-256-gcm-sha256` or `chacha20-poly1305-sha256`. Note that hash algorithm is used for various hashing purposes, not just data integrity, so it is always required. Files encrypted with one algorithm can be decrypted even if the configuration specifies different algorithm, as the parameters are stored in the file. List of known algorithms that Dovecot supports as of writing. | Encryption algorithm | Supported size | Supported modes | | --- | --- | --- | | [chacha20](https://en.wikipedia.org/wiki/ChaCha20) | \- | [poly1305](https://en.wikipedia.org/wiki/ChaCha20-Poly1305)1,3 | | [aes](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) | 128, 192, 256 | [gcm](https://en.wikipedia.org/wiki/Galois/Counter_Mode)1, [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | | [camellia](https://en.wikipedia.org/wiki/Camellia_\(cipher\)) | 128, 192, 256 | [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | | [aria](https://en.wikipedia.org/wiki/ARIA_\(cipher\)) | 128, 192, 256 | [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | | [seed](https://en.wikipedia.org/wiki/SEED) | \- | [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | | [sm4](https://en.wikipedia.org/wiki/SM4_\(cipher\)) | \- | [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | | [des-ede3](https://en.wikipedia.org/wiki/Triple_DES) | \- | [cbc](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#CBC)2 | 1 Uses [AEAD](https://en.wikipedia.org/wiki/AEAD) integrity. 2 Uses [HMAC](https://en.wikipedia.org/wiki/HMAC) integrity. 3 Requires recent enough OpenSSL. ## Modes Of Operation Mail crypt plugin can operate using **either** global keys or folder keys. Using both is not supported. ### Global keys In this mode, all keying material is taken from the settings: - `[crypt_global_public_key_file](/main/core/summaries/settings.html#crypt_global_public_key_file)` - `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` - List of `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` keys You can use either Elliptic Curve (EC) keys (recommended) or RSA keys. Changed: 2.4.5 ML-KEM keys are also supported with OpenSSL 3.3+. See below for examples using them. No key generation is automatically performed. A good solution for environments where no user folder sharing is needed is to generate per-user EC key pair and encrypt that with something derived from user's password. The benefit is that it can be easier to do key management when you can do the EC re-encryption steps in case of password change in your user database instead of dovecot's database. You should not configure `[crypt_user_key_curve](/main/core/summaries/settings.html#crypt_user_key_curve)` when using global keys. #### Elliptic Curve (EC) Key In order to generate an EC key, you must first choose a curve from the output of this command: sh ``` openssl ecparam -list_curves ``` If you choose the curve `prime256v1`, generate an EC key with the command: sh ``` openssl ecparam -name prime256v1 -genkey | openssl pkey -out ecprivkey.pem ``` Then generate a public key out of your private EC key sh ``` openssl pkey -in ecprivkey.pem -pubout -out ecpubkey.pem ``` These keys can now be used with this configuration: doveconf ``` mail_plugins { mail_crypt = yes } crypt_global_public_key_file = ecpubkey.pem crypt_global_private_key main { crypt_private_key_file = ecprivkey.pem crypt_private_key_password = secret } ``` ##### Converting EC key to PKEY If you have an EC private key which begins with something like: ``` -----BEGIN EC PRIVATE KEY----- ``` With possibly parameters like this before that: ``` -----BEGIN EC PARAMETERS----- BgUrgQQACg== -----END EC PARAMETERS----- ``` You must convert it to pkey format with: sh ``` openssl pkey -in oldkey.pem -out newkey.pem ``` Then `newkey.pem` can be used with mail-crypt plugin. #### Using Edwards curve DSA (EdDSA) Added: 2.4.0 You can use EdSDA keys by using algorithm `X25519` or `X448` (case sensitive). To generate a suitable keypair, use sh ``` openssl genpkey -algorithm X448 -out edprivkey.pem openssl pkey -in private.pem -pubout -out edpubkey.pem ``` Note that ED25519 keys are not suitable for X25519. #### RSA key WARNING Use of RSA keys is discouraged, please use [EC keys](/main/core/plugins/mail_crypt.html#elliptic-curve-ec-key) instead. You can generate an unencrypted RSA private key in the pkey format with the command: sh ``` openssl genpkey -algorithm RSA -out rsaprivkey.pem ``` Alternatively, you can generate a password encrypted private key with: sh ``` openssl genpkey -algorithm RSA -out rsaprivkey.pem -aes-128-cbc -pass pass:secret ``` This does make the password show up in the process listing, so it can be visible for everyone on the system. Regardless of whether you generated an unencrypted or password encrypted private key, you can generate a public key out of it with: sh ``` openssl pkey -in rsaprivkey.pem -pubout -out rsapubkey.pem ``` These keys can then be used with this configuration: doveconf ``` mail_plugins { mail_crypt = yes } crypt_global_public_key_file = rsapubkey.pem crypt_global_private_key main { crypt_private_key_file = rsaprivkey.pem crypt_private_key_password = secret } ``` ### Folder Keys In this mode, a key pair is generated for the user. Then for each folder a key pair is generated. This folder is encrypted using the user key. A user can have more than one key pair for reading, but only one can be active for writing. - `[crypt_user_key_curve](/main/core/summaries/settings.html#crypt_user_key_curve)` must be set. - `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` must be set, as it is used to store the keys. #### Unencrypted User Keys In this version of the folder keys mode, each user's private key is stored unencrypted on the server. Example config for folder keys with Maildir: doveconf ``` mail_plugins { mail_crypt = yes } mail_attribute { dict file { path = %{home}/Maildir/dovecot-attributes } } crypt_user_key_curve = secp521r1 ``` #### Encrypted User Keys In this version of the folder keys mode, the user's private key is stored encrypted on the server. Example config for mandatory encrypted folder keys with Maildir: doveconf ``` mail_plugins { mail_crypt = yes } mail_attribute { dict file { path = %{home}/Maildir/dovecot-attributes } } crypt_user_key_curve = secp521r1 crypt_user_key_require_encrypted = yes ``` The password that is used to decrypt the user's private key must be provided via the `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` setting. See below. #### Choosing Encryption Password It is recommended to use a hash of the user's plaintext login password as the encryption key password instead of the plaintext password directly. This way the plaintext password is less likely to become visible accidentally, such as in debug logs. Also using a strong hash makes the key more resistant against brute force attacks. Another issue that you must consider when using the login password is that when the password changes, **you must re-encrypt the user private key**. Instead of using the login password, another possibility could be to store the encryption password in a separate database (e.g. SQL or LDAP). Example config where the user's login password is used as the encryption key password: doveconf ``` passdb sql { query = SELECT email as user, password, '%{password | hash("pbkdf2")}' AS userdb_crypt_user_key_password \ FROM virtual_users \ WHERE email='%{user}' } ``` ## Base64-encoded Keys Mail-crypt plugin can read keys that are base64 encoded. This is intended mostly for providing PEM keys via userdb, because it may be difficult to get userdb to return multi-line values. Hence, this is possible: Key Generationdovecot.conf sh ``` openssl ecparam -name secp256k1 -genkey | openssl pkey | base64 -w0 > ecprivkey.pem base64 -d ecprivkey.pem | openssl ec -pubout | base64 -w0 > ecpubkey.pem ``` doveconf ``` mail_plugins { mail_crypt = yes } crypt_global_private_key main { # create the filter, but leave its settings empty } passdb static { password = pass fields { crypt_global_public_key_file = ecpubkey.pem crypt_global_private_key/main/private_key_file = inline: } } ``` ## Read-only Mode If you have encrypted mailboxes that you need to read, but no longer want to encrypt new mail, use empty `[crypt_write_algorithm](/main/core/summaries/settings.html#crypt_write_algorithm)` setting: doveconf ``` crypt_write_algorithm = crypt_global_private_key main { crypt_private_key_file = server.key } ``` ## mail-crypt-plugin and ACLs If you are using global keys, mails can be shared within the key scope. The global key can be provided with several different scopes: - Global scope: key is configured in `dovecot.conf` file - Per-user(group) scope: key is configured in userdb file With folder keys, key sharing can be done to single user, or to multiple users. When a key is shared to a single user, and the user has a public key available, the folder key is encrypted using recipient's public key. This requires the `mail_crypt_acl` plugin, which will enable accessing the encrypted shared folders. If you have `[crypt_acl_require_secure_key_sharing](/main/core/summaries/settings.html#crypt_acl_require_secure_key_sharing)` enabled, you can't share the key to groups or someone with no public key. ## Decrypting Files Encrypted with mail-crypt Plugin You can use \[`decrypt.py`\]\[[https://github.com/dovecot/tools/blob/main/dcrypt-decrypt.py](https://github.com/dovecot/tools/blob/main/dcrypt-decrypt.py)\] to decrypt encrypted files. ## Settings ### `crypt_acl_require_secure_key_sharing` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, you cannot share a key to groups or someone without a public key. ### `crypt_global_private_key` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` - `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` List of global private key(s) to decrypt mails. Add `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` and optionally `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` inside each filter. ### `crypt_global_public_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) Public key to encrypt files. Key must be in [PEM pkey format](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey). The PEM key may additionally be base64-encoded into a single line, which can make it easier to store into userdb extra fields. ### `crypt_private_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - [Mail Crypt Plugin: Converting EC key to PKEY](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey) - `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` Private key in [Mail Crypt Plugin: Converting EC key to PKEY](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey). The PEM key may additionally be base64-encoded into a single line, which can make it easier to store into userdb extra fields. Used inside `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` and `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` lists. ### `crypt_private_key_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` Name of the private key inside `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` or `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)`. ### `crypt_private_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` Password to decrypt `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)`. ### `crypt_user_key_curve` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Defines the elliptic curve to use for key generation. A key pair is generated for the user, and a key pair is generated for each folder. The folder key is encrypted using the user key. This must be set if you wish to use [folder keys](/main/core/plugins/mail_crypt.html#folder-keys) rather than [global keys](/main/core/plugins/mail_crypt.html#global-keys). With global keys (either RSA or EC keys), all keying material is taken from the global key settings and no key generation is performed. Any valid curve supported by the underlying cryptographic library is allowed. Example: ``` crypt_user_key_curve = secp521r1 ``` For EdDSA, you need to use X448 or X25519, case sensitive. ### `crypt_user_key_encryption_key` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` - `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` List of private key(s) to decrypt user's master private key. Add `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` and optionally `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` inside each filter. ### `crypt_user_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password to decrypt user's master private key. ### `crypt_user_key_require_encrypted` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` If yes, require user's master private key to be encrypted with `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` or `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)`. If they are unset new user key generation will fail. This setting doesn't affect already existing non-encrypted keys. ### `crypt_write_algorithm` **Default:** `aes-256-gcm-sha256` **Value:** [string](/main/core/settings/types.html#string) Set the encryption algorithm for newly written mails. If empty, new mails are not encrypted. Existing encrypted mails (with any algorithm) can be decrypted regardless of this setting. ## `fs-crypt` `fs-crypt` is a [lib-fs wrapper](/main/core/config/fs.html) that can encrypt and decrypt files. It works similarly to the [fs-compress wrapper](/main/core/plugins/fs_compress.html#compression-fs-compress-plugin). It can be used to encrypt e.g.: - External mail attachments (`[mail_ext_attachment](/main/core/summaries/settings.html#mail_ext_attachment)`) Note that `fs-crypt` and the [fs-compress plugin](/main/core/plugins/fs_compress.html) wrapper can be also combined. Please make sure that compression is always applied before encryption. See [fs-compress plugin](/main/core/plugins/fs_compress.html) for an example and more details about compression. ### `fs-crypt` settings See [Mail Crypt Plugin: Settings](/main/core/plugins/mail_crypt.html#settings) for generic mail-crypt settings. `fs-crypt` supports the same key types as `mail-crypt`. Changed: 2.4.5 ML-KEM keys are also supported with OpenSSL 3.3+. WARNING Changed: 2.4.0 fs-crypt requires encryption keys by default. ### `fs_crypt_read_plain_fallback` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled files that are not encrypted are returned as-is. By default it results in a read error. ## Doveadm Commands TIP For doveadm commands that are working with password protected keys, the global `-o` option should be used to provide the password. Example: sh ``` doveadm -o crypt_private_key_password=some_password <...doveadm command...> ``` To encrypt/decrypt files manually, you can use: sh ``` doveadm \ -o fs=crypt,posix \ -o fs/crypt/fs_driver=crypt \ -o fs/posix/fs_driver=posix \ -o crypt_private_key_file=pubkey.pem \ -o crypt_global_private_key=main \ -o crypt_global_private_key/main/crypt_private_key_file=privkey.pem \ fs get/put '' path/to/input-file [/path/to/output-file] ``` ### `mailbox cryptokey export` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-export)` Exports user or folder private keys. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey generate` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-generate)` Generate new keypair for user or folder. To generate new active user key and re-encrypt all your keys with it can be done with sh ``` doveadm mailbox cryptokey generate -u username -UR ``` This can be used to generate new user keypair and re-encrypt and create folder keys. INFO You must provide a password if you want to generate password-protected keypair right away. You can also use `[doveadm mailbox cryptokey password]()` to secure it. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey list` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-list)` List all keys for user or mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey password` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-password)` Sets, changes or clears password for user's private key. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) # Mail Logger Plugin (`mail-log`) This plugin can be used to log several actions done in a mail session: - Setting and removing \\Deleted flag - Expunging (includes autoexpunge) - Copying mails - Saves - Mailbox creations - Mailbox deletions - Mailbox renames - Any flag changes Messages' IMAP UID and Message-ID header is logged for each action. Example: ``` imap(user): copy -> Trash: uid=908, msgid=<123.foo@bar> imap(user): delete: uid=908, msgid=<123.foo@bar> imap(user): expunged: uid=908, msgid=<123.foo@bar> ``` The [notify plugin](/main/core/plugins/notify.html) is required the mail\_log plugin's operation, so be certain it's also enabled. ## Settings ### `mail_log_cached_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, everything except `save` event will log only the fields that can be looked up from cache. This improves performance if some of the fields aren't cached and it's not a strict requirement to log them. ### `mail_log_events` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of events to log. Available events: - `delete` - `undelete` - `expunge` - `save` - `copy` - `mailbox_create` - `mailbox_delete` - `mailbox_rename` - `flag_change` ### `mail_log_fields` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of fields to log. | Field | Restrictions | | --- | --- | | `uid` | | | `box` | | | `msgid` | | | `size` | Only available for `expunge` and `copy` events. | | `vsize` | Only available for `expunge` and `copy` events. | | `vsize` | | | `flags` | | | `from` | | | `subject` | | ## Example Configuration doveconf ``` # Enable the plugin globally for all services mail_plugins { notify = yes mail_log = yes } mail_log_events = delete undelete expunge mailbox_delete mailbox_rename mail_log_fields = uid box msgid size from mail_log_cached_only = yes ``` # Mail Lua Plugin (`mail-lua`) mail-lua is a plugin that can be loaded to provide API for mail storage Lua plugins. See [Lua](/main/core/admin/lua/index.html). ## Settings ### `mail_lua` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[lua_file](/main/core/summaries/settings.html#lua_file)` - `[lua_settings](/main/core/summaries/settings.html#lua_settings)` **Changes:** - Added: 2.4.0 Named filter for initializing [mail\_lua plugin](/main/core/plugins/mail_lua.html) settings. Example: ``` mail_lua { lua_file = /etc/dovecot/user.lua lua_settings { extra_param = %{userdb:extra_param} } } ``` ## Variable expansion functions This component registers two providers. Note that these providers have very limited functionality at this moment. ### `%{lua_file(,,...)}` Executes the script at given path, and if it's successful, use the string result. If values are provided after function name, they are passed to the Lua function as parameters. Pipeline value is always added last, if present. Example: ``` mail_plugins { mail_lua = yes } mail_home = %{user|lua_file("/etc/dovecot/get_home.lua","get_home")} ``` ``` -- This is not a good way to do this, provided here for example -- purposes only. Do not use this. local function get_home(user) return "/home/" .. user end ``` ### `%{lua_call(,...)}` Executes Lua function in the `[lua_file](/main/core/summaries/settings.html#lua_file)`, and if successful, use the string result. If values are provided after function name, they are passed to the Lua function as parameters. Pipeline value is always added last, if present. Example: ``` mail_plugins { mail_lua = yes } mail_lua { lua_file = /etc/dovecot/mail.lua } mail_home = %{user|lua_call("get_home")} ``` ``` -- This is not a good way to do this, provided here for example -- purposes only. Do not use this. local function get_home(user) return "/home/" .. user end ``` # Notify Status Plugin (`notify-status`) This plugin updates a [dictionary](/main/core/config/dict.html) with mailbox status information every time a mailbox changes. ## Settings WARNING This plugin requires that the [notify plugin](/main/core/plugins/notify.html) is loaded. ### `mailbox_notify_status` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether notifications for a single mailbox or mailbox wildcards are enabled. Example: ``` mailbox INBOX { notify_status = yes } mailbox Spam { notify_status = yes } mailbox *BOX { notify_status = yes } ``` ### `notify_status` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Dependencies:** - [dictionary](/main/core/config/dict.html) The URI of the dictionary to use. This MUST be set for the plugin to be active. See [dictionary](/main/core/config/dict.html) for how to configure dictionaries. ``` notify_status { dict proxy { name = notify_status socket_path = dict-async } } ``` ### `notify_status_value` **Default:** `{"messages":%{messages},"unseen":%{unseen}}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) A template of the string that will be written to the dictionary. The template supports variable substitution of the form `%{variable_name}`. Supported variable substitutions: | Field | Value | | --- | --- | | `first_recent_uid` | First recent UID | | `highest_modseq` | Highest modification sequence number | | `highest_pvt_modseq` | Highest private modification sequence number | | `mailbox` | Mailbox name | | `messages` | Number of messages | | `recent` | Number of recent messages (deprecated) | | `uidnext` | Predicted next UID value | | `uidvalidity` | Current UID validity | | `unseen` | Number of unseen messages | | `username` | Username (user@domain) | ## Configuration ### Dictionary Configuration See [dictionary](/main/core/config/dict.html) for how to configure dictionaries. This plugin updates the `priv/status/` key. ### Example doveconf ``` mail_plugins { notify = yes notify_status = yes } notify_status { dict proxy { name = notify_status socket_path = dict-async } } # By default no mailbox is added to dict. To enable all notify_status for # all mailboxes add: #mailbox_notify_status = yes # If you keep the default mailbox_notify_status = no you can enable it per # mailbox like this: mailbox inbox { notify_status = yes } mailbox TestBox { notify_status = yes } ``` ### SQL dict Example Dictionary MapSQL Schema ``` dict_map priv/status/$box { sql_table = mailbox_status username_field = username key_field mailbox { value = $box } value_field status { } } ``` sql ``` CREATE TABLE mailbox_status ( username VARCHAR(255) NOT NULL, mailbox VARCHAR(255) NOT NULL, status VARCHAR(255), PRIMARY KEY (username, mailbox) ); ``` # Notify Plugin (`notify`) The notify plugin can be used to easily develop other plugins that need to do some work when something in user’s mailboxes change. See [mail-log plugin](/main/core/plugins/mail_log.html) plugin as an example how to develop a plugin based on the notify plugin. ## Settings There are no `dovecot.conf` settings for this plugin. # POP3 Migration Plugin (`pop3-migration`) The pop3-migration plugin is used to preserve POP3 UIDLs. When dsync is handling IMAP INBOX and requests a POP3 UIDL, the plugin connects to the POP3 server and figures out which IMAP messages match the POP3 messages and returns the appropriate POP3 UIDL. The plugin works by matching POP3 messages to IMAP messages. This isn’t always trivial with some servers, which can keep the POP3 and IMAP messages in different order or include more than just IMAP INBOX messages in the POP3 messages. DANGER Always do a test migration to verify that POP3 UIDLs are preserved correctly. If the UIDL format is wrong, all the mails have to be re-migrated. TIP See Also: - [migration](/main/core/admin/migration.html). ## Configuration This plugin requires a pop3c namespace configured for accessing the source POP3 server. For example: doveconf ``` namespace pop3c { prefix = POP3-MIGRATION-NS/ separator = / mail_driver = pop3c mail_path = inbox = no list = no hidden = yes } ``` ## Settings ### `pop3_migration_all_mailboxes` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) By default it's assumed that POP3 contains the same messages as IMAP INBOX. If there are any unexpected mails, the migration fails. If the POP3 server includes other folders' contents in POP3 as well, this setting needs to be enabled. It causes Dovecot to try to match POP3 messages in all the migrated folders, not just INBOX. There is no warning logged if any POP3 UIDLs are missing or if POP3 has messages that aren't found from IMAP. ### `pop3_migration_ignore_extra_uidls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If IMAP INBOX has all messages that exist in POP3, but POP3 still has some additional messages, the migration fails. Enable this setting to log it as a warning and continue anyway. This could happen if there's a race condition where a new mail is just delivered and it shows up in POP3 but not in IMAP. ### `pop3_migration_ignore_missing_uidls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If POP3 has messages that aren't found from IMAP INBOX, and IMAP INBOX also has messages not found from POP3, the migration fails. Enable this setting to log it as a warning and continue anyway. ### `pop3_migration_mailbox` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) This setting points to the POP3 INBOX in the configured pop3c namespace. This setting is required for the plugin to be active. ``` pop3_migration_mailbox = POP3-MIGRATION-NS/INBOX ``` ### `pop3_migration_skip_size_check` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) IMAP and POP3 messages are attempted to be matched by the message sizes by default. This is the most efficient way of matching the messages, since both IMAP and POP3 listings can usually be looked up from indexes/caches. If the IMAP INBOX and POP3 listings don't match exactly, or if two adjacent messages have the same size, the rest of the messages are matched by reading their headers. If this setting is enabled, the message size check is skipped entirely and only headers are matched. This may be necessary for reliability if it's known that the IMAP and POP3 messages cannot be matched by size anyway. ### `pop3_migration_skip_uidl_cache` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If imapc is configured with persistent indexes, the POP3 UIDLs are stored into the imapc mailbox's dovecot.index.cache files. Any following incremental migrations use these cached UIDLs if possible. This setting can be used to disable this in case there are any problems with the cache. This setting is unlikely to be ever needed. # Push Notification Plugin: Lua (`push-notification-lua`) You can use Lua to write custom push notification handlers. INFO Lua push notification requires the base push-notification plugin to be configured. See [push-notification plugin](/main/core/plugins/push_notification.html) for configuration information. See [Lua](/main/core/admin/lua/index.html) for general information on how Lua is implemented in Dovecot. ## Configuration Lua push notification handler requires [push-notification plugin](/main/core/plugins/push_notification.html), [mail-lua plugin](/main/core/plugins/mail_lua.html), and [push-notification-lua plugin](/main/core/plugins/push_notification_lua.html) to be loaded. | Name | Required | Type | Description | | --- | --- | --- | --- | | `push_notification_driver` | **YES** | [string](/main/core/settings/types.html#string) | To identify this settings block the driver should get the value `lua`. | | `push_notification_lua_file` | NO | [File](/main/core/settings/types.html#file) | The lua file to execute. See `[lua_file](/main/core/summaries/settings.html#lua_file)`. | | `push_notification_lua_settings` | NO | [String List](/main/core/settings/types.html#string-list) | Extra parameters to pass to the Lua `script_init()` function. See `[lua_settings](/main/core/summaries/settings.html#lua_settings)`. | ## Example Configuration doveconf ``` mail_plugins { mail_lua = yes notify = yes push_notification = yes push_notification_lua = yes } push_notification lua { lua_file = /path/to/lua/script lua_settings { extra_param = %{userdb:extra_param} } } ``` ## API Overview The Lua driver hooks into all events, and calls matching functions when found in Lua script. The driver supports all available [Push Notification Events](/main/core/plugins/push_notification.html#push-notification-events). All events are called within a transaction. The event is called with context and an event table, which contains the event parameters. All events contain at least: | Name | Description | | --- | --- | | `name` | Name of the event name | | `user` | Current mail user | Events are always called after the fact. There has to be at least one event handler, or the transaction begin and end functions are never called. This is an optimization to avoid a roundtrip to Lua when it's not needed. ### Transactions `dovecot_lua_notify_begin_txn(user)` Start transaction. Return value is used as transaction context and is treated as opaque value by Lua driver. The user parameter is `mail_user` object. `dovecot_lua_notify_end_txn(context, success)` End transaction, context is unreferenced. ### Mailbox Events All mailbox events contain the following parameters: | Name | Description | | --- | --- | | `mailbox` | Name of the affected mailbox | Functions: `dovecot_lua_notify_event_mailbox_create(context, {name, mailbox})` Called when mailbox has been created. `dovecot_lua_notify_event_mailbox_delete(context, {name, mailbox})` Called when mailbox has been deleted. `dovecot_lua_notify_event_mailbox_rename(context, {name, mailbox, mailbox_old})` Called when mailbox has been renamed, old name is retained in `mailbox_old` attribute. `dovecot_lua_notify_event_mailbox_subscribe(context, {name, mailbox})` Called when mailbox has been subscribed to. The mailbox does not necessarily exist. `dovecot_lua_notify_event_mailbox_unsubscribe(context, {name, mailbox})` Called when mailbox has been unsubscribed from. The mailbox does not necessarily exist. ### Message Events All message events contain following parameters: | Name | Description | | --- | --- | | `mailbox` | Mailbox name | | `uid` | Message UID | | `uid_validity` | Mailbox UIDVALIDITY | Functions: `dovecot_lua_notify_event_message_new(context, {name, mailbox, uid, uid_validity, date, tz, from, from_address, from_display_name, to, to_address, to_display_name, subject, snippet})` Called when message is delivered. `dovecot_lua_notify_event_message_append(context, {name, mailbox, uid, uid_validity, from, from_address, from_display_name, to, to_address, to_display_name, subject, snippet})` Called when message is APPENDed to a mailbox (via IMAP). `dovecot_lua_notify_event_message_read(context, {name, mailbox, uid, uid_validity})` Called when message is marked as `Seen`. `dovecot_lua_notify_event_message_trash(context, {name, mailbox, uid, uid_validity})` Called when message is marked `Deleted`. `dovecot_lua_notify_event_message_expunge(context, {name, mailbox, uid, uid_validity})` Called when message is expunged. `dovecot_lua_notify_event_flags_set(context, {name, mailbox, uid, uid_validity, flags, keywords_set})` Called when message flags or keywords are set. `flags` is a bitmask. `keywords_set` is a table of strings of the keywords set by the event. `dovecot_lua_notify_event_flags_clear(context, {name, mailbox, uid, uid_validity, flags, keywords_clear, keywords_old})` Called when message flags or keywords are removed. `flags` is a bitmask. `keywords_clear` contains the keywords cleared, `keywords_old` is the table of keywords that were set before the event. ## Example Scripts Simple example with `dovecot.http.client` lua ``` local url = require 'socket.url' local client = nil function script_init() client = dovecot.http.client({debug=True, timeout=10000}) end local function table_get(t, k, d) return t[k] or d end function dovecot_lua_notify_begin_txn(user) return {messages={}, ep=user:plugin_getenv("push_lua_url"), username=user.username} end function dovecot_lua_notify_end_txn(ctx, success) local i, msg = next(ctx["messages"], nil) while i do local rq = client:request({url=ctx["ep"], method="POST"}) rq:set_payload("from=" .. url.escape(table_get(msg, "from", "")) .. "&to=" .. url.escape(table_get(msg, "to", "")) .. "&subject=" .. url.escape(table_get(msg, "subject", "")) .. "&snippet=" .. url.escape(table_get(msg, "snippet", "")) .. "&user=" .. url.escape(ctx["username"])) r = rq:submit() if r and r:status()/100 ~= 2 then dovecot.i_error("lua-push: Remote error " .. tostring(r:reason()) .. " handling push notification") end i, msg = next(ctx["messages"], i) end end function dovecot_lua_notify_event_message_append(ctx, event) table.insert(ctx["messages"], event) end function dovecot_lua_notify_event_message_new(ctx, event) table.insert(ctx["messages"], event) end ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 Example with event code lua ``` -- To use: -- -- push_notification lua { -- lua_file = /home/example/empty.lua -- lua_settings { -- push_lua_url = http://push.notification.server/handler -- } -- } -- -- server is sent a POST message to given url with parameters -- local client = nil local url = require "socket.url" function table_get(t, k, d) return t[k] or d end function script_init() client = dovecot.http.client({debug=True, timeout=10000}) end function dovecot_lua_notify_begin_txn(user) return {user=user, event=dovecot.event(), ep=user:plugin_getenv("push_lua_url"), states={}, messages={}} end function dovecot_lua_notify_event_message_new(ctx, event) -- get mailbox status local mbox = ctx.user:mailbox(event.mailbox) mbox:sync() local status = mbox:status(dovecot.storage.STATUS_RECENT, dovecot.storage.STATUS_UNSEEN, dovecot.storage.STATUS_MESSAGES) mbox:free() ctx.states[event.mailbox] = status table.insert(ctx.messages, {from=event.from,subject=event.subject,mailbox=event.mailbox}) end function dovecot_lua_notify_event_message_append(ctx, event, user) dovecot_lua_notify_event_message_new(ctx, event, user) end function dovecot_lua_notify_end_txn(ctx) -- report all states for i,msg in ipairs(ctx.messages) do local e = dovecot.event(ctx.event) e:set_name("lua_notify_mail_finished") reqbody = "mailbox=" .. url.escape(msg.mailbox) .. "&from=" .. url.escape(table_get(msg, "from", "")) .. "&subject=" .. url.escape(table_get(msg, "subject", "")) e:log_debug(ctx.ep .. " - sending " .. reqbody) local rq = client:request({url=ctx["ep"], method="POST"}) rq:set_payload(reqbody) rq:add_header("content-type", "application/x-www-form-url.escaped") local code = rq:submit():status() e:add_int("result_code", code) e:log_info("Mail notify status " .. tostring(code)) end for box,state in pairs(ctx.states) do local e = dovecot.event() e:set_name("lua_notify_mailbox_finished") reqbody = "mailbox=" .. url.escape(state.mailbox) .. "&recent=" .. tostring(state.recent) .. "&unseen=" .. tostring(state.unseen) .. "&messages=" .. tostring(state.messages) e:log_debug(ctx.ep .. " - sending " .. reqbody) local rq = client:request({url=ctx["ep"], method="POST"}) rq:set_payload(reqbody) rq:add_header("content-type", "application/x-www-form-url.escaped") local code = rq:submit():status() e:add_int("result_code", code) e:log_info("Mailbox notify status " .. tostring(code)) end end ``` 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 # Push Notification Plugin (`push-notification`) Dovecot's Push Notification plugin implements a framework that exposes [RFC 5423](https://datatracker.ietf.org/doc/html/rfc5423) (Internet Message Store Events) events that occur in Dovecot to a system that can be used to report these events to external services. ## Push Notification Events These events (see [RFC 5423 (section 4.1)](https://datatracker.ietf.org/doc/html/rfc5423#section-4.1) for descriptions) are available within the notification framework, although a driver may not implement all of them: - FlagsClear - FlagsSet - MailboxCreate - MailboxDelete - MailboxRename - MailboxSubscribe - MailboxUnsubscribe - MessageAppend - MessageExpunge - MessageNew - MessageRead - MessageTrash These events are not supported by the notification framework: - Login (handled by [Authentication](/main/core/config/auth/overview.html)) - Logout (handled by [Authentication](/main/core/config/auth/overview.html)) - QuotaExceed (handled by [quota plugin](/main/core/plugins/quota.html)) - QuotaWithin (handled by [quota plugin](/main/core/plugins/quota.html)) ## Usage To use push notifications, both the `notify` and the `push_notification` plugins need to be activated by defining them in `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)`. This can either be set globally or restricted to the protocols where you want push notifications to be generated. For example, to restrict to mail delivery notifications only, this config should be used: doveconf ``` protocol lmtp { mail_plugins { notify = yes push_notification = yes } } # If notifications are also needed for LDA-based delivery, add: protocol lda { mail_plugins { notify = yes push_notification = yes } } ``` ## Settings ### `push_notification` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The configuration value is a named filter for a specified driver, see [Push Notifications](/main/core/plugins/push_notification.html) for their names and their supported options. ### `push_notification_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Dependencies:** - `[push_notification](/main/core/summaries/settings.html#push_notification)` The name of the driver. This value determines the available options and the behavior. See [Push Notifications](/main/core/plugins/push_notification.html) for the list of supported drivers and options. ## Drivers A push notification driver is defined by the `[push_notification](/main/core/summaries/settings.html#push_notification)` setting. The configuration value is a named filter for a specified driver, see the driver for their names and their supported options. It is possible to specify multiple push notification drivers by giving unique names to the individual driver configurations. Multiple configuration for a driver of the same type is useful if, for example, you want to process a single notification with the same driver but different endpoints. Example: doveconf ``` push_notification ox1 { driver = ox ox_url = http://example.com/foo } push_notification ox2 { driver = ox ox_url = http://example.com/bar } ``` The list of drivers shipped with Dovecot core appears below. ### DLOG (Debug log) \[`dlog`\] The most simple push notification plugin is the `dlog` plugin. It will write notifications into the debug log of the process. This driver has no options. To enable it you will have to define it explicitly, otherwise it is disabled. | Name | Required | Type | Description | | --- | --- | --- | --- | | `push_notification_driver` | **YES** | [string](/main/core/settings/types.html#string) | To identify this settings block the driver should get the value `dlog`. | #### Example Configuration doveconf ``` push_notification dlog { driver = dlog } ``` ### OX (Open-Xchange) driver \[`ox`\] The OX driver supports sending notifications on MessageNew events (i.e. mail deliveries, not IMAP APPENDs). This driver was designed for use with [OX App Suite Push Notification API](https://documentation.open-xchange.com/7.10.5/middleware/mail/dovecot/dovecot_push.html) but can be used by any push endpoint that implements this API, not just OX App Suite. #### Configuration Options | Name | Required | Type | Description | | --- | --- | --- | --- | | `push_notification_driver` | **YES** | [string](/main/core/settings/types.html#string) | To identify this settings block the driver should get the value `ox`. | | `push_notification_ox_url` | **YES** | [string](/main/core/settings/types.html#string) | The HTTP end-point (URL + authentication information) to use is configured in the Dovecot configuration file. Contains authentication information needed for Basic Authentication (if any). Example: `http + "://" + + ":" + + "@" + + ":" + + "/preliminary/http-notify/v1/notify"` For HTTPS endpoints, system CAs are trusted by default, but internal CAs might need further configuration. For further details on configuring the App Suite endpoint, see: [OX App Suite Push Notification API#Configuration of Dovecot "http-notify" plugin-in](https://documentation.open-xchange.com/latest/middleware/mail/dovecot/dovecot_push.html#configuration-of-dovecot-http-notify-plug-in) | | `push_notification_ox_cache_ttl` | NO | [time](/main/core/settings/types.html#time) | Cache lifetime for the METADATA entry for a user. (DEFAULT: `60 seconds`) | | `push_notification_ox_user_from_metadata` | NO | [boolean](/main/core/settings/types.html#boolean) | Use the user stored in the METADATA entry instead of the user sent by OX endpoint. (DEFAULT: user returned by endpoint response is used, i.e. `no`) | #### Example Configuration doveconf ``` push_notification ox { ox_url = http://login:pass@node1.domain.tld:8009/preliminary/http-notify/v1/notify user_from_metadata = yes cache_ttl = 10secs } ``` #### Metadata The push notifications are enabled separately for each user using METADATA. Normally \[OX App Suite\]\[ox-app-suite\] does this internally, but for e.g. testing purposes you can do this yourself: sh ``` doveadm mailbox metadata set -u user@example.com \ -s "" /private/vendor/vendor.dovecot/http-notify user=11@3 ``` #### Example Payload Push notification sent in JSON format with the following fields: | Name | Type | Description | | --- | --- | --- | | `event` | string | [RFC 5423](https://datatracker.ietf.org/doc/html/rfc5423) event type (currently only "MessageNew") | | `folder` | string | Mailbox name | | `from` | string | [RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822) address of the message sender (MIME-encoded), if applicable | | `imap-uid` | number | UID of the message, if applicable | | `imap-uidvalidity` | number | [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501) UIDVALIDITY value of the mailbox | | `snippet` | string | Snippet of the message body (UTF-8), if applicable | | `subject` | string | Subject of the message (MIME-encoded), if applicable | | `unseen` | number | [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501) UNSEEN value of the mailbox | | `user` | string | User identifier | INFO The returned numbers are generally integer values in the range `0`..`4294967295`. Example (`Content-Type: application/json; charset=utf-8`): json ``` { "user": "4@464646669", "imap-uidvalidity": 123412341, "imap-uid": 2345, "folder": "INBOX", "event": "MessageNew", "from": "=?utf-8?q?=C3=84?= ", "subject": "Test =?utf-8?q?p=C3=A4iv=C3=A4=C3=A4?=", "snippet": "Hey guys\nThis is only a test...", "unseen": 2 } ``` ### Lua \[`lua`\] You can use Lua to write custom push notification handlers. See [push-notification-lua plugin](/main/core/plugins/push_notification_lua.html) for configuration information. # Quota Clone Plugin (`quota-clone`) Quota clone plugin is useful when you want to store everybody's current quota usage to a database, but you don't want to use the database as the authoritative quota database. For example you might want to access everybody's quota via Redis (or SQL) but you don't store the Redis database permanently so it could become empty once in a while. Additionally, it is expensive to directly scan quota information from each individual user account, so quota-clone allows access to quota information that is less resource intensive. In these example use-cases, you can use [Quota Driver: Count](/main/core/plugins/quota.html#driver-count) as the authoritative quota database and make a copy of the quota usage to Redis. From Redis you could then once in a while gather everybody's current quota usage and send it to yet another place (e.g. for statistics handling). Every time quota is updated, the value is updated to the cloned dict. There are race conditions with it so the quota may not always be 100% correct. The old value is always replaced with the new one though (not just incremented/decremented) so the cloned quota is never too much wrong. ## Settings ### `quota_clone` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [dictionary](/main/core/config/dict.html) **Changes:** - Added: 2.4.0 Named filter for initializing dictionary used to update with quota clone information. ``` redis_host = 127.0.0.1 redis_port = 6379 quota_clone { dict redis { } } ``` ### `quota_clone_unset` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Unset quota information before updating. This is needed with some dict drivers that do not support upserting, such as SQL with older SQLite. ## Updated Keys The keys that are written: | Key | Value | | --- | --- | | `priv/quota/messages` | Count of messages | | `priv/quota/storage` | Storage usage (in bytes) | ## Example Configuration doveconf ``` mail_plugins { quota = yes quota_clone = yes } redis_host = 127.0.0.1 redis_port = 6379 quota_clone { dict redis { } } ``` More complex example using SQL: doveconf ``` dict_server { dict mysql { driver = sql sql_driver = mysql dict_map priv/quota/messages { sql_table = quota username_field = username value_field messages { } } dict_map priv/quota/storage { sql_table = quota username_field = username value_field bytes { } } } } quota_clone { dict proxy { name = mysql } } ``` # Quota Plugin (`quota`) Quota tracking and enforcing plugin. Three plugins are associated with quota: | Name | Description | | --- | --- | | [imap-quota plugin](/main/core/plugins/imap_quota.html) | Enables IMAP commands for requesting and administering current quota. | | quota (this plugin) | Implements the actual quota handling and includes all quota drivers. | | [quota-clone plugin](/main/core/plugins/quota_clone.html) | Copy the current quota usage to a dict. | ## Configuration ### Enabling Quota Plugins Enable in configuration files, e.g.: doveconf ``` # Enable quota plugin for tracking and enforcing the quota. mail_plugins { quota = yes } protocol imap { # Enable the IMAP QUOTA extension, allowing IMAP clients to ask for the # current quota usage. mail_plugins { imap_quota = yes } } # Keep this setting outside the quota { .. } to allow easily overriding it # in userdb lookups. quota_storage_size = 1G quota "User quota" { } ``` ### Quota Root Quota root is a concept from IMAP Quota specifications ([RFC 2087](https://datatracker.ietf.org/doc/html/rfc2087)). Normally you'll have only one quota root, but in theory there could be, e.g., "user quota" and "domain quota" roots. It's unspecified how the quota roots interact with each other (if at all). In some systems, for example, INBOX could have a completely different quota root from the rest of the mailboxes (e.g. INBOX in `/var/mail/` partition and others in `/home/` partition). ### Quota Limits There are two types of quota limits: - Message count limits - Storage size limits The message count limit is calculated as: `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` \* `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)`. Zero is assumed to be unlimited. The storage size limit is calculated as: `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` \* `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` + `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)`. Zero is assumed to be unlimited. The percentage and extra values are mainly useful to allow exceeding the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. #### Example doveconf ``` quota_storage_size = 1G namespace inbox { mailbox Trash { quota_storage_extra = 100M } mailbox SPAM { quota_ignore = yes } } ``` This means that the user has 1GB quota, but when saving messages to Trash mailbox it's possible to use up to 1.1GB of quota. The quota isn't specifically assigned to Trash, so if you had 1GB of mails in Trash you could still save 100MB of mails to Trash, but nothing to other mailboxes. Additionally, any messages in the SPAM folder are ignored and would not count against the quota at all. ### Per-User Quota You can override the quota settings in your [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). Keep global settings in configuration plugin section and override only those settings you need to in your userdb. Use `[doveadm user](/main/core/summaries/doveadm.html#user)` command to verify that the userdb returns the expected quota settings. #### Override: LDAP Example [LDAP authentication](/main/core/config/auth/databases/ldap.html) where the quota limit is in `quotaBytes` field: dovecot.conf doveconf ``` userdb ldap { ... fields { home = %{ldap:homeDirectory} quota_storage_size = {ldap:quotaBytes}B } } ``` #### Override: SQL Example (for MySQL): doveconf ``` userdb sql { query = SELECT uid, gid, home, CONCAT(quota_limit_bytes, 'B') AS quota_storage_size \ FROM users \ WHERE userid = '%{user}' passdb sql { # SQL with userdb prefetch: Remember to prefix quota_quota_storage_size with userdb_ # (just like all other userdb extra fields): query = SELECT userid AS user, password, uid AS userdb_uid, gid AS userdb_gid, \ CONCAT(quota_limit_bytes, 'B') AS userdb_quota_storage_size \ FROM users \ WHERE userid = '%{user}' } ``` Example (for PostgreSQL and SQLite): doveconf ``` sql_driver = sqlite # alternatively: pgsql userdb sql { query = SELECT uid, gid, home, quota_limit_bytes || 'B' AS quota_storage_size \ FROM users \ WHERE userid = '%{user}' } ``` #### Override: passwd-file Example [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) entries: ``` user:{plain}pass:1000:1000::/home/user::userdb_quota_storage_size=100M user2:{plain}pass2:1001:1001::/home/user2::userdb_quota_storage_size=200M user3:{plain}pass3:1002:1002::/home/user3::userdb_mail_path=~/Maildir userdb_quota_storage_size=300M ``` #### Override: passwd The [passwd authentication database](/main/core/config/auth/databases/passwd.html) userdb doesn't support extra fields. That's why you can't directly set users' quota limits to passwd file. You can use an additional userdb (e.g. [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) where only the quota limits are specified. ### Quota for Public Namespaces You can create a separate namespace-specific quota that's shared between all users. This is done by configuring the quota root inside the namespace filter. For example: doveconf ``` namespace public { type = public prefix = Public/ #mail_path = .. quota "Shared quota" { #quota_storage_size = ... } } quota "User quota" { #quota_storage_size = ... } ``` Note that globally configured quota roots are used only for private namespaces. ### Quota for Private Namespaces You can create a separate namespace-specific quota for a folder hierarchy. This is done by configuring the quota root inside the namespace filter. For example: doveconf ``` namespace inbox { quota "User quota" { #quota_storage_size = ... } } namespace archive { type = private prefix = Archive/ #mail_path = .. quota "Archive quota" { #quota_storage_size = ... } } ``` Note that both quotas must be configured inside the namespace filter. Using a global quota configuration would apply to both namespaces. ### Quota and Shared Namespaces Quota plugin considers shared namespaces against owner's quota, not the current user's. The regular private quota configuration is used - there is no need to explicitly configure quota for shared namespaces. The quota limits are also taken from the userdb. Public namespaces are ignored unless there is explicit quota specified for it. ### Custom Quota Exceeded Message See `[quota_exceeded_message](/main/core/summaries/settings.html#quota_exceeded_message)`. Example: doveconf ``` quota_exceeded_message = Quota exceeded, please go to http://www.example.com/over_quota_help for instructions on how to fix this. ``` ## Quota Drivers Quota driver specifies the method how Dovecot keeps track of the current quota usage. They don't specify users' quota limits - that's done by [returning extra fields from userdb](#per-user-quota). We recommend using [`count`](#driver-count) for any new installations. If you need usage data to an external database, consider using [quota-clone plugin](/main/core/plugins/quota_clone.html) for exporting the information. (It's very slow to query every user's quota from the index files directly.) ### Driver: Count The `count` quota driver tracks the quota internally within Dovecot's index files. INFO This is the **RECOMMENDED** way of calculating quota on recent Dovecot installations. Each mailbox's quota is tracked separately and when the current quota usage is wanted to be known, the mailboxes' quotas are summed up together. To get the best performance, make sure `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)`. WARNING If you're switching from some other quota driver to `count`, make sure that all the mails have their virtual sizes already indexed. Otherwise there may be a significant performance hit when Dovecot starts opening all the mails to get their sizes. You can help to avoid this by accessing the mailbox vsizes for all the users before doing the configuration change: `[doveadm mailbox status -u user@domain vsize '\*']()`. #### Configuration `count` driver doesn't have any additional parameters. doveconf ``` mailbox_list_index = yes # Avoid spending excessive time waiting for the quota calculation to finish # when mails' vsizes aren't already cached. If this many mails are opened, # finish the quota calculation on background in indexer-worker process. Mail # deliveries will be assumed to succeed, and explicit quota lookups will # return internal error. protocol !indexer-worker { mail_vsize_bg_after_count = 100 } # 10MB quota limit quota_storage_size = 10M quota "User quota" { } ``` ### Driver: fs The `fs` (filesystem) quota driver supports both local filesystems and rquota (NFS). #### Configuration ##### `quota_fs_message_limit` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If yes, use filesystem quota's inode limit as the message count limit. This can be useful with Maildir or sdbox. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ##### `quota_fs_mount_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) If specified, enable FS quota for the specified mount path. Only mailboxes existing in this mount path have the quota enabled. Empty value looks up the mountpoint automatically. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ##### `quota_fs_type` **Default:** `any` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``user``group` Using `any` attempts to use the user quota first, with a fallback to group quota. Using `user` or `group` only attempts to use the user or the group quota, with a fallback to unlimited quota limit. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). #### Systemd If you are using systemd, please make sure you **turn off**`PrivateDevices=yes`, otherwise the driver won't work properly. The best way to do this is to use `systemctl edit dovecot` command or add file `/etc/systemd/system/dovecot.service.d/override.conf` with: ``` [Service] PrivateDevices=off ``` #### Index Files It's a good idea to keep index files in a partition where there are no filesystem quota limits. The index files exist to speed up mailbox operations, so Dovecot runs more slowly if it can't keep them updated. You can specify the index file location with the `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` setting. Dovecot can handle "out of disk space" errors in index file handling and transparently move to in-memory indexes. It'll use the in-memory indexes until the mailbox is re-opened. #### mbox It's a good idea to have `[mbox_lazy_writes = yes](/main/core/summaries/settings.html#mbox_lazy_writes)` (default), otherwise Dovecot might give "Not enough disk space" errors when opening the mailbox, making it impossible to expunge any mails. If user has run out of quota and index files are also in memory (because they're also over quota), it's possible that message flag changes are lost. This should be pretty rare though because Dovecot keeps some extra space allocated inside the mbox file for flag changes. Example: doveconf ``` mail_driver = mbox mail_path = ~/mail mail_inbox_path = /var/mail/%{user} mail_index_path = /var/no-quotas/index/%{user} ``` #### Maildir Maildir needs to be able to add UIDs of new messages to `dovecot-uidlist` file. If it can't do this, it can give an error when opening the mailbox, making it impossible to expunge any mails. Currently the only way to avoid this is to use a separate partition for the uidlist files where there are no filesystem quota limits. You can do this with the `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)` setting. Example: doveconf ``` mail_driver = maildir mail_path = ~/Maildir mail_index_path = /var/no-quotas/index/%{user} mail_control_path = /var/no-quotas/control/%{user} ``` Note that if you change the location of the control files, Dovecot will look in the new control path directory (`/var/no-quotas/control/%{user}`) for the mailbox `subscriptions` file. #### Configuration Examples doveconf ``` mail_plugins { quota = yes } protocol imap { mail_plugins { imap_quota = yes } } quota user { driver = fs } ``` If you want to see both user and group quotas as separate quota roots, you can use: doveconf ``` quota "User quota" { driver = fs fs_type = user } quota "Group quota" { driver = fs fs_type = group } ``` If you have your mails in two filesystems, you can create two quota roots: doveconf ``` quota INBOX { driver = fs # Assuming INBOX in /var/mail/ which is mounted to / fs_mount_path = / } quota Others { driver = fs # Assuming other mailboxes are in /home mount fs_mount_path = /home } ``` ### Driver: imapc See [Imapc: Quota](/main/core/config/mailbox_formats/imapc.html#quota). #### Configuration ##### `quota_imapc_mailbox_name` **Default:** `INBOX` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_root_name](/main/core/summaries/settings.html#quota_imapc_root_name)` If non-empty, use `GETQUOTAROOT ` to get the imapc quota root. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ##### `quota_imapc_root_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` If `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` is empty, use `GETQUOTA ` to get the imapc quota. Some servers may have an empty quota root name. This is why if this setting and `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` both have empty values, this setting is used. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### Driver: maildir WARNING Note that **Maildir++ quota works only with Maildir format**. However, even with Maildir format the recommendation is to use [`count`](#driver-count). The `maildir` quota driver implements Maildir++ quota in Dovecot. Dovecot implements the [Maildir++ specification](https://www.courier-mta.org/imap/README.maildirquota.html) so Dovecot remains compatible with [Courier](https://www.courier-mta.org/), [maildrop](https://www.courier-mta.org/maildrop/), [Exim](https://www.exim.org/), etc. #### Maildirsize File The `maildirsize` file in the Maildir root directory contains both the quota limit information and the current quota status. It contains a header in format: ``` S,C ``` Removed: 2.4.0 Maildir++ quota limit must now be specified in Dovecot configuration. It will no longer be read from the `maildirsize` file. The limits are still written to the file header, but they are ignored by Dovecot. Maildir++ quota relies on `maildirsize` file having correct information, so if your users can modify the file in some way (e.g. shell access), you're relying on the goodwill of your users for the quota to work. You can't rely on Dovecot noticing external changes to Maildir and updating `maildirsize` accordingly. This happens eventually when quota is being recalculated, but it may take a while. Quota recalculation also won't trigger quota warning executions. Once the `maildirsize` reaches 5120 bytes, the quota is recalculated and the file is recreated. This makes sure that if quota happens to be broken (e.g. externally deleted files) it won't stay that way forever. ## Quota Service The quota service allows Postfix to check quota before delivery. This service does not support proxying, so it works only in non-clustered setups when there is a single Dovecot server. doveconf ``` service quota-status { executable = quota-status -p postfix inet_listener quota-status { # You can choose any port you want port = 12340 } client_limit = 1 } ``` And then have postfix `check_policy_service` check that: ``` smtpd_recipient_restrictions = ... check_policy_service inet:mailstore.example.com:12340 ``` For more about this service, see [https://sys4.de/en/blog/postfix-dovecot-mailbox-quota/](https://sys4.de/en/blog/postfix-dovecot-mailbox-quota/) ## Quota Warning Scripts You can configure Dovecot to run an external command when user's quota exceeds a specified limit. Note that the warning is ONLY executed at the exact time when the limit is being crossed, so when you're testing you have to do it by crossing the limit by saving a new mail. If something else besides Dovecot updates quota so that the limit is crossed, the warning is never executed. The quota warning limits are configured the same way as the actual [Quota Limits](/main/core/plugins/quota.html#quota-limits), but just placed inside the `[quota_warning](/main/core/summaries/settings.html#quota_warning)` filter. Only the command for the first exceeded limit is executed, so configure the highest limit first. The actual commands that are run need to be created as services (create a named Dovecot service and use the service name as the \`quota-warning socket name\` argument). ### Configuration #### `quota_warning` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Create a new [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The filter name refers to `[quota_warning_name](/main/core/summaries/settings.html#quota_warning_name)` setting. The `[execute](/main/core/summaries/settings.html#execute)` setting is required to be specified inside the filter. The order of `quota_warning` filters in the configuration is important: Only the first warning that matches the rules is executed. This means you must configure the highest limits first. #### `quota_warning_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Name of the [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The `[quota_warning](/main/core/summaries/settings.html#quota_warning)` filter name refers to this setting. This name is only used within the configuration to identify the quota warning - it has no meaning otherwise. #### `quota_warning_resource` **Default:** `storage` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `storage``message` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Which [quota resource](/main/core/plugins/quota.html#quota-limits) the quota warning is tracking. #### `quota_warning_threshold` **Default:** `over` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `over``under` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Should the quota warning be executed when quota grows over the limit, or when it drops under the limit. ### Example Configuration doveconf ``` quota user { warning warn-95 { quota_storage_percentage = 95 execute quota-warning { args = 95 %{user} } } warning warn-80 { quota_storage_percentage = 80 execute quota-warning { args = 80 %{user} } } warning warn-under { quota_storage_percentage = 100 # user is no longer over quota threshold = under execute quota-warning { args = below %{user} } } } service quota-warning { executable = script /usr/local/bin/quota-warning.sh # use some unprivileged user for executing the quota warnings user = vmail unix_listener quota-warning { } } ``` With the above example, when user's quota exceeds 80% `quota-warning.sh` is executed with parameter `80`. The same goes for when quota exceeds 95%. If user suddenly receives a huge mail and the quota jumps from 70% to 99%, only the 95 script is executed. You have to create the `quota-warning.sh` script yourself. Here is an example that sends a mail to the user: Example `quota-warning.sh` sh ``` #!/bin/sh PERCENT=$1 USER=$2 cat << EOF | /usr/local/libexec/dovecot/dovecot-lda -d $USER -o quota_enforce=no From: postmaster@domain.com Subject: quota warning Your mailbox is now $PERCENT% full. EOF ``` The quota enforcing is disabled to avoid looping. ### Overquota-flag [Quota warning scripts](/main/core/plugins/quota.html#quota-warning-scripts) can be used to set an overquota-flag to userdb (e.g. LDAP) when user goes over/under quota. This flag can be used by MTA to reject mails to an user who is over quota already at SMTP RCPT TO stage. A problem with this approach is there are race conditions that in some rare situations cause the overquota-flag to be set even when user is already under quota. This situation doesn't solve itself without manual admin intervention or the overquota-flag feature: This feature checks the flag's value every time user logs in (or when mail gets delivered or any other email access to user) and compares it to the current actual quota usage. If the flag is wrong, a script is executed that fixes up the situation. The `[execute](/main/core/summaries/settings.html#execute)` setting inside `[quota_over_status](/main/core/summaries/settings.html#quota_over_status)` named filter specifies the script that is executed. The current `[quota_over_status_current](/main/core/summaries/settings.html#quota_over_status_current)` value is appended as the last parameter. The overquota-flag name in userdb must be `[quota_over_status_current](/main/core/summaries/settings.html#quota_over_status_current)`. These settings are available: - `[quota_over_status_lazy_check](/main/core/summaries/settings.html#quota_over_status_lazy_check)` - `[quota_over_status_mask](/main/core/summaries/settings.html#quota_over_status_mask)` Example: doveconf ``` quota_over_status { # If quota_over_status_current=TRUE, the overquota-flag is enabled. # Otherwise not. mask = TRUE # Any non-empty value for quota_over_status_current means user is over quota. # Wildcards can be used in a generic way, e.g. "*yes" or "*TRUE*" #mask = * lazy_check = yes execute quota-warning { args = mismatch %{user} } } ``` ## Quota Grace See `[quota_storage_grace](/main/core/summaries/settings.html#quota_storage_grace)`. By default the last mail can bring user over quota. This is useful to allow user to actually unambiguously become over quota instead of fail some of the last larger mails and pass through some smaller mails. Of course the last mail shouldn't be allowed to bring the user hugely over quota, so by default this limit is 10 MB. To change the quota grace, use: doveconf ``` # allow user to become max 50 MB over quota quota_storage_grace = 50 M } ``` ## Maximum Mailbox Count See `[quota_mailbox_count](/main/core/summaries/settings.html#quota_mailbox_count)`. Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit. The default is `0`, which is unlimited. ## Maximum Messages Per Mailbox See `[quota_mailbox_message_count](/main/core/summaries/settings.html#quota_mailbox_message_count)`. Maximum number of messages that can be created in a single mailbox. ## Maximum Saved Mail Size See `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`. Dovecot allows specifying the maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or doveadm save). The default is `unlimited`. Since outgoing mail sizes are also typically limited on the MTA side, it can be beneficial to prevent user from saving too large mails, which would later on fail on the MTA side anyway. ## Quota Virtual Sizes Indicates that the quota plugin should use virtual sizes rather than physical sizes when calculating message sizes. Required for the `count` driver. This is automatically determined by the quota plugin. ## Quota Admin Commands The [imap-quota plugin](/main/core/plugins/imap_quota.html) implements the `SETQUOTA` command, which allows changing the logged in user's quota limit if the user is admin. See [imap-quota plugin](/main/core/plugins/imap_quota.html) for further configuration information. ## Quota Recalculation If your quotas are out of sync, you can use `[doveadm quota recalc -u <uid>]()` command to recalculate them. ## Quota and Trash Mailbox Standard way to expunge messages with IMAP works by: 1. Marking message with `\Deleted` flag 2. Actually expunging the message using EXPUNGE command Both of these commands can be successfully used while user's quota is full. However many clients use a `move-to-Trash` feature, which works by: 1. COPY the message to Trash mailbox 2. Mark the message with \\Deleted 3. Expunge the message from the original mailbox 4. (Maybe later expunge the message from Trash when `clean trash` feature is used) If user is over quota (or just under it), the first COPY command will fail and user may get an unintuitive message about not being able to delete messages because user is over quota. The possible solutions for this are: - Disable move-to-trash feature from client - You can create a separate quota rule ignoring Trash mailbox's quota. Note that this would allow users to store messages infinitely to the mailbox. - You can create a separate quota rule giving Trash mailbox somewhat higher quota limit (but not unlimited). To make sure users don't start keeping messages permanently in Trash you can use `[mailbox_autoexpunge](/main/core/summaries/settings.html#mailbox_autoexpunge)` to expunge old messages from Trash mailbox. ## Debugging Quota User's current quota usage can be looked up with `[doveadm quota get -u user@domain]()`. User's current quota may sometimes be wrong for various reasons (typically only after some other problems). The quota can be recalculated with `[doveadm quota recalc -u user@domain]()`. ## Quota Status Service Dovecot supports quota-status service, which uses Postfix-compatible policy server protocol. This allows Postfix to check the quota before mail delivery. Example: dovecot.conf/etc/postfix/main.cf doveconf ``` service quota-status { executable = quota-status -p postfix unix_listener /var/spool/postfix/private/quota-status { user = postfix } # Or with TCP: inet_listener postfix { # You can choose any port you want port = 12340 } client_limit = 1 } ``` ``` smtpd_recipient_restrictions = ... check_policy_service unix:private/quota-status # Or with TCP: #check_policy_service inet:mailstore.example.com:12340 ``` ### Configuration #### `quota_status_nouser` **Default:** `REJECT Unknown user` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a nonexistent users. #### `quota_status_overquota` **Default:** `554 5.2.2 %{error}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is over the quota. The `%{error}` variable expands to the more detailed reason, which is generally `[quota_exceeded_message](/main/core/summaries/settings.html#quota_exceeded_message)`. #### `quota_status_success` **Default:** `OK` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is under the quota. #### `quota_status_toolarge` **Default:** `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a mail that is larger than user's `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` or `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`. Empty value defaults to `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)`. ## Settings ### `quota` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Quota Root](/main/core/plugins/quota.html#quota-root) Create a new [quota root](/main/core/plugins/quota.html#quota-root). The filter name refers to `[quota_name](/main/core/summaries/settings.html#quota_name)` setting. Globally configured quota roots are used only for private namespaces. To use quota for public namespaces, configure it inside the public namespace. Example: ``` quota "User quota" { storage_size = 1G } ``` ### `quota_driver` **Default:** `count` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Drivers](/main/core/plugins/quota.html#quota-drivers) Quota driver to use. See [Quota Drivers](/main/core/plugins/quota.html#quota-drivers). ### `quota_enforce` **Default:** `yes` `quota_imapc { no }` **Value:** [boolean](/main/core/settings/types.html#boolean) If disabled, the quota limit isn't actually enforced. The quota is still tracked and the current quota usage is visible to IMAP GETQUOTA commands. ### `quota_exceeded_message` **Default:** `Quota exceeded (mailbox for user is full)` **Value:** [string](/main/core/settings/types.html#string) The message specified here is passed on to a user who goes over quota. There are also other messages, which are currently hard coded: - When exceeding `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`: `Mail size is larger than the maximum size allowed by server configuration` - When exceeding `[quota_mailbox_message_count](/main/core/summaries/settings.html#quota_mailbox_message_count)`: `Too many messages in the mailbox` ### `quota_fs_message_limit` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If yes, use filesystem quota's inode limit as the message count limit. This can be useful with Maildir or sdbox. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_fs_mount_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) If specified, enable FS quota for the specified mount path. Only mailboxes existing in this mount path have the quota enabled. Empty value looks up the mountpoint automatically. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_fs_type` **Default:** `any` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``user``group` Using `any` attempts to use the user quota first, with a fallback to group quota. Using `user` or `group` only attempts to use the user or the group quota, with a fallback to unlimited quota limit. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_hidden` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If yes, hide the quota root from IMAP GETQUOTA commands. ### `quota_ignore` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If yes, don't include this mailbox or namespace in quota calculations. Example: ``` namespace inbox { mailbox Trash { quota_ignore = yes } } namespace secondary { quota_ignore = yes } ``` ### `quota_ignore_unlimited` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If yes, ignore the quota root entirely if it has no quota limits. This means no tracking of the quota, and not making it visible to IMAP GETQUOTA commands. ### `quota_imapc_mailbox_name` **Default:** `INBOX` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_root_name](/main/core/summaries/settings.html#quota_imapc_root_name)` If non-empty, use `GETQUOTAROOT ` to get the imapc quota root. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### `quota_imapc_root_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` If `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` is empty, use `GETQUOTA ` to get the imapc quota. Some servers may have an empty quota root name. This is why if this setting and `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` both have empty values, this setting is used. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### `quota_mail_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Quota: Maximum Saved Mail Size](/main/core/plugins/quota.html#maximum-saved-mail-size) The maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or `[doveadm save](/main/core/summaries/doveadm.html#save)`). If configured, will also be advertized with `APPENDLIMIT` extensions for IMAP clients, and `SIZE` extension for LMTP clients. ### `quota_mailbox_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Quota: Maximum Mailbox Count](/main/core/plugins/quota.html#maximum-mailbox-count) **Changes:** - Added: 2.4.0 Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit. ### `quota_mailbox_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of messages that can be created in a single mailbox. ### `quota_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Maximum number of messages for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` to get the final value (in this mailbox or namespace). This is reported as the MESSAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ### `quota_message_percentage` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` Multiplier for the `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` setting (in this mailbox/namespace). This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_message_count = 10000 namespace inbox { mailbox Trash { # 110% * 10000 = 11000 limit quota_message_percentage = 110 } } ``` ### `quota_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota](/main/core/summaries/settings.html#quota)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Name of the [quota root](/main/core/plugins/quota.html#quota-root). The `[quota](/main/core/summaries/settings.html#quota)` filter name refers to this setting. The quota root name is just an arbitrary string that is sent to IMAP clients, which in turn may show it to the user. The name has no meaning. ### `quota_over_status` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) Named filter for executing the overquota-flag script. The `[execute](/main/core/summaries/settings.html#execute)` setting is required to be specified inside the filter. ### `quota_over_status_current` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) An identifier that indicates whether the overquota-flag is active for a user. This identifier is compared against `[quota_over_status_mask](/main/core/summaries/settings.html#quota_over_status_mask)` to determine if the overquota-flag should be set for the user. Usually, this value will be loaded via [userdb](/main/core/config/auth/userdb.html). ### `quota_over_status_lazy_check` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, overquota-flag is checked only when current quota usage is going to already be checked anyway. This prevents any additional storage I/O that would be caused by the overquota-flag check. ### `quota_over_status_mask` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) The search string to match against `[quota_over_status_current](/main/core/summaries/settings.html#quota_over_status_current)` to determine if the overquota-flag is set for the user. Wildcards can be used in a generic way, e.g. `*yes` or `*TRUE*`. ### `quota_status_nouser` **Default:** `REJECT Unknown user` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a nonexistent users. ### `quota_status_overquota` **Default:** `554 5.2.2 %{error}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is over the quota. The `%{error}` variable expands to the more detailed reason, which is generally `[quota_exceeded_message](/main/core/summaries/settings.html#quota_exceeded_message)`. ### `quota_status_success` **Default:** `OK` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is under the quota. ### `quota_status_toolarge` **Default:** `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a mail that is larger than user's `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` or `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`. Empty value defaults to `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)`. ### `quota_storage_extra` **Default:** `0` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` - [Quota Root](/main/core/plugins/quota.html#quota-root) If set, increase the `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` (for the mailbox/namespace) by this amount. This is an alternative to using `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)`, although both can also be used. This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_storage_size = 1G namespace inbox { mailbox Trash { # 1G + 100M = 1100M quota_storage_extra = 100M } } ``` ### `quota_storage_grace` **Default:** `10 M` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Quota Root](/main/core/plugins/quota.html#quota-root) If set, allows message deliveries (LDA, LMTP) to exceed quota once by this amount. After the quota is already over the limit, the grace no longer applies. This prevents a situation where some smaller mails may still become delivered, but larger mail deliveries fail, and the user may not have received any warning about reaching the quota limit. ### `quota_storage_percentage` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Multiplier for the `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` setting (in this mailbox/namespace). This is an alternative to using `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)`, although both can also be used. This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_storage_size = 1G namespace inbox { mailbox Trash { # 110% * 1G = 1100M quota_storage_percentage = 110 } } ``` ### `quota_storage_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` - `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` - `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` - [Quota Root](/main/core/plugins/quota.html#quota-root) Quota storage size limit for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` and then increased by `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` to get the final value (in this mailbox or namespace). This is reported as the STORAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ### `quota_warning` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Create a new [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The filter name refers to `[quota_warning_name](/main/core/summaries/settings.html#quota_warning_name)` setting. The `[execute](/main/core/summaries/settings.html#execute)` setting is required to be specified inside the filter. The order of `quota_warning` filters in the configuration is important: Only the first warning that matches the rules is executed. This means you must configure the highest limits first. ### `quota_warning_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Name of the [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The `[quota_warning](/main/core/summaries/settings.html#quota_warning)` filter name refers to this setting. This name is only used within the configuration to identify the quota warning - it has no meaning otherwise. ### `quota_warning_resource` **Default:** `storage` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `storage``message` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Which [quota resource](/main/core/plugins/quota.html#quota-limits) the quota warning is tracking. ### `quota_warning_threshold` **Default:** `over` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `over``under` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) Should the quota warning be executed when quota grows over the limit, or when it drops under the limit. # Pigeonhole Sieve Extprograms Plugin (`sieve-extprograms`) The "sieve\_extprograms" plugin provides an extension to the Sieve filtering language, adding new action commands for invoking a predefined set of external programs. Messages can be piped to or filtered through those programs and string data can be input to and retrieved from those programs. To mitigate the security concerns, the external programs cannot be chosen arbitrarily; the available programs are restricted through administrator configuration. ## Configuration The plugin is activated by adding it to the `[sieve_plugins](/main/core/summaries/settings.html#sieve_plugins)` setting: doveconf ``` sieve_plugins { sieve_extprograms = yes } ``` This plugin registers the `vnd.dovecot.pipe`, `vnd.dovecot.filter`, and `vnd.dovecot.execute` extensions with the Sieve interpreter. However, these extensions are not enabled by default and thus need to be enabled explicitly. It is recommended to restrict the use of these extensions to global context by adding these to the `[sieve_global_extensions](/main/core/summaries/settings.html#sieve_global_extensions)` setting. If personal user scripts also need to directly access external programs, the extensions need to be added to the `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting. The commands introduced by the Sieve language extensions in this plugin can directly pipe a message or string data to an external program (typically a shell script) by forking a new process. Alternatively, these can connect to a unix socket behind which a Dovecot script service is listening to start the external program, e.g. to execute as a different user or for added security. The program name specified for the new Sieve `pipe`, `filter`, and `execute` commands is used to find the program or socket in a configured directory. Separate directories are specified for the sockets and the directly executed binaries. The socket directory is searched first. Since the use of "/" in program names is prohibited, it is not possible to build a hierarchical structure. Programs are executed with a limited set of environment variables: `HOME`, `USER`, `SENDER`, `RECIPIENT`, and `ORIG_RECIPIENT`. The `[import_environment](/main/core/summaries/settings.html#import_environment)` setting does not apply here. If a shell script is expected to read a message or string data, it must fully read the provided input until the data ends with EOF, otherwise the Sieve action invoking the program will fail. The action will also fail when the shell script returns a nonzero exit code. Standard output is available for returning a message (for the filter command) or string data (for the execute command) to the Sieve interpreter. Standard error is written to the LDA log file. The three extensions introduced by this plugin - `vnd.dovecot.pipe`, `vnd.dovecot.filter` and `vnd.dovecot.execute` - each have separate but similar configuration. ## Settings ### `sieve_execute_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory where the plugin looks for programs (shell scripts) to execute directly for the `vnd.dovecot.execute` extension. ### `sieve_execute_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) Configures the maximum execution time after which the program run by the `vnd.dovecot.execute` extension is forcibly terminated. ### `sieve_execute_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.execute` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_execute_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.execute` extension. ### `sieve_filter_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory where the plugin looks for programs (shell scripts) to execute directly and filter messages through for the `vnd.dovecot.filter` extension. ### `sieve_filter_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) Configures the maximum execution time after which the program run by the `vnd.dovecot.filter` extension is forcibly terminated. ### `sieve_filter_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.filter` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_filter_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.filter` extension. ### `sieve_pipe_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory where the plugin looks for programs (shell scripts) to execute directly and pipe messages to for the `vnd.dovecot.pipe` extension. ### `sieve_pipe_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) Configures the maximum execution time after which the program run by the `vnd.dovecot.pipe` extension is forcibly terminated. ### `sieve_pipe_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.pipe` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_pipe_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.pipe` extension. ## Specification Read the [specification](https://github.com/dovecot/pigeonhole/blob/master/doc/rfc/spec-bosch-sieve-extprograms.txt) for detailed information on how to use the new language extensions. ## Configuration Examples ### Socket Service for "pipe" and "execute" doveconf ``` sieve_script personal { path = ~/.dovecot.sieve } sieve_plugins { sieve_extprograms = yes } sieve_global_extensions { vnd.dovecot.pipe = yes vnd.dovecot.execute = yes } # pipe sockets in /var/run/dovecot/sieve-pipe sieve_pipe_socket_dir = sieve-pipe # execute sockets in /var/run/dovecot/sieve-execute sieve_execute_socket_dir = sieve-execute service sieve-pipe-script { # This script is executed for each service connection executable = script /usr/lib/dovecot/sieve-extprograms/sieve-pipe-action.sh # use some unprivileged user for execution user = dovenull # socket name is program-name in Sieve (without sieve-pipe/ prefix) unix_listener sieve-pipe/sieve-pipe-script { } } service sieve-execute-action { # This script is executed for each service connection executable = script /usr/lib/dovecot/sieve-extprograms/sieve-execute-action.sh # use some unprivileged user for execution user = dovenull # socket name is program-name in Sieve (without sieve-execute/ prefix) unix_listener sieve-execute/sieve-execute-action { } } ``` ### Direct Execution for "pipe" and "filter" doveconf ``` sieve_script personal { path = ~/.dovecot.sieve } sieve_plugins { sieve_extprograms = yes } sieve_global_extensions { vnd.dovecot.pipe = yes vnd.dovecot.filter = yes } # This directory contains the scripts that are available for the pipe command. sieve_pipe_bin_dir = /usr/lib/dovecot/sieve-pipe # This directory contains the scripts that are available for the filter # command. sieve_filter_bin_dir = /usr/lib/dovecot/sieve-filter ``` ### Test Incoming Message This simple example shows how to use the "vnd.dovecot.execute" extension to perform some sort of test on the incoming message. dovecot.confSieve Scripthasfrop.sh doveconf ``` sieve_extensions { vnd.dovecot.execute = yes } sieve_plugins { sieve_extprograms = yes } sieve_execute_bin_dir = /usr/lib/dovecot/sieve-execute ``` ``` require "vnd.dovecot.execute"; if not execute :pipe "hasfrop.sh" { discard; stop; } ``` sh ``` # Something that reads the whole message and inspects it for some # property. Not that the whole message needs to be read from input! N=`cat | grep -i "FROP"` # Check it for the undesirable text "FROP" if [ ! -z "$N" ]; then # Result: deny exit 1; fi # Result: accept exit 0 ``` At the location `/usr/lib/dovecot/sieve-execute`, create the executable script `hasfrop.sh`. In this example, the `hasfrop.sh` checks whether the message contains the literal text "FROP" anywhere in the message. The Sieve script shown above discards the message if this script ends with an exit code other than 0, which happens when "FROP" was found. ### Query/Update MySQL This example shows how to use the `vnd.dovecot.execute` extension for querying/updating a MySQL database. This is used to redirect messages only once every 300s for a particular sender. Note that this particular use case could also be implemented using the Sieve [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html). dovecot.confSieve Scriptvacationcheck.sh doveconf ``` sieve_extensions { vnd.dovecot.execute = yes } sieve_plugins { sieve_extprograms = yes } sieve_execute_bin_dir = /usr/lib/dovecot/sieve-execute ``` ``` require ["variables", "copy", "envelope", "vnd.dovecot.execute"]; # put the envelope-from address in a variable if envelope :matches "from" "*" { set "from" "${1}"; } # execute the vacationcheck.sh program and redirect the message based on # its exit code if execute :output "vacation_message" "vacationcheck.sh" ["${from}","300"] { redirect :copy "foo@bar.net"; } ``` sh ``` USER=postfixadmin PASS=pass DATABASE=postfixadmin # DB STRUCTURE #CREATE TABLE `sieve_count` ( # `from_address` varchar(254) NOT NULL, # `date` datetime NOT NULL #) ENGINE=InnoDB DEFAULT CHARSET=latin1; # #ALTER TABLE `sieve_count` # ADD KEY `from_address` (`from_address`); MAILS=$(mysql -u$USER -p$PASS $DATABASE --batch --silent -e "SELECT count(*) as ile FROM sieve_count WHERE from_address='$1' AND DATE_SUB(now(),INTERVAL $2 SECOND) < date;") ADDRESULT=$(mysql -u$USER -p$PASS $DATABASE --batch --silent -e "INSERT INTO sieve_count (from_address, date) VALUES ('$1', NOW());") # uncomment below to debug # echo User $1 sent $MAILS in last $2 s >> /usr/lib/dovecot/sieve-pipe/output.txt # echo Add result : $ADDRESULT >> /usr/lib/dovecot/sieve-pipe/output.txt # echo $MAILS if [ "$MAILS" = "0" ] then exit 0 fi exit 1 ``` At the location `/usr/lib/dovecot/sieve-execute`, create the executable script `vacationcheck.sh`. In this example, the `vacationcheck.sh` script needs two parameters: the sender address and a time interval specified in seconds. The time interval is used to specify the minimum amount of time that needs to have passed since the sender was last seen. If the script returns exit code 0, then message is redirected in the Sieve script shown above. # Sieve IMAPSieve Plugin (`sieve-imapsieve`) IMAPSieve ([RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)) defines the use of Sieve filtering in IMAP, operating when messages are created or their attributes are changed. The `sieve_imapsieve` plugin implements the `imapsieve` extension for the Sieve filtering language, adding functionality for using Sieve scripts from within IMAP. TIP Full details about this plugin can be found at [imap-sieve plugin](/main/core/plugins/imap_sieve.html). # Sieve Plugin (`sieve`) ## Configuration To use Sieve, you will first need to make sure you are using Dovecot [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) for delivering incoming mail to users' mailboxes. Then, you need to enable the Sieve plugin in your configuration: doveconf ``` protocol lda { mail_plugins { sieve = yes } } protocol lmtp { mail_plugins { sieve = yes } } ``` ## Script storage Sieve scripts are retrieved from a script storage. This can currently be the local filesystem, an LDAP database or any dict storage. Depending on the storage implementation, its type and its configuration, storages can contain one script, several scripts identified by name, and a series of scripts in a well-defined order to be executed in sequence. Script storages are configured in a named `[sieve_script](/main/core/summaries/settings.html#sieve_script)` block: doveconf ``` sieve_script personal { path = ~/.dovecot.sieve } ``` The storage name (`personal` in the example) is used internally within configurations, as an identifier for logging, and as an identifier for command line tools. It also allows updating a storage that was defined earlier - by repeating the `[sieve_script](/main/core/summaries/settings.html#sieve_script)` block and adding additional configuration settings - or it allows userdb to override storage settings for specific users. ### Script storage types Sieve scripts can be evaluated at various stages in message delivery and for stored messages. The type of the Sieve script storage determines where it is applicable, how the storage is accessed and how the retrieved Sieve script is evaluated. The type of the Sieve script storage is configured using the `[sieve_script_type](/main/core/summaries/settings.html#sieve_script_type)` setting. The following types are currently recognized (others are defined by the [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html)): #### `personal` The `personal` storage serves as the user's main personal storage. Although more than a single `personal` storage can be defined, only the first one listed in the configuration is used. The LDA Sieve plugin uses the personal storage to find the active script for Sieve filtering at delivery. If the storage supports storing more than a single script (e.g. the [file storage](/main/core/plugins/sieve.html#file-storage-driver) does), personal scripts can also be retrieved by name. The Sieve include extension will then use this storage for retrieving `:personal` scripts and the ManageSieve service will be able to store the user's scripts there. If the storage supports storing more than a single script, only one of those scripts will be the active script used at delivery. The active script can be managed by the user through the ManageSieve service. If the personal storage has no active script, the [default script](/main/core/plugins/sieve.html#script-storage-type-default) will be executed if configured. If no personal storage is defined explicitly, auto-detection will be attempted. This is currently only trying the [file storage driver](/main/core/plugins/sieve.html#file-storage-driver), which looks for a `~/.dovecot.sieve` script file or a directory at `~/sieve/` containing script files. In the latter case `~/.dovecot.sieve` is expected to be a symbolic link pointing to the active script file. If auto-detection also finds no personal storage, Sieve processing will be skipped and no default script is executed. #### `after` An `after` storage is the source of one script or several scripts that are to be executed after the user's personal script. If the storage supports storing more than a single script, these scripts will be executed in a well-defined order defined by the storage driver. Multiple `after` storages can be configured and each storage will be accessed in sequence to retrieve scripts for execution after the personal script. The storages will be accessed in the order these storages are defined in the configuration, unless the order is overridden by the `[sieve_script_precedence](/main/core/summaries/settings.html#sieve_script_precedence)` setting. This is usually a global script, so be sure to pre-compile the specified script manually in that case using the sievec command line tool, as explained by `[sievec(1)](/main/core/man/sievec.1.html)`. #### `before` A `before` storage behaves identical to an `after` storage, except the contained script or scripts are run **before** user's personal script (instead of **after**). #### `default` The `default` storage yields the sieve script that gets executed **only** if the user's personal Sieve script does not exist. Although more than a single `default` storage can be defined, only the first one listed in the configuration is used. If `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` is set for this script storage, the default script can be seen and accessed by this name through ManageSieve (and doveadm sieve). See [Sieve visible default script](/main/core/plugins/sieve.html#visible-default-script). This is usually a global script, so be sure to pre-compile the specified script manually in that case using the sievec command line tool, as explained by `[sievec(1)](/main/core/man/sievec.1.html)`. #### `discard` The `discard` storage yields the sieve script that gets executed for any message that is about to be discarded; i.e., it is not delivered anywhere by the normal Sieve execution. Although more than a single `discard` storage can be defined, only the first one listed in the configuration is used. The `discard` storage is currently only applicable for message delivery. The script from the `discard` storage is only executed when the "implicit keep" is canceled, by e.g. the "discard" action, and no actions that deliver the message are executed. Delivery in this case means both local delivery to a mailbox and redirection to a remote recipient. This "discard script" can prevent discarding the message, by executing alternative actions. If the discard script does nothing, the message is still discarded as it would be when no discard script is configured. #### `global` A `global` storage is the source of `:global` include scripts for the Sieve include extension. Scripts are accessed by name, so if the storage yields only one script, a name must be defined for it; either implicitly by the storage driver or explicitly using `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)`. Multiple `global` storages can be configured and each storage will be queried in sequence to retrieve the requested script by name. The storages will be queried in the order these storages are defined in the configuration until the script is found. The order can be overridden by the `[sieve_script_precedence](/main/core/summaries/settings.html#sieve_script_precedence)` setting. ### Script storage drivers Sieve script storages are implemented as a storage driver. The default [file](#sieve_storage_file) storage driver uses the local filesystem. It can use a single script file or a directory containing several Sieve script files with a symbolic link pointing to the active script. More complex setups can use other storage drivers such as [ldap](#sieve_storage_ldap) or [dict](#sieve_storage_dict) to fetch Sieve scripts from LDAP databases or dict storages, respectively. The storage driver is configured using the `[sieve_script_driver](/main/core/summaries/settings.html#sieve_script_driver)` setting. If not explicitly configured for a `[sieve_script](/main/core/summaries/settings.html#sieve_script)` block, the storage driver is [file](/main/core/plugins/sieve.html#file-storage-driver) and the default directory is `~/sieve/` with a symbolic link at `~/.dovecot.sieve` pointing to the active script file in that directory. #### Common Settings All Sieve script storages support the following common settings: ### `sieve_script` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) Creates a new Sieve script storage to the list of script storages. The filter name refers to the `[sieve_script_storage](/main/core/summaries/settings.html#sieve_script_storage)` setting. Example: ``` sieve_script personal { [...] } ``` ### `sieve_script_bin_path` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) Points to the directory where the compiled binaries for this script location are stored. This directory is created automatically if possible. If this setting is not configured, the behavior depends on the storage driver. For the [file storage driver](/main/core/plugins/sieve.html#file-storage-driver), the binaries are for example stored in the same directory as the corresponding sieve scripts. Don't specify the same directory for multiple script storages (e.g. with different types), as this will result in undefined behavior. For one, the same script name could point to different scripts for different storages, leading to a conflict, because the storages will try to use the same binary file. Multiple mail users can share a single script directory if the associated script storage configuration is identical between users and all users share the same system credentials (uid, gid). All users will then use the same scripts for that storage type. ### `sieve_script_cause` **Default:** `delivery` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The causes for executing Sieve scripts from this storage. This is currently only relevant for the IMAPSieve plugin. For standard Sieve execution at message delivery the cause is "delivery". Other causes can be `append`, `copy` and `flag`. ### `sieve_script_driver` **Default:** `file` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The Sieve script storage driver to use. See [Script storage drivers](/main/core/plugins/sieve.html#script-storage-drivers). ### `sieve_script_name` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The (default) name of a Sieve script retrieved from this storage. If the name of the Sieve script cannot be derived somehow from the storage (e.g. from a file name) and the storage for a single script is specified, this option is required (e.g. for dict locations that must point to a particular script). If the name of the script is derived from the storage, the value of the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting overrides that name. If the Sieve interpreter explicitly queries for a specific name (e.g. to let the Sieve [Sieve include extension](/main/core/config/sieve/extensions/include.html) retrieve a script from the [global script storage](/main/core/plugins/sieve.html#script-storage-type-global)), this setting has no effect. For the Sieve script storage with type [default](/main/core/plugins/sieve.html#script-storage-type-default), the name is required to make the default script visible in ManageSieve. See '[Sieve visible default script](/main/core/plugins/sieve.html#visible-default-script)' ### `sieve_script_precedence` **Default:** `infinite` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The precedence of this Sieve storage in the configuration. Normally, script storages with matching type and cause are accessed in the order these are specified in the configuration. This setting can be used to configure an explicit order. Storages will be accessed with lower precedence first. ### `sieve_script_storage` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The identifier of the Sieve script storage. This is used only in configurations and by command line tools - it's not visible to the user. The `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter refers to this setting. ### `sieve_script_type` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage types](/main/core/plugins/sieve.html#script-storage-types) The type of the configured Sieve script storage. See [Script storage types](/main/core/plugins/sieve.html#script-storage-types). ### File storage driver The `file` script storage driver is used to retrieve Sieve scripts from the file system. This is the default type if the `[sieve_script_driver](/main/core/summaries/settings.html#sieve_script_driver)` setting is omitted. The path configured using the `[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` setting can either point to a directory or to a regular file. If the path points to a directory, a script called `name` is retrieved by reading a file from that directory with the file name `name.sieve`. When a script storage with type [personal](/main/core/plugins/sieve.html#script-storage-type-personal) is using the `file` driver and the`[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` points to a directory, a symbolic link points to the currently active script (the script executed at delivery). The active script can be modified by the user through ManageSieve and by the administrator using `[doveadm sieve activate]()`. The location of this symbolic link can be configured using the `[sieve_script_active_path](/main/core/summaries/settings.html#sieve_script_active_path)` setting. When a script storage with type [before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after) is using the `file` driver and `[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` points to a directory, all files in that directory with a `.sieve` extension are part of the sequence. The sequence order of the scripts in that directory is determined by the file names, using a normal 8-bit per-octet comparison. Unless overridden using the `[sieve_script_bin_path](/main/core/summaries/settings.html#sieve_script_bin_path)` setting, compiled binaries for scripts retrieved from a `file` script storage are by default stored in the same directory as where the script file was found if possible. #### Configuration The `file` storage driver supports all settings described in [Common Settings](#common-settings). Additionally, the following settings apply to this driver: ### `sieve_script_active_path` **Default:** `~/.dovecot.sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) When [ManageSieve server](/main/core/config/sieve/managesieve.html) is used, one script in the storage can be active; i.e., evaluated at delivery. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)`. For that storage driver, the active script in the storage directory is pointed to by a symbolic link. This setting configures where this symbolic link is located. If the `[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` setting points to a regular file, this setting has no effect (and ManageSieve cannot be used). ### `sieve_script_path` **Default:** `~/sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) A file system path pointing to a Sieve script file or a directory containing one or more Sieve script files with names structured as `.sieve`. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)` ##### `sieve_script_name` If the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting is not configured and the Sieve script is not retrieved by name (e.g. using the [include extension](/main/core/config/sieve/extensions/include.html) or by [ManageSieve](/main/core/config/sieve/managesieve.html)), the name defaults to the file name without the `.sieve` suffix. ##### `sieve_script_bin_path` If the `[sieve_script_bin_path](/main/core/summaries/settings.html#sieve_script_bin_path)` setting is not configured, the binaries are stored in the same directory as the corresponding sieve scripts by default. #### Example doveconf ``` sieve_script personal { driver = file path = ~/sieve active_path = ~/.dovecot.sieve } sieve_script default { type = default name = default driver = file path = /etc/dovecot/sieve/default/ } ``` ### Dict storage driver To retrieve a Sieve script from a [dictionary](/main/core/config/dict.html) database, two lookups are performed. First, the name of the Sieve script is queried from the dict path `/priv/sieve/name/`. If the Sieve script exists, this yields a data ID which in turn points to the actual script text. The script text is subsequently queried from the dict path `/priv/sieve/data/`. The second query is only necessary when no compiled binary is available or when the script has changed and needs to be recompiled. The data ID is used to detect changes in the dict's underlying database. Changing a Sieve script in the database must be done by first making a new script data item with a new data ID. Then, the mapping from name to data ID must be changed to point to the new script text, thereby changing the data ID returned from the name lookup, i.e. the first query mentioned above. Script binaries compiled from Sieve scripts contained in a dict database record the data ID. While the data ID contained in the binary is identical to the one returned from the dict lookup, the binary is assumed up-to-date. When the returned data ID is different, the new script text is retrieved using the second query and compiled into a new binary containing the updated data ID. #### Configuration The `dict` storage driver supports all settings described in [Common Settings](#common-settings). ##### `sieve_script_name` If the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting is not configured and the Sieve script is not retrieved by name (e.g. using the [include extension](/main/core/config/sieve/extensions/include.html) or by [ManageSieve](/main/core/config/sieve/managesieve.html)), the name defaults to `default`. ##### `sieve_script_bin_path` By default, compiled binaries are not stored at all for Sieve scripts retrieved from a dict database. Thus, the Sieve binaries will be compiled each time they are called. To improve performance, `[sieve_script_bin_path](/main/core/summaries/settings.html#sieve_script_bin_path)` should be specified to cache the compiled binaries on the local filesystem. For Example: doveconf ``` sieve_script personal { driver = dict name = keep bin_path = ~/.sieve-bin # or #bin_path=/var/sieve-scripts/%{user} dict file { path = /etc/dovecot/sieve.dict } } ``` TIP Sieve uses the ID number as its cache index and to detect the need to compile. Therefore, if a script is changed, then its ID must also be changed for it to be reloaded. #### Examples ##### Flat File Driver To retrieve the Sieve script named "keep" from the dict file /etc/dovecot/sieve.dict: dovecot.conf/etc/dovecot/sieve.dict doveconf ``` # Only the "keep" script will be used. sieve_script personal { driver = dict name = keep dict file { path = /etc/dovecot/sieve.dict } } ``` ``` priv/sieve/name/keep 1 priv/sieve/name/discard 2 priv/sieve/data/1 keep; priv/sieve/data/2 discard; ``` A more advanced example using the same config as above: notify an external email address when new mail has arrived. Note that the script all needs to be on one line. /etc/dovecot/sieve.dict ``` priv/sieve/name/notify 5 priv/sieve/data/5 require ["enotify", "variables"]; if header :matches "From" "*" { set "from" "${1}";} notify :importance "3" :message "New email from ${from}" "mailto:other@domain.com?body=New%20email%20has%20arrived."; ``` ##### Using a SQL Driver For greater flexibility, it's possible to use a SQL driver for your dict scripts. First, set up a configuration file (such as `/etc/dovecot/dict-sieve-sql.conf.inc`) with your database configuration. This should consist of the following parts: First, set up a configuration file (such as `/etc/dovecot/dict-sieve-sql.conf`) with your database configuration. Next, create a dict proxy service (in `dovecot.conf`). Finally, configure Sieve to check the dict to lookup up a script called "active" in the database: /etc/dovecot/dict-sieve-sql.conf.incdovecot.conf doveconf ``` # The name mapping that yields the ID of the Sieve script # The name of the script, as per the "sieve" config parameter dict_map priv/sieve/name/$script_name { # The database table sql_table = user_sieve_scripts # The username field in the table to query username_field = username # The field which contains the return value of the script ID value_field id { } # The script name field in the table to query key_field script_name { pattern = $script_name } } # The name mapping that yields the script content from ID # The ID, obtained from above dict_map priv/sieve/data/$id { # The database table sql_table = user_sieve_scripts # The username field in the table to query username_field = username # The field which contains the script value_field script_data { } # The id field in the table to query fields id { id = $id } } ``` doveconf ``` dict_server { dict sieve { driver = sql sql_driver = pgsql pgsql localhost { parameters { dbname = dovecot user = dovecot password = password } } !include /etc/dovecot/dict-sieve-sql.conf.inc } } # dict lookup sieve_script personal { driver = dict name = active dict proxy { name = sieve } } ``` As with the flat file, the database query will need to return the Sieve script all in one line, otherwise the subsequent lines will be ignored. INFO You might need to configure the [dict proxy permissions](/main/core/config/dict.html#dictionary-proxy-process). ### LDAP storage driver The `ldap` storage driver is used to retrieve Sieve scripts from an LDAP database. To retrieve a Sieve script from the LDAP database, at most two lookups are performed. First, the LDAP entry containing the Sieve script is searched using the specified LDAP search filter. If the LDAP entry changed since it was last retrieved (or it was never retrieved before), the attribute containing the actual Sieve script is retrieved in a second lookup. In the first lookup, a special attribute is read and checked for changes. Usually, this is the `modifyTimestamp` attribute, but an alternative can be configured. Depending on how Pigeonhole was configured and compiled (refer to INSTALL file for more information), LDAP support may only be available when a plugin called `sieve_storage_ldap` is loaded. #### Configuration The `ldap` storage driver supports all settings described in [Common Settings](#common-settings). The following settings apply to this script storage driver: ### `ldap_auth_dn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specify the Distinguished Name (the username used to login to the LDAP server). Leave it commented out to bind anonymously (useful with `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`). Example: `ldap_auth_dn = uid=dov-read,dc=example,dc=com,dc=.` ### `ldap_auth_dn_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for LDAP server. Used if `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is specified. ### `ldap_auth_sasl_authz_id` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL authorization ID, ie. the `[ldap_auth_dn_password](/main/core/summaries/settings.html#ldap_auth_dn_password)` is for this "master user", but the `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is still the logged in user. Normally you want to keep this empty. ### `ldap_auth_sasl_mechanisms` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of SASL mechanism names to use. ### `ldap_auth_sasl_realm` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL realm to use. ### `ldap_base` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP base. `[Settings variables](/main/core/settings/variables.html)` can be used. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the variable. Examples: - `ldap_base = dc=mail, dc=example, dc=org` - `ldap_base = %{passdb:classOfServiceDN | safe}` ### `ldap_debug_level` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP library debug level as specified by `LDAP_DEBUG_*` in `ldap_log.h`. Value `-1` means everything. You may need to recompile OpenLDAP with debugging enabled to get enough output. ### `ldap_deref` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``searching``finding``always` Specify dereference which is set as an LDAP option. ### `ldap_scope` **Default:** `subtree` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `base``onelevel``subtree` This specifies the search scope. ### `ldap_starttls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set to `yes` to use TLS to connect to the LDAP server. ### `ldap_uris` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP URIs to use. Configure this setting to specify what LDAP server(s) to connect to. The URIs are in syntax `protocol://host:port`. Example: `ldap_uris = ldaps://secure.domain.org` ### `ldap_version` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP protocol version to use. Likely `2` or `3`. ### `sieve_script_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The LDAP search filter that is used to find the entry containing the Sieve script. These variables can be used: | Variable | Description | | --- | --- | | `%{user}` | username | | `%{user | username}` | user part in user@domain, same as `%{user}` if there's no domain | | `%{user | domain}` | domain part in user@domain, empty if user there's no domain | | `%{home}` | user's home directory | | `%{name}` | name of the Sieve script | ### `sieve_script_ldap_modified_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The name of the attribute used to detect modifications to the LDAP entry. ### `sieve_script_ldap_script_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The name of the attribute containing the Sieve script. ##### `sieve_script_name` If the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting is not configured and the Sieve script is not retrieved by name (e.g. using the [include extension](/main/core/config/sieve/extensions/include.html) or by [ManageSieve](/main/core/config/sieve/managesieve.html)), the name defaults to `default`. ##### `sieve_script_bin_path` By default, compiled binaries are not stored at all for Sieve scripts retrieved from LDAP. Thus, the Sieve binaries will be compiled each time they are called. To improve performance, `[sieve_script_bin_path](/main/core/summaries/settings.html#sieve_script_bin_path)` should be specified to cache the compiled binaries on the local filesystem. For Example: doveconf ``` sieve_script personal { driver = ldap name = keep bin_path = ~/.sieve-bin # or #bin_path = /var/sieve-scripts/%{user} # LDAP settings: ... } ``` TIP Sieve uses the LDAP entry configured using `[sieve_script_ldap_modified_attribute](/main/core/summaries/settings.html#sieve_script_ldap_modified_attribute)` to detect the need to compile. Therefore, if a script is changed, then this entry must also be changed for it to be reloaded. Depending on which LDAP entry is configured, this can happen implicitly by the LDAP database itself (which is normally the case for the default `modifyTimestamp` entry). #### Example If support for the `ldap` script storage driver is compiled as a plugin, it needs to be added to the `[sieve_plugins](/main/core/summaries/settings.html#sieve_plugins)` setting before it can be used, e.g.: doveconf ``` sieve_plugins { sieve_storage_ldap = yes } ``` dovecot.conf doveconf ``` sieve_script personal { driver = ldap bin_path = ~/.sieve-bin/ # Don't use privileged LDAP credentials here as these may likely leak. Only # search and read access is required. # Space separated list of LDAP URIs to use. ldap_uris = ldap://localhost # Distinguished Name - the username used to login to the LDAP server. # Leave it commented out to bind anonymously. ldap_auth_dn = cn=sieve,ou=Programs,dc=example,dc=org # Password for LDAP server, if dn is specified. ldap_auth_dnpassword = secret # LDAP base ldap_base = dc=mail,dc=example,dc=org # Dereference: never, searching, finding, always ldap_deref = never # Search scope: base, onelevel, subtree ldap_scope = subtree # Filter for user lookup. Some variables can be used: # %{user} - username # %{user | username} - user part in user@domain, same as %{user} if there's no domain # %{user | domain} - domain part in user@domain, empty if there's no domain # %{name} - name of the Sieve script ldap_filter = (&(objectClass=posixAccount)(uid=%{user})) # Attribute containing the Sieve script ldap_script_attribute = mailSieveRuleSource # Attribute used for modification tracking ldap_modified_attribute = modifyTimestamp } ``` ## Settings ### `managesieve_client_workarounds` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | Enables various workarounds for ManageSieve clients. Currently there are none. ### `managesieve_implementation_string` | Key 1 | | --- | | Default | `Dovecot Pigeonhole` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Sets the ManageSieve implementation string returned by the `IMPLEMENTATION` capability. ### `managesieve_logout_format` **Default:** `bytes=%{input}/%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) Specifies the string pattern used to compose the logout message of an authenticated session. The following substitutions are available: | Variable Name | Description | | --- | --- | | `%{input}` | Total number of bytes read from client | | `%{output}` | Total number of bytes sent to client | | `%{put_count}` | Number of scripts uploaded by client using PUTSCRIPT command | | `%{put_bytes}` | Number of bytes with script data sent by client using PUTSCRIPT command | | `%{get_count}` | Number of scripts downloaded by client using GETSCRIPT command | | `%{get_bytes}` | Number of bytes with script data sent to client using GETSCRIPT command | | `%{check_count}` | Number of scripts checked by client using CHECKSCRIPT command | | `%{check_bytes}` | Number of bytes with script data sent by client using CHECKSCRIPT command | | `%{deleted_count}` | Number of scripts deleted by client using DELETESCRIPT command | | `%{renamed_count}` | Number of scripts renamed by client using RENAMESCRIPT command | | `%{session}` | The client session ID | ### `managesieve_max_compile_errors` | Key 1 | | --- | | Default | `5` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | The maximum number of compile errors that are returned to the client upon script upload or script verification. ### `managesieve_max_line_length` | Key 1 | | --- | | Default | `64k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The maximum ManageSieve command line length in bytes. Since long command lines are very unlikely with ManageSieve, changing this will generally not be useful. ### `managesieve_notify_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `NOTIFY` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). ### `managesieve_sieve_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `SIEVE` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). ### `recipient_delimiter` **Default:** `+` **Value:** [string](/main/core/settings/types.html#string) The separator between the :user and :detail address parts. ### `sieve_duplicate_default_period` | Key 1 | | --- | | Default | `14d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Advanced Setting; this should not normally be changed. | Default period after which tracked values are purged from the duplicate tracking database. ### `sieve_duplicate_max_period` | Key 1 | | --- | | Default | `7d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Advanced Setting; this should not normally be changed. | Maximum period after which tracked values are purged from the duplicate tracking database. ### `sieve_editheader_header` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` - `[sieve_editheader_header_forbid_add](/main/core/summaries/settings.html#sieve_editheader_header_forbid_add)` - `[sieve_editheader_header_forbid_delete](/main/core/summaries/settings.html#sieve_editheader_header_forbid_delete)` Configures a new message header, which can be forbidden to be added or deleted. The filter name refers to the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` setting. ### `sieve_editheader_header_forbid_add` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` Forbid adding the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` to the message header. Addition of the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, adding this header to this setting has no effect. ### `sieve_editheader_header_forbid_delete` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` Forbid deleting the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` from the message header. Deleting the `Received:` and `Auto-Submitted:` fields is always forbidden, while removing the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, using this setting with one of these headers has no effect. ### `sieve_editheader_header_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` The name of the message header that is forbidden to be added or deleted. ### `sieve_editheader_max_header_size` **Default:** `2k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) The maximum size in bytes of a header field value passed to the addheader command. The minimum value for this setting is `1024` bytes. ### `sieve_extensions` **Default:** `` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The Sieve language extensions available to users. By default, all supported extensions are available, except for deprecated extensions, extensions that add the ability to change messages, extensions that require explicit configuration, or extensions that are still under development. Some system administrators may want to disable certain Sieve extensions or enable those that are not available by default. Supported extensions are listed at [Sieve extensions](/main/core/config/sieve/overview.html#extensions). Example: ``` # Enable the vacation-seconds extension in addition to all # extensions enabled by default. sieve_extensions { vacation-seconds = yes } ``` ### `sieve_extlists_list` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) This named list filter creates a new list definition for the ":list" match type in Sieve (extlists extension). The filter name refers to the `[sieve_extlists_list_name](/main/core/summaries/settings.html#sieve_extlists_list_name)` setting. That is the name of the list as it will be accessible from the Sieve. A dict definition that is placed inside the scope of this list filter will serve as the lookup for this extlists list. So, values from Sieve are looked up in the defined dict. This is often used to define an address book or whitelist external to the Sieve script. If no dict is present, the list will function as an empty list. Example: ``` sieve_extlists_list :addrbook:default { dict proxy { name = addressbook } } ``` ### `sieve_extlists_list_max_lookup_size` **Default:** `1k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) The maximum size of a value looked up from this Sieve extlists list. If the value is too large, the lookup will be skipped and will yield no result. Note that individual lists may have different limits, meaning that lookups in other lists may still succeed. ### `sieve_extlists_list_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) The name of the list as it will be accessible through the Sieve ":list" match type (extlists extension). According to the standard, this name must be a valid URI. This implementation further limits that to a valid URN or TAG URI. Note that the standard URN and TAG equality rules dictate that at least parts of these URIs are case-sensitive, so it is best to consider the configured name case-sensitive entirely. The `[sieve_extlists_list](/main/core/summaries/settings.html#sieve_extlists_list)` filter refers to this setting. ### `sieve_global_extensions` **Default:** `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Which Sieve language extensions are **only** available in global scripts. This can be used to restrict the use of certain Sieve extensions to administrator control, for instance when these extensions can cause security concerns. This setting has higher precedence than `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`, meaning that the extensions enabled with this setting are never available to the user's personal script no matter what is specified for the `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting. The syntax of this setting is identical to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`, with the difference that extensions are enabled or disabled for exclusive use in global scripts. Currently, no extensions are marked as such by default. ### `sieve_implicit_extensions` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) WARNING Do not use this setting unless you really need to! The Sieve language extensions implicitly available to users. The extensions listed in this setting do not need to be enabled explicitly using the Sieve "require" command. This behavior directly violates the Sieve standard, but can be necessary for compatibility with some existing implementations of Sieve (notably jSieve). The syntax and semantics of this setting are otherwise identical to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. ### `sieve_include_max_includes` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Advanced Setting; this should not normally be changed. | The maximum number of scripts that may be included. This is the total number of scripts involved in the include tree. ### `sieve_include_max_nesting_depth` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Advanced Setting; this should not normally be changed. | The maximum nesting depth for the include tree. ### `sieve_max_actions` **Default:** `32` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` - `[sieve_notify_max_notifications](/main/core/summaries/settings.html#sieve_notify_max_notifications)` The maximum number of actions that can be performed during a single script execution. If set to `0`, no limit on the total number of actions is enforced. This is the total across all action classes. Per-class limits such as `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` and `[sieve_notify_max_notifications](/main/core/summaries/settings.html#sieve_notify_max_notifications)` apply on top: the effective per-class limit is `min(per_class_setting, sieve_max_actions)`. When the total cap is hit first, execution aborts with "total number of actions exceeds policy limit". ### `sieve_max_cpu_time` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) **Changes:** - Changed: 2.4.5 Changed from `0` (unlimited) to `30s`. Previously, `30s` was the default only for the `sieve_env_location_ms` (IMAP) filter; it now applies globally including MDA execution. The maximum amount of CPU time that a Sieve script is allowed to use while executing. If the execution exceeds this resource limit, the script ends with an error, causing the implicit "keep" action to be executed. This limit is not only enforced for a single script execution, but also cumulatively for the last executions within a configurable timeout (see `[sieve_resource_usage_timeout](/main/core/summaries/settings.html#sieve_resource_usage_timeout)`). ### `sieve_max_redirects` **Default:** `4` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` The maximum number of redirect actions that can be performed during a single script execution. `0` means redirect is prohibited. The effective per-script limit on `redirect` actions is `min(sieve_max_redirects, sieve_max_actions)`: when the total number of actions in a script reaches `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` (default `32`), execution aborts with "total number of actions exceeds policy limit" before this per-class limit is reached. To allow more than `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` redirect actions, raise that setting as well. ### `sieve_max_script_size` | Key 1 | | --- | | Default | `1M` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The maximum size of a Sieve script. The compiler will refuse to compile any script larger than this limit. If set to `0`, no limit on the script size is enforced. ### `sieve_notify_mailto_envelope_from` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[sieve_redirect_envelope_from](/main/core/summaries/settings.html#sieve_redirect_envelope_from)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) Defines the source of the notification sender address for e-mail notifications. ### `sieve_notify_mailto_max_headers` | Key 1 | | --- | | Default | `16` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_recipients](/main/core/summaries/settings.html#sieve_notify_mailto_max_recipients)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit the number of additional headers per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of additional headers a single `mailto:` notify action may specify in its URI. `0` means no limit on the number of headers is enforced. ### `sieve_notify_mailto_max_recipients` | Key 1 | | --- | | Default | `8` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_headers](/main/core/summaries/settings.html#sieve_notify_mailto_max_headers)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit the number of recipients per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of recipients a single `mailto:` notify action may specify, counted across the URI and any `to`/`cc`/`bcc` headers. `0` means no limit on the number of recipients is enforced. ### `sieve_notify_max_notifications` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` - `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Changes | - Added: 2.4.5 Setting to limit maximum number of notifications per script is added. | | Advanced Setting; this should not normally be changed. | The maximum number of `notify` actions ([RFC 5435](https://datatracker.ietf.org/doc/html/rfc5435)) that can be performed during a single script execution. `0` means `notify` is prohibited; scripts using the `notify` command will fail to compile with "local policy prohibits the use of a notify action". Note that this is independent of `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)`; the `redirect` action and the `notify` mailto method are governed by separate limits. The effective per-script limit on `notify` actions is `min(sieve_notify_max_notifications, sieve_max_actions)`: when the total number of actions in a script reaches `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` (default `32`), execution aborts with "total number of actions exceeds policy limit" before this per-class limit is reached. To allow more than `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` notify actions, raise that setting as well. ### `sieve_plugins` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The Pigeonhole Sieve interpreter can have plugins of its own. Using this setting, the used plugins can be specified. Check [Sieve plugins](/main/core/config/sieve/overview.html#extensions) for available plugins. ### `sieve_quota_script_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of personal Sieve scripts a single user can have. ### `sieve_quota_storage_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) The maximum amount of disk storage a single user's scripts may occupy. ### `sieve_redirect_envelope_from` **Default:** `sender` **Value:** [string](/main/core/settings/types.html#string) Specifies what envelope sender address is used for redirected messages. Normally, the Sieve `redirect` command copies the sender address for the redirected message from the processed message So, the redirected message appears to originate from the original sender. The following options are supported for this setting: | Option | Description | | --- | --- | | `sender` | The sender address is used | | `recipient` | The final recipient address is used | | `orig_recipient` | The original recipient is used | | `user_email` | The user's primary address is used. This is configured with the `[sieve_user_email](/main/core/summaries/settings.html#sieve_user_email)` setting. If that setting is not configured, `user_email` is equal to `sender`. | | `postmaster` | The `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)` configured for LDA/LMTP. | | `` | Redirected messages are always sent from `user@domain`. The angle brackets are mandatory. The null `<>` address is also supported. | When the envelope sender of the processed message is the null address `<>`, the envelope sender of the redirected message is also always `<>`, irrespective of what is configured for this setting. ### `sieve_resource_usage_timeout` **Default:** `1h` **Value:** [time](/main/core/settings/types.html#time) To prevent abuse, the Sieve interpreter can record resource usage of a Sieve script execution in the compiled binary if it is significant. Currently, this happens when CPU system + user time exceeds 1.5 seconds for one execution. Such high resource usage is summed over time in the binary and once that cumulative resource usage exceeds the limits (`[sieve_max_cpu_time](/main/core/summaries/settings.html#sieve_max_cpu_time)`), the Sieve script is disabled in the binary for future execution, even if an individual execution exceeded no limits. If the last time high resource usage was recorded is older than `[sieve_resource_usage_timeout](/main/core/summaries/settings.html#sieve_resource_usage_timeout)`, the resource usage in the binary is reset. This means that the Sieve script is only disabled when the limits are cumulatively exceeded within this timeout. With the default configuration this means that the Sieve script is only disabled when the total CPU time of Sieve executions that lasted more than 1.5 seconds exceeds 30 seconds in the last hour. A disabled Sieve script can be reactivated by the user by uploading a new version of the Sieve script after the excessive resource usage times out. An administrator can force reactivation by forcing a script compile (e.g. using the sievec command line tool). ### `sieve_script` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) Creates a new Sieve script storage to the list of script storages. The filter name refers to the `[sieve_script_storage](/main/core/summaries/settings.html#sieve_script_storage)` setting. Example: ``` sieve_script personal { [...] } ``` ### `sieve_script_active_path` **Default:** `~/.dovecot.sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) When [ManageSieve server](/main/core/config/sieve/managesieve.html) is used, one script in the storage can be active; i.e., evaluated at delivery. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)`. For that storage driver, the active script in the storage directory is pointed to by a symbolic link. This setting configures where this symbolic link is located. If the `[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` setting points to a regular file, this setting has no effect (and ManageSieve cannot be used). ### `sieve_script_bin_path` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) Points to the directory where the compiled binaries for this script location are stored. This directory is created automatically if possible. If this setting is not configured, the behavior depends on the storage driver. For the [file storage driver](/main/core/plugins/sieve.html#file-storage-driver), the binaries are for example stored in the same directory as the corresponding sieve scripts. Don't specify the same directory for multiple script storages (e.g. with different types), as this will result in undefined behavior. For one, the same script name could point to different scripts for different storages, leading to a conflict, because the storages will try to use the same binary file. Multiple mail users can share a single script directory if the associated script storage configuration is identical between users and all users share the same system credentials (uid, gid). All users will then use the same scripts for that storage type. ### `sieve_script_cause` **Default:** `delivery` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The causes for executing Sieve scripts from this storage. This is currently only relevant for the IMAPSieve plugin. For standard Sieve execution at message delivery the cause is "delivery". Other causes can be `append`, `copy` and `flag`. ### `sieve_script_driver` **Default:** `file` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The Sieve script storage driver to use. See [Script storage drivers](/main/core/plugins/sieve.html#script-storage-drivers). ### `sieve_script_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The LDAP search filter that is used to find the entry containing the Sieve script. These variables can be used: | Variable | Description | | --- | --- | | `%{user}` | username | | `%{user | username}` | user part in user@domain, same as `%{user}` if there's no domain | | `%{user | domain}` | domain part in user@domain, empty if user there's no domain | | `%{home}` | user's home directory | | `%{name}` | name of the Sieve script | ### `sieve_script_ldap_modified_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The name of the attribute used to detect modifications to the LDAP entry. ### `sieve_script_ldap_script_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) The name of the attribute containing the Sieve script. ### `sieve_script_name` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The (default) name of a Sieve script retrieved from this storage. If the name of the Sieve script cannot be derived somehow from the storage (e.g. from a file name) and the storage for a single script is specified, this option is required (e.g. for dict locations that must point to a particular script). If the name of the script is derived from the storage, the value of the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting overrides that name. If the Sieve interpreter explicitly queries for a specific name (e.g. to let the Sieve [Sieve include extension](/main/core/config/sieve/extensions/include.html) retrieve a script from the [global script storage](/main/core/plugins/sieve.html#script-storage-type-global)), this setting has no effect. For the Sieve script storage with type [default](/main/core/plugins/sieve.html#script-storage-type-default), the name is required to make the default script visible in ManageSieve. See '[Sieve visible default script](/main/core/plugins/sieve.html#visible-default-script)' ### `sieve_script_path` **Default:** `~/sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) A file system path pointing to a Sieve script file or a directory containing one or more Sieve script files with names structured as `.sieve`. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)` ### `sieve_script_precedence` **Default:** `infinite` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The precedence of this Sieve storage in the configuration. Normally, script storages with matching type and cause are accessed in the order these are specified in the configuration. This setting can be used to configure an explicit order. Storages will be accessed with lower precedence first. ### `sieve_script_storage` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) The identifier of the Sieve script storage. This is used only in configurations and by command line tools - it's not visible to the user. The `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter refers to this setting. ### `sieve_script_type` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage types](/main/core/plugins/sieve.html#script-storage-types) The type of the configured Sieve script storage. See [Script storage types](/main/core/plugins/sieve.html#script-storage-types). ### `sieve_spamtest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. The syntax is identical to the `[sieve_spamtest_status_header](/main/core/summaries/settings.html#sieve_spamtest_status_header)` setting. This setting cannot be used together with `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. ### `sieve_spamtest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_spamtest_score_max_header](/main/core/summaries/settings.html#sieve_spamtest_score_max_header)`. This setting can specify a fractional score with a decimal point. ### `sieve_spamtest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. ### `sieve_spamtest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_spamtest_text_value](/main/core/summaries/settings.html#sieve_spamtest_text_value)`). ### `sieve_spamtest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) When the `[sieve_spamtest_status_type](/main/core/summaries/settings.html#sieve_spamtest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_spamtest_status_header = X-Spam-Verdict sieve_spamtest_status_type = text sieve_spamtest_text_value { 1 = Not Spam 10 = Spam } ``` ### `sieve_trace_addresses` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) Enables showing byte code addresses in the trace output, rather than only the source line numbers. ### `sieve_trace_debug` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) Enables highly verbose debugging messages that are usually only useful for developers. ### `sieve_trace_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) The directory where trace files are written. Trace debugging is disabled if this setting is not configured or if the directory does not exist. If the path is relative or it starts with `~/` it is interpreted relative to the current user's home directory. ### `sieve_trace_level` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) The verbosity level of the trace messages. Trace debugging is disabled if this setting is not configured. Options are: | Option | Description | | --- | --- | | `actions` | Only print executed action commands, like keep, fileinto, reject, and redirect. | | `commands` | Print any executed command, excluding test commands. | | `tests` | Print all executed commands and performed tests. | | `matching` | Print all executed commands, performed tests and the values matched in those tests. | ### `sieve_user_email` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The primary e-mail address for the user. This is used as a default when no other appropriate address is available for sending messages. If this setting is not configured, either the postmaster or null `<>` address is used as a sender, depending on the action involved. This setting is important when there is no message envelope to extract addresses from, such as when the script is executed in IMAP. ### `sieve_user_log_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The path to the file where the user log file is written. A default location is used if this setting is not explicitly configured: - If the main user's personal Sieve script storage (as configured with `[sieve_script](/main/core/summaries/settings.html#sieve_script)` uses the [File storage driver](/main/core/plugins/sieve.html#file-storage-driver), the logfile is set to `.log` by default. - If the script is not stored as a file, the default user log file is `~/.dovecot.sieve.log`. ### `sieve_vacation_check_recipient` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This setting determines whether the checks for implicit delivery are performed. If this is skipped, this means that the vacation command does not verify that the message is explicitly addressed at the recipient. Use this option with caution. Specifying `no` will violate the Sieve standards and can cause vacation replies to be sent for messages not directly addressed at the recipient. ### `sieve_vacation_default_period` **Default:** `7d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the default period that is used when no `:days` or `:seconds` tag is specified. The configured value must lie between `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)` and `[sieve_vacation_max_period](/main/core/summaries/settings.html#sieve_vacation_max_period)`. ### `sieve_vacation_max_period` **Default:** `60d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the maximum period that can be specified for the `:days` tag of the vacation command. The configured value must be larger than `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)`. A value of `0` has a special meaning: it indicates that there is no upper limit. ### `sieve_vacation_min_period` **Default:** `1d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) Specifies the minimum period that can be specified for the `:days` and `:seconds` tags of the vacation command. A minimum of `0` indicates that users are allowed to make the Sieve interpreter send a vacation response message for every incoming message that meets the other reply criteria (refer to [RFC 5230](https://datatracker.ietf.org/doc/html/rfc5230)). A value of zero is not recommended. ### `sieve_vacation_send_from_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This setting determines whether vacation messages are sent with the SMTP `MAIL FROM` envelope address set to the recipient address of the Sieve script owner. Normally this is set to `<>`, which is the default as recommended in the specification. This is meant to prevent mail loops. However, there are situations for which a valid sender address is required and this setting can be used to accommodate for those. ### `sieve_vacation_use_original_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) This specifies whether the original envelope recipient should be used in the check for implicit delivery. The vacation command checks headers of the incoming message, such as `To:` and `Cc:` for the address of the recipient, to verify that the message is explicitly addressed at the recipient. If the recipient address is not found, the vacation action will not trigger a response to prevent sending a reply when it is not appropriate. Normally only the final recipient address is used in this check. This setting allows including the original recipient specified in the SMTP session if available. This is useful to handle mail accounts with aliases. Use this option with caution: if you are using aliases that point to more than a single account, as senders can get multiple vacation responses for a single message. Use the [LDA](/main/core/config/delivery/lda.html) `-a` option or the LMTP/LDA `[lda_original_recipient_header](/main/core/summaries/settings.html#lda_original_recipient_header)` setting to make the original SMTP recipient available to Sieve. ### `sieve_variables_max_scope_count` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Advanced Setting; this should not normally be changed. | The maximum number of variables that can be declared in a scope. There are currently two variable scopes: the normal script scope and the global scope created by the [Sieve include extension](/main/core/config/sieve/extensions/include.html). The minimum value for this setting is `128`. ### `sieve_variables_max_value_size` | Key 1 | | --- | | Default | `4k` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Advanced Setting; this should not normally be changed. | The maximum allowed size for the value of a variable. If exceeded at runtime, the value is always truncated to the configured maximum. The minimum value for this setting is `4000 bytes`. ### `sieve_virustest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value Format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. The syntax is identical to `[sieve_virustest_status_header](/main/core/summaries/settings.html#sieve_virustest_status_header)`. This setting cannot be used together with `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. ### `sieve_virustest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_virustest_score_max_header](/main/core/summaries/settings.html#sieve_virustest_score_max_header)`. This setting can specify a fractional score with a decimal point. ### `sieve_virustest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) Value Format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. ### `sieve_virustest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_virustest_text_value](/main/core/summaries/settings.html#sieve_virustest_text_value)`). ### `sieve_virustest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) When the `[sieve_virustest_status_type](/main/core/summaries/settings.html#sieve_virustest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_virustest_status_header = X-VirusCheck sieve_virustest_status_type = text sieve_virustest_text_value { 1 = Clean 2 = Presumed Clean 3 = Not sure 4 = Almost Certain 5 = Definitely } ``` ### Extension-specific Configuration Sieve language extensions may have specific configuration. See [Sieve extensions](/main/core/config/sieve/overview.html#extensions) for a list of extensions and links to their configuration pages. ### Per-user Sieve script location By default, the Dovecot Sieve plugin looks for the user's Sieve script file in the user's home directory (`~/.dovecot.sieve`). This requires that the [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) is set for the user. If you want to store the script elsewhere, you can override the default by configuring a [personal script storage](/main/core/plugins/sieve.html#script-storage-type-personal). This can be done in two ways: 1. Define the full `[sieve_script](/main/core/summaries/settings.html#sieve_script)` block in dovecot configuration. 2. Return the user-specific settings as a extra fields from [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). For example, to use a Sieve script file named `.sieve` in `/var/sieve-scripts`, use: doveconf ``` sieve_script personal { path = /var/sieve-scripts/%{user}.sieve } ``` You may use templates like `%{user}`, as shown in the example. See `[Settings variables](/main/core/settings/variables.html)`. A relative path (or just a filename) will be interpreted to point under the user's home directory. ## Executing Multiple Scripts Sequentially The Dovecot Sieve plugin allows executing multiple Sieve scripts sequentially. The extra scripts can be executed before and after the user's private script. For example, this allows executing global Sieve policies before the user's script. See the [before](/main/core/plugins/sieve.html#script-storage-type-before) and [after](/main/core/plugins/sieve.html#script-storage-type-after) Sieve storage types for details on how to configure the execution sequence. The script execution ends when the currently executing script in the sequence does not yield a "keep" result: when the script terminates, the next script is only executed if an implicit or explicit "keep" is in effect. INFO To end all script execution, a script MUST NOT execute keep and it MUST cancel the implicit keep. For example, executing: `discard; stop;`. This means that the command `keep;` has different semantics when used in a sequence of scripts. For normal Sieve execution, `keep;` is equivalent to `fileinto "INBOX";`, because both cause the message to be stored in INBOX. However, in sequential script execution, it only controls whether the next script is executed. Storing the message into INBOX (the default folder) is not done until the last script in the sequence executes (implicit) keep. To force storing the message into INBOX earlier in the sequence, the fileinto command can be used (with `:copy` or together with `keep;`). Apart from the `keep` action, all actions triggered in a script in the sequence are executed before continuing to the next script. This means that when a script in the sequence encounters an error, actions from earlier executed scripts are not affected. The sequence is broken however, meaning that the script execution of the offending script is aborted and no further scripts are executed. An implicit keep is executed instead. Just as for executing a single script the normal way, the Dovecot Sieve plugin takes care never to duplicate deliveries, forwards or responses. When vacation actions are executed multiple times in different scripts, the usual error is not triggered: the subsequent duplicate vacation actions are simply discarded. For example: doveconf ``` # Global scripts executed before the user's personal script. # E.g. handling messages marked as dangerous sieve_script before1 { type = before path = /var/lib/dovecot/sieve/discard-viruses.sieve } # Domain-level scripts retrieved from LDAP sieve_script before2 { type = before name = ldap-domain driver = ldap # ldap settings here: ... } # User-specific scripts executed before the user's personal script. # E.g. a vacation script managed through a non-ManageSieve GUI. sieve_script before3 { type = before path = /var/vmail/%{user | domain}/%{user | username}/sieve-before } # User-specific scripts executed after the user's personal script. # (if keep is still in effect) # E.g. user-specific default mail filing rules sieve_script after1 { type = after path = /var/vmail/%{user | domain}/%{user | username}/sieve-after } # Global scripts executed after the user's personal script # (if keep is still in effect) # E.g. default mail filing rules. sieve_script after2 { type = after path = /var/lib/dovecot/sieve/after.d/ } ``` TIP Be sure to manually pre-compile the scripts specified by [before](/main/core/plugins/sieve.html#script-storage-type-before) and [after](/main/core/plugins/sieve.html#script-storage-type-after) Sieve storage types by using the [sievec](/main/core/config/sieve/overview.html#manually-compiling-sieve-scripts) tool. ## Visible Default Script The [default](/main/core/plugins/sieve.html#script-storage-type-default) Sieve storage type specifies the location of a default script that is executed when the user has no active personal script. Normally, this default script is invisible to the user; i.e., it is not listed in [ManageSieve server](/main/core/config/sieve/managesieve.html). To give the user the ability to see and read the default script, it is possible to make it visible under a specific configurable name using `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` in the `[sieve_script](/main/core/summaries/settings.html#sieve_script)` block of the [default](/main/core/plugins/sieve.html#script-storage-type-default) storage setting. The [default](/main/core/plugins/sieve.html#script-storage-type-default) storage needs to point to a valid script location as well for this to work: if the default script does not exist at the indicated location, it is not shown. ManageSieve will magically list the default script under that name, even though it does not actually exist in the user's normal personal script storage. This way, the ManageSieve client can see that it exists and it can retrieve its contents. If no normal script is active, the default is always listed as active. The user can replace the default with a custom script, by uploading it under the default script's name. If that custom script is ever deleted, the default script will reappear from the shadows implicitly. This way, ManageSieve clients will not need any special handling for this feature. If the name of the default script is equal to the name the client uses for the main script, it will initially see and read the default script when the user account is freshly created. The user can edit the script, and when the edited script is saved through the ManageSieve client, it will override the default script. If the user ever wants to revert to the default, the user only needs to delete the edited script and the default will reappear. doveconf ``` sieve_script personal { path = ~/sieve active_path = ~/.dovecot.sieve } sieve_script default { type = default name = roundcube path = /var/lib/dovecot/sieve/default.sieve } ``` ## Trace Debugging Trace debugging provides detailed insight in the operations performed by the Sieve script. Messages about what the Sieve script is doing are written to the specified directory. WARNING On a busy server, this functionality can quickly fill up the trace directory with a lot of trace files. Enable this only temporarily and as selective as possible; e.g., enable this only for a few users by returning the settings below from userdb as [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) rather than enabling these for everyone. ### Settings These settings apply to both the Sieve plugin and [imap-sieve plugin](/main/core/plugins/imap_sieve.html). #### `sieve_trace_addresses` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) Enables showing byte code addresses in the trace output, rather than only the source line numbers. #### `sieve_trace_debug` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) Enables highly verbose debugging messages that are usually only useful for developers. #### `sieve_trace_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) The directory where trace files are written. Trace debugging is disabled if this setting is not configured or if the directory does not exist. If the path is relative or it starts with `~/` it is interpreted relative to the current user's home directory. #### `sieve_trace_level` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) The verbosity level of the trace messages. Trace debugging is disabled if this setting is not configured. Options are: | Option | Description | | --- | --- | | `actions` | Only print executed action commands, like keep, fileinto, reject, and redirect. | | `commands` | Print any executed command, excluding test commands. | | `tests` | Print all executed commands and performed tests. | | `matching` | Print all executed commands, performed tests and the values matched in those tests. | # Trash Plugin (`trash`) Normally, a quota exceeded error is returned if saving/copying a message would bring the user over quota. With the trash plugin, the oldest messages are instead expunged from the specified mailboxes until the message can be saved. If the new message is large enough that it wouldn't fit even if all messages from configured mailboxes were expunged, then no messages are expunged and the user receives a "Quota exceeded" error. ## Settings ### `trash_priority` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) If non-zero, enables the trash plugin for the mailbox with the specified priority. Mailboxes with smaller priority number are emptied before mailboxes with a larger priority number. If there are multiple mailboxes with the same priority, expunge the oldest mail from them first. Example where Trash is emptied before Spam: ``` namespace inbox { mailbox Trash { trash_priority = 1 } mailbox Spam { trash_priority = 2 } } ``` ## Configuration Requires [quota plugin](/main/core/plugins/quota.html) to be loaded and configured to use non-FS quota. Example: dovecot.conf doveconf ``` mail_plugins { quota = yes trash = yes } namespace inbox { # Spam mailbox is emptied before Trash mailbox Spam { trash_priority = 1 } # Trash mailbox is emptied before Sent mailbox Trash { trash_priority = 2 } # If both Sent and "Sent Messages" mailboxes exist, the next oldest message # to be deleted is looked up from both of the mailboxes. mailbox Sent { trash_priority = 3 } mailbox "Sent Messages" { trash_priority = 3 } ``` # Virtual Mailbox Plugin (`virtual`) This plugin allows virtual mailboxes to be created. Virtual mailboxes consist of Dovecot search criteria that are used to build a listing of messages that don't exist in a physical mailbox. ## Settings ### `virtual_max_open_mailboxes` | Key 1 | | --- | | Default | `64` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | How many mailboxes to open in virtual plugin. ## Configuration ### Load Plugin First, you'll have to load the plugin: doveconf ``` mail_plugins { virtual = yes } ``` ### Namespace Then, you'll have to create a [namespace](/main/core/config/namespaces.html) for the virtual mailboxes, for example: doveconf ``` namespace virtual { prefix = virtual/ separator = / mail_driver = virtual mail_path = ~/Maildir/virtual } ``` After this you can create virtual mailboxes under `~/Maildir/virtual`. By default it uses the `fs` layout, so you can create directories such as: - INBOX: `~/Maildir/virtual/INBOX/` - Sub/mailbox: `~/Maildir/virtual/Sub/mailbox/` If you prefer to use the Maildir++ layout instead, set `[mailbox_list_layout = maildir++](/main/core/summaries/settings.html#mailbox_list_layout)`. ### Virtual Mailboxes For each virtual directory you need to create a `dovecot-virtual` file. Its syntax is like: ``` <1+ mailbox patterns> [ [etc..]] ``` Mailbox patterns can contain IMAP LIST-compatible [RFC 3501 (section 6.3.8)](https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.8)`*` and `%` wildcards. They are currently evaluated only when the virtual mailbox is being selected, so if more mailboxes are created during that they aren't noticed. `*` wildcard matches only one namespace at a time based on the namespace prefix. For example if you have namespaces with an empty prefix and a prefix `mail/`: - `*` matches only mailboxes from the namespace with empty prefix - `mail*` matches mailboxes beginning with name `mail` from the namespace with empty prefix - `mail/*` matches only mailboxes from the `mail/` namespace Beware that `*` will not match any mailbox which already has a more specialized match! The mailbox names have special prefixes: - `-`: Don't include this mailbox. - `+`: Drop \\Recent flags from the backend mailbox when opening it. - `!`: Save new mails to this mailbox (see below). If you need to actually include a mailbox name that contains such prefix, you can currently just kludge it by using `+` prefix (if you don't care about the \\Recent flags) and adding the mailbox name after that (e.g. `+-box`). Search program is compatible with IMAP SEARCH command [RFC 3501 (section 6.4.4)](https://datatracker.ietf.org/doc/html/rfc3501#section-6.4.4). Besides the standard SEARCH key you may want to use X-MAILBOX key which matches the message's original mailbox. TIP Leading whitespace is required in front of the search specifications. ### Saving Mails to Virtual Mailboxes It's possible to configure virtual mailbox so that it's possible to save/copy messages there. This is done by specifying a single physical mailbox where the message is really saved by prefixing it with `!`, e.g.: ``` !INBOX work/* unseen ``` WARNING Nothing guarantees that the saved mail will actually show up in the virtual mailbox. If a message was saved with \\Seen flag to the above virtual mailbox, it wouldn't show up there. This also means it's problematic to support IMAP UIDPLUS extension for virtual mailboxes, and currently Dovecot doesn't even try (no \[APPENDUID\] or \[COPYUID\] is sent to client). The `!-prefixed` virtual mailbox is also selected from; you don't need to list it again without an ! or you'll get two copies of your messages in the virtual mailbox. ## IMAPSieve Filters Added: 2.4.0 When saving to a virtual mailbox is configured, imapsieve scripts act as if the save was done directly to the physical destination mailbox. For example if Virtual/All folder was configured with INBOX as the save destination, this `sieve.before` script would be run both when saving to INBOX and when saving to Virtual/All folder: ``` imapsieve_mailbox_name = INBOX # Virtual/All would NOT work imapsieve_mailbox_causes = COPY imapsieve_mailbox_before = /etc/dovecot/sieve.before ``` Also, the `imap.mailbox` environment always contains INBOX, even when saving via Virtual/All folder. ## Mailbox Selection Based on METADATA Instead of a mailbox name, you can specify a metadata filter: ``` [-]/: ``` There can be multiple metadata entries. All the entries must match. For example: ``` * /private/vendor/vendor.dovecot/virtual:* -/private/vendor/vendor.dovecot/virtual:ignore all ``` This matches all mailboxes, which contain a virtual METADATA entry that has any value except `ignore`. ## Virtual POP3 INBOX If you want POP3 INBOX to contain some or all mailboxes, you can do this in the following way: dovecot.conf/etc/dovecot/virtual/INBOX/dovecot-virtual doveconf ``` # Namespace Configuration # The default namespace that is visible to IMAP clients namespace inbox { prefix = separator = / list = yes } # Virtual namespace for the virtual INBOX. Use a global directory for # dovecot-virtual files. namespace virtual { prefix = virtual/ separator = / mail_driver = virtual mail_path = /etc/dovecot/virtual mail_index_path = ~/Maildir/virtual list = no hidden = yes } # Copy of the inbox namespace. We'll use this in dovecot-virtual file. namespace real { prefix = RealMails/ separator = / list = no hidden = yes } # Note: none of the namespaces have inbox=yes. This is because for IMAP users # you want the inbox namespace to have 'inbox=yes', but for POP3 users you want # the virtual namespace to have 'inbox=yes'. This requires setting the # 'inbox=yes' in userdb extra fields. For example with MySQL you can do # this like: userdb sql { query = SELECT ..., \ CASE '%{protocol}' WHEN 'pop3' THEN NULL ELSE 'yes' END AS 'namespace/inbox/inbox', \ CASE '%{protocol}' WHEN 'pop3' THEN 'yes' ELSE NULL END AS 'namespace/virtual/inbox' \ WHERE ... } ``` ``` RealMails RealMails/* -RealMails/Trash -RealMails/Trash/* -RealMails/Spam all ``` You'll have to use the `RealMails/` prefix if you want to use `*` wildcard, otherwise it would match INBOX, which in turn would again lead to the virtual INBOX and that would create a loop. Also to avoid accidental POP3 UIDL changes, you shouldn't base the UIDLs on IMAP UIDs. Instead use GUIDs (with Maildir the same as base filename): doveconf ``` pop3_uidl_format = %{guid} ``` ## Configuration Examples List all messages with \\Deleted flag in all mailboxes: ~/Maildir/virtual/Trash/dovecot-virtual ``` * deleted ``` List all unseen INBOX and work/\* messages: ~/Maildir/virtual/unseen/dovecot-virtual ``` INBOX work/* unseen ``` Create a GMail-style conversation view for INBOX which shows all threads that have messages in INBOX, but shows all messages in the thread regardless of in what mailbox they physically exist in: ~/Maildir/virtual/all/dovecot-virtual~/Maildir/virtual/INBOX/dovecot-virtual ``` * all ``` ``` virtual/all inthread refs x-mailbox INBOX ``` Create a mailbox containing messages from all mailboxes except Trash and its children: ~/Maildir/virtual/all/dovecot-virtual ``` * -Trash -Trash/* all ``` Create a virtual Sentmail folder that includes Sent\*: ~/Maildir/virtual/Sentmail/dovecot-virtual ``` Sent* all ``` List messages from past 48 hours (syntax is in seconds): ~/Maildir/virtual/recent/dovecot-virtual ``` INBOX work/* all younger 172800 ``` List unseen messages from foo and flagged messages from all mailboxes (including foo): ~/Maildir/virtual/example/dovecot-virtual ``` foo or unseen flagged * flagged ``` # Welcome Plugin (`welcome`) Call a script when the user logs in for the first time. This is specifically done when the INBOX is (auto)created. The scripts are called similarly to [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts). ## Settings ### `welcome_script` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [welcome](/main/core/plugins/welcome.html) Named filter for configuring the welcome `[execute](/main/core/summaries/settings.html#execute)` script. ``` welcome { execute welcome { args = %{user} } ``` ### `welcome_wait` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [welcome](/main/core/plugins/welcome.html) If enabled, wait for the script to finish. By default, the welcome script is run asynchronously. ## Example Configuration doveconf ``` mail_plugins { welcome = yes } welcome { execute welcome { args = %{user} } wait = yes } service welcome { executable = script /usr/local/bin/welcome.sh user = dovecot unix_listener welcome { user = vmail } } ``` # Dovecot Config File Syntax TIP See Also: - [Settings Types](/main/core/settings/types.html), and - [Settings Variables](/main/core/settings/variables.html). ## Config Version The first setting in the configuration file must be `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)`. It specifies the configuration syntax, the used setting names and the expected default values. Changed: 2.4.0 All settings are now global settings, i.e. there is no settings hierarchy. There are various filters, which can limit where the settings are used. It is possible to configure any setting inside any filter, although they may not actually do anything in there. Setting name prefixes can be stripped out (and they are in `[doveconf(1)](/main/core/man/doveconf.1.html)` output) when the prefix matches the parent [named \[list\] filter](/main/core/settings/syntax.html#named-filters). For example: doveconf ``` # named list filter namespace inbox { # namespace_separator setting separator = / } # named list filter passdb static { # passdb_static_password setting password = foo # this is allowed, but it does nothing here namespace_separator = / } ``` ## Basic Syntax The syntax generally looks like this: doveconf ``` # this is a comment settings_key = settings_value ``` The `#` character and everything after it are comments. Extra spaces and tabs are ignored. If you need to use these, put the value inside quotes. The quote character inside a quoted string is escaped with `\"`: doveconf ``` settings_key = "# char, \"quote\", and trailing whitespace " ``` If Dovecot doesn't seem to be reading your configuration correctly, use `doveconf -n` to check how Dovecot actually parses it. ## Named Filters Added: 2.4.0 All settings are globals. There are several filters which can be used to restrict when the settings are used. There are "named filters" and "named list filters". Named filters are used to access settings in some specific situations. For example: doveconf ``` mail_attribute { dict file { } } ``` In this case when mail attributes are being accessed, the dict settings are looked up using the named filter called `mail_attribute`. Note that named filters cannot have a name before the `{`, i.e. `mail_attribute foo {` will result in an error. Setting names that begin with the same prefix as a named filter will be treated as if they belong inside the named filter. For example all these settings are equivalent and modify the exact same setting: doveconf ``` auth_policy_server_url = example.com auth_policy { server_url = example.com auth_policy_server_url = example.com } ``` Regardless of which method is used in `dovecot.conf`, the `[doveconf(1)](/main/core/man/doveconf.1.html)` output will be: doveconf ``` auth_policy { server_url = example.com } ``` Named list filters are similar to named filters, except there can be many of them, each with a unique name. For example: doveconf ``` namespace inbox { prefix = INBOX/ } namespace virtual { prefix = Virtual/ } ``` Both named filters and named list filters can be updated later on in the configuration. For example: doveconf ``` namespace inbox { prefix = INBOX/ } # ... # possibly included from another file: # The namespaces settings get merged into the same inbox namespace filter. namespace inbox { mailbox Trash { special_use = \Trash } } ``` The named list filter's name may also sometimes be used as part of the settings instead of simply a name. For example: doveconf ``` service auth { unix_listener auth-master { # ... } } ``` Above the `auth-master` both uniquely identifies the filter name, but it also acts as the `unix_listener_path` setting. Settings inside filters are automatically attempted to be prefixed by the innermost filter prefix to avoid repetition. For example: doveconf ``` service imap { inet_listener imaps { ssl = yes } } ``` The `ssl` setting is attempted to be looked up in this order: 1. `inet_listener_imaps_ssl` 2. `inet_listener_ssl` 3. `ssl` The first setting that exists is used. TIP The filters must currently be written with the linefeeds as shown above. For example this doesn't work: doveconf ``` namespace inbox { prefix = INBOX/ } # DOES NOT WORK ``` ## Named Filter Overrides It's possible to add/update/replace named (list) filters via userdb settings or via `-o` command line parameters. For example if you have: doveconf ``` oauth2 { http_client_request_max_attempts = 1 } ``` This can be replaced with `-o oauth2/http_client_request_max_attempts=2` command line parameters. Similarly for named list filters if you have: doveconf ``` namespace inbox { separator = / } ``` This can be replaced with `-o namespace/inbox/separator=.` command line parameters. If you want to add a new named list filter, use `+=[,,...]`. For example: `-o namespace+=second -o namespace/second/...=...` If you want to replace all the named list filters, use `=[,,...]` (i.e. without the `+`). For example: `-o namespace=inbox,second -o namespace/second/...=...` ## Connection Filters There are a few different connection/session related filters: - `protocol `: Name of the service/protocol that is reading the settings. For example: `imap`, `pop3`, `doveadm`, `lmtp`, `lda` - `remote `: Remote client's IP/network. For non-TCP connections this will never match. For example `10.0.0.1` or `10.0.0.0/16`. - `local_name `: Matches TLS connection's SNI name, if it's sent by the client. Commonly used to [configure multiple TLS certificates](/main/core/config/ssl.html). - `local `: Locally connected IP/network. For non-TCP connections this will never match. For example `127.0.0.1` or `10.0.0.0/16`. These filters work for most of the settings, but most importantly auth settings currently only support the protocol filter. Some of the other settings are also global and can't be filtered, such as `[log_path](/main/core/summaries/settings.html#log_path)`. An example, which uses all of the filters: doveconf ``` local 127.0.0.1 { local_name imap.example.com { remote 10.0.0.0/24 { protocol imap { # ... } } } } ``` The nesting of the filters must be exactly in that order or the config parsing will fail. When applying the settings, the settings within the most-specific filters override the less-specific filter's settings, so the order of the filters in config file doesn't matter. Example: doveconf ``` local 127.0.0.2 { key = 127.0.0.2 } local 127.0.0.0/24 { key = 127.0.0.0/24 } local 127.0.0.1 { key = 127.0.0.1 } # The order of the above blocks doesn't matter: # If local IP=127.0.0.1, key=127.0.0.1 # If local IP=127.0.0.2, key=127.0.0.2 # If local IP=127.0.0.3, key=127.0.0.0/24 ``` Similarly remote local filters override remote filters, which override `local_name` filters, which override protocol filters. In some situations Dovecot may also return an error if it detects that the same setting is being ambiguously set by multiple matching filters. ## Setting types See [Settings Types](/main/core/settings/types.html) for which types of settings are supported by the configuration. Note especially the [String List](/main/core/settings/types.html#string-list) and [Boolean List](/main/core/settings/types.html#boolean-list) which look similar to named filters. ## Groups includes You can create groups of settings, which can be referred to elsewhere. The groups themselves are grouped into labels. The label prefix can be omitted from the settings' names. The syntax is: doveconf ``` group @label name { # settings, with label_ prefix automatically attempted to be added } ``` For example: doveconf ``` group @mysql default { host = mysql.example.com mysql_ssl = yes ssl_client_ca_file = /etc/ssl/ca.pem } passdb sql { @mysql = default # ... } group @mailboxes english { mailbox Trash { auto = subscribe special_use = \Trash } mailbox Drafts { auto = subscribe special_use = \Drafts } } group @mailboxes finnish { mailbox Roskakori { auto = subscribe special_use = \Trash } mailbox Luonnokset { auto = subscribe special_use = \Drafts } } namespace inbox { @mailboxes = english } ``` You can override settings inside a group by adding the override settings after it. For example: doveconf ``` @mysql = default mysql_host = mysql2.example.com # override the default mysql_host ``` Note that explicit settings always override group settings. For example this is not possible: doveconf ``` mailbox trash { name = Trash } group @mailboxes finnish { mailbox trash { name = Roskakori } } @mailboxes = finnish # Does not work - name is still Trash ``` It's possible to override groups using the command line parameter `-o` or userdb. For example above you can return `namespace/inbox/@mailboxes=finnish` from userdb to change mailbox names to Finnish language. Note that groups can't be added via overrides unless `@label` is already set in the config file. You can see the default group contents with e.g.: console ``` doveconf -d @metric_defaults/proxy ``` ## Including Config Files The main `dovecot.conf` file can also include other config files: doveconf ``` !include local.conf !include /path/to/another.conf !include conf.d/*.conf ``` The paths are relative to the currently parsed config file's directory. Example: doveconf ``` # /etc/dovecot/dovecot.conf: !include conf.d/imap.conf # /etc/dovecot/conf.d/imap.conf: !include imap2.conf # /etc/dovecot/conf.d/imap2.conf is being included ``` If any of the includes fail (e.g. file doesn't exist or permission denied), it results in an error. It's not an error if wildcards don't result in any matching files. To avoid these errors, you can use `!include_try` instead: doveconf ``` !include_try passwords.conf ``` Including a file preserves the context where it's included from. Example: doveconf ``` protocol imap { !include imap-settings.conf } ``` ## Long lines It's possible to split the setting values into multiple lines. doveconf ``` setting_key = \ long \ value # equivalent to: "long value" ``` All the whitespace between lines is converted to a single space regardless of how many spaces or tabs are at the beginning of the line or before the ''. Even if there is zero whitespace a single space is added. ## Reading Value From File It's possible to read the value for a setting from a file: doveconf ``` key = login_log_format_elements = $SET:login_log_format_elements method=%{mechanism} login_log_format_elements = $SET:login_log_format_elements session=<%{session}> # Expands to: user=<%{user}> method=%{mechanism} session=<%{session}> ``` Changed: 2.4.2 The setting name can now contain a filter path, for example `$SET:service/auth/service_client_limit`. Full setting names must be used here - automatic prefixing isn't attempted. See also [Settings Variables](/main/core/settings/variables.html). ## Environment Variables It is possible use `$ENV:name` to expand values from environment. Expansion only works when it's surrounded by spaces, and is not inside `"quotes"` or `'quotes'`. Note that these are also Case Sensitive. These can also be used for external config files, but you need to list them in `[import_environment](/main/core/summaries/settings.html#import_environment)` so that processes can see them. For [File](/main/core/settings/types.html#file) it is better to use `$ENV:` instead of `%{env:}`. This is because with `$ENV:` the expansion and file reading is done by the config process, which normally runs as root. With `%{env:}` the expansion is delayed until the process accessing the setting expands it, but the process may not have enough permissions to open the file. # Dovecot Settings Types ## String String can contain any character. Strings support `[Settings variables](/main/core/settings/variables.html)`. ## String without variables Certain settings require specific variables and thus don't use the default `[Settings variables](/main/core/settings/variables.html)`. For example: doveconf ``` imap_logout_format = in=%{input} out=%{output} ``` Here the `%{input}` and `%{output}` refer to variables specific to the `[imap_logout_format](/main/core/summaries/settings.html#imap_logout_format)` setting. ## Unsigned Integer Unsigned integer is a number between `0`..`4294967295`, although specific settings may have additional restrictions. The value can also be `unlimited`, which translates to `4294967295`. ## Octal Unsigned Integer Same as [unsigned integer](/main/core/settings/types.html#unsigned-integer), but if the value is prefixed with `0`, the number is read as octal (instead of decimal). ## Boolean Boolean settings interpret any value as true, or false. `yes` and `no` are the recommended values. However, `y` and `1` also work as `yes`. Whereas, only `no` will work as false. All these are case-insensitive. Other values give errors. ## Size The size value type is used in Dovecot configuration to define the amount of space taken by something, such as a file, cache or memory limit. The size value type is case insensitive. The following suffixes can be used to define size: | Suffix | Value | | --- | --- | | `B` | bytes | | `K` | kilobytes | | `M` | megabytes | | `G` | gigabytes | | `T` | terabytes | The values can optionally be followed by `I` or `IB`. For example `K = KI = KIB`. The size value type is base `2`, meaning a kilobyte equals `1024` bytes. The value can also be `unlimited`. ## Time The Time value is used in Dovecot configuration to define the amount of Time taken by something or for doing something, such as a sending or downloading file, processing, and more. The Time value supports using suffixes of any of the following words: - `secs` - `seconds` - `mins` - `minutes` - `hours` - `days` - `weeks` INFO So for example, `d`, `da`, `day`, and `days` all mean the same. Make sure to be explicit if you want to differentiate between `mins` and `msecs`, as `m` does not suffice. The value can also be `infinite`. ## Millisecond Time This is similar to [Time](#time), but additionally supports milliseconds (`ms`) precision. The Millisecond Time value supports using suffixes of any of the following words: - All the suffixes supported by [Time](#time) - `msecs` - `mseconds` - `millisecs` - `milliseconds` All of these have the same meaning. Similarly to [Time](#time), the suffixes can be shortened to e.g. `ms`. ## IP Addresses The IP can be IPv4 address like `127.0.0.1`, IPv6 address without brackets like `::1`, or with brackets like `[::1]`. The DNS name is looked up once during config parsing, e.g. `host.example.com`. If a /block is specified, then it's a CIDR address like `1.2.3.0/24`. If a /block isn't specified, then it defaults to all bits, i.e. /32 for IPv4 addresses and /128 for IPv6 addresses. ## Port Number This type is an [unsigned integer](/main/core/settings/types.html#unsigned-integer) with numbers ranging only from `0` to `65535`. ## URL Special type of [String](#string) setting. Conforms to Uniform Resource Locators (URL) ([RFC 1738](https://datatracker.ietf.org/doc/html/rfc1738)). ## File Path to a file. The file contents are read already by the config process, so the file permissions don't usually matter. However, some callers need to open the file again (e.g. the `ssl_client_cert_file` setting with MySQL) after the process has dropped root privileges. If you use [%variables](/main/core/settings/variables.html) in file names, the expansion is delayed until the process accessing the setting expands it. The process is usually not running as root at this point, so you may need to adjust file permissions. It's possible to give inline values (instead of a path to a file) using the [heredoc syntax](/main/core/settings/syntax.html) (`<`. ## Named Filter The settings inside the filter are used only in a specific situation. See [Named Filters](/main/core/settings/syntax.html#named-filters) for more details. ## Named List Filter The settings inside the filter are used only in a specific situation. The filter has a unique name, which can be used to identify it within the list. See [Named Filters](/main/core/settings/syntax.html#named-filters) for more details. ## String List The string list type is a list of `key=value` pairs. Each key name is unique within the list (i.e. giving the same key multiple times overrides the previous one). The string list is configured similarly to [Named Filters](/main/core/settings/syntax.html#named-filters): doveconf ``` fs_randomfail_ops { read = 100 write = 50 } ``` Changed: 2.4.5 Both the `key` and the `value` support `[Settings variables](/main/core/settings/variables.html)`; previously only the `value` was expanded. Keys are expanded before duplicate keys are resolved, so deduplication operates on the final key name. You can clear out an existing string list by setting it to an empty value. This is mainly useful to drop default settings. For example: doveconf ``` auth_policy_request_attributes = auth_policy_request_attributes { login = %{requested_username} } ``` ## Boolean List The boolean list type is a list of `key=yes/no` pairs. Each key is unique within the list (i.e. giving the same key multiple times overrides the previous one). The boolean list can be configured as a space or comma-separated list, which replaces the previous boolean list entirely. For example: doveconf ``` mail_plugins = quota imap_quota mail_plugins = acl,imap_acl # removes quota and imap_quota ``` Quotes are also supported: doveconf ``` doveadm_allowed_commands = "mailbox list" "mailbox create" ``` The boolean list can also be configured to update an existing boolean list. For example: doveconf ``` mail_plugins = quota acl protocol imap { mail_plugins { imap_quota = yes imap_acl = yes } } local 10.0.0.0/24 { protocol imap { mail_plugins { imap_acl = no } } } ``` Changed: 2.4.5 The `key` names support `[Settings variables](/main/core/settings/variables.html)`; previously they were always used literally. Keys are expanded before duplicate keys are resolved, so deduplication operates on the final key name. # Settings Variables You can use special variables in several places: - All settings, except of type [String without variables](/main/core/settings/types.html#string-without-variables). Most commonly used by [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting). - Static [userdb](/main/core/config/auth/userdb.html) and [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) template strings - [LDAP authentication](/main/core/config/auth/databases/ldap.html), [SQL authentication database](/main/core/config/auth/databases/sql.html), and [userdb](/main/core/config/auth/userdb.html) query strings - Log prefix for imap/pop3 process ## Variable expansion syntax Changed: 2.4.0 We have introduced an entirely new variable expansion syntax. The basic syntax is `%{variable (| filter | filter ...)}`, which means that most existing variables work, but there are some changes, so check variable usage carefully when converting old syntax. The simple case of just getting a value of variable is `%{variable}`. These can be in middle of strings. Another syntax is `%{provider:variable}`, where the value is provided by a provider. There are global providers, and context-specific providers. A variable can be then filtered with various filters, such as `%{variable | upper}` to get uppercase representation of variable. You can chain as many filters as you need. If a pipeline contains the `safe` filter, it applies to the whole pipeline output. The `safe` filter must always be the last filter in the pipeline. Filters can accept parameters, both positional and named. E.g. `%{literal('\r\n\')}` will expand to CR LF. `%{user | substr(0, 1)}` will take first character of username. Example of named parameters would be `%{user | md5(rounds=1000,salt='pepper')}` For escaping: - `%%{text}` emits `%{text}` - `%%%{text}` emits `%%{text}` - `%{concat('%', variable)}` emits `%` - Otherwise `%` doesn't need any escaping. `%%` emits the same `%%`. Filters accept strings, numbers and variables as parameters. Parameters can be positional or named key-value pairs. Key names cannot be variables. The left side of pipe character (`|`) is provided as input to a filter. Some filters can be used in place of variables, e.g. lookup, literal and if. When value is missing or empty, you can use the `default` filter to provide value. Missing variables will cause errors and must be negated with default. This does not apply to all providers, some providers return empty when value is missing. If the last filter would output binary data, the data is encoded with `hexlify` filter by default. To avoid this, you can use `text` filter, which will sanitize the input and mark it as text. The new syntax also supports simple maths, you can do one operation. E.g. `%{port + 1000}`. Addition, substraction, multiplication, division and modulo operations are supported for now. A special case for modulo operation is that it can be applied to binary input, e.g. `sha1 % 256`. The input is treated as 64-bit unsigned number and modulo is taken from that. All strings must be encapsulated with `"` or `'`, and you can escape them using `\\` within string. Numbers when used as parameters must be provided without quotes. ## List of filters All parameters are strings unless stated otherwise. If parameter is `any`, it accepts both numbers and strings. Boolean type is `0` for false and `1` for true. Filters that have `any` in input and output mean that they will accept bytes or string, and the result will be bytes or strings, depending on the input. The types indicated as interpreted types, as everything is stored as strings internally. Bytes output type indicates that the output will be tagged as binary output. Subsequent filters can change this. | Filter | Input | Output | Description | | --- | --- | --- | --- | | `base64(pad=boolean, url=boolean)` | Bytes | String | Base64 encode given input, defaults to pad and not url scheme. | | `benumber` | Bytes | Number | Convert big-endian encoded input into a number. | | `concat(any, any...)` | Bytes | Bytes | Concatenates input with value(s). Numbers are coerced to strings. Input is optional. | | `date(format, tz=string)` | Number | String | Format a UNIX timestamp using [`strftime(3)`](https://man7.org/linux/man-pages/man3/strftime.3.html) `format`. The input may be a plain seconds value or the `.` form produced by the [`time:unix`](#time-variables) provider; any fractional part is ignored. `tz` selects the timezone used to break the timestamp into calendar fields: `utc` (alias `gmt`, the default) or `local`. Added: 2.4.5 | | `decrypt(key=bytes, iv=bytes, raw=boolean, algorithm=string)` | Any | Any | Decrypts given input, see [cryptography support](#cryptography-support). | | `decrypt(key=string, salt=string, rounds=number, raw=boolean, hash=string, algorithm=string)` | Any | Any | Decrypts given input, see [cryptography support](#cryptography-support). | | `default(value)` | String | String | Replace empty or missing input with value. Clears missing variable error. If no value is provided, empty string is used. | | `domain` | String | String | Provides domain part of user@domain value. | | `encrypt(key=bytes, iv=bytes, raw=boolean, algorithm=string)` | Any | Any | Encrypts given input, see [cryptography support](#cryptography-support). | | `encrypt(key=string, salt=string, rounds=number, raw=boolean, hash=string, algorithm=string)` | Any | Any | Encrypts given input, see [cryptography support](#cryptography-support). | | `epoch(unit=string)` | Number | Number | Convert a UNIX timestamp into an integer in the requested `unit`: `s` (seconds, the default), `ms` (milliseconds), `us` (microseconds) or `ns` (nanoseconds). The input may be a plain seconds value or the `.` form produced by the [`time:unix`](#time-variables) provider. The output never contains a decimal point. Added: 2.4.5 | | `escape` | String | String | Apply the configured escape function to the input. Returns an error if no escape function is configured. This filter is intended to be used with `safe` filter, when used without, the output is escaped again at output time (double-escaped). Added: 2.4.5 | | `from_epoch(unit=string)` | Number | Number | Inverse of `epoch`: convert an integer UNIX timestamp expressed in `unit` (`s`, `ms`, `us` or `ns`, default `s`) into the canonical `.` form produced by the [`time:unix`](#time-variables) provider. Useful for feeding millisecond/nanosecond timestamps from external input into the `date` filter. Added: 2.4.5 | | `hash(method, rounds=number, salt=string)` | Bytes | Bytes | Returns raw hash from input using given hash method. Rounds and salt are optional. | | `hexlify(width)` | Bytes | String | Convert bytes into hex with optional width, truncates or pads up to width. | | `hex(width)` | Number | Number | Convert base-10 number to base-16 number. If width is specified the result is truncated or padded with 0 to width. Negative width is applied after number. | | `if(left, operator, right, true, false)` | String | String | Evaluates given comparison and returns true or false value. See [conditionals](#conditionals). | | `if(operator, right, true, false)` | String | String | Evaluates given comparison against input value and retuns true or false value. | | `index(separator, nth)` | String | String | Returns nth element from separator separated string. Zero based. Negative values are looked relative to end of list. | | `iso8601(tz=string)` | Number | String | Format a UNIX timestamp as an ISO 8601 / RFC 3339 date-time string, e.g. `2025-06-08T10:40:00Z`. The input may be a plain seconds value or the `.` form from the [`time:unix`](#time-variables) provider; any fractional part is ignored. `tz` is `utc` (alias `gmt`, the default), which uses the `Z` suffix, or `local`, which uses a `+HH:MM`/`-HH:MM` offset. Added: 2.4.5 | | `ldap_dn` | String | String | Converts any `.` into `,dc=`. For example `domain.com` converts into `domain,dc=com`. | | `lenumber` | Bytes | Number | Convert little-endian encoded input into a number. | | `lfill(width, filler)` | Any | Any | Pads value from left with filler until length is width. Default filler is `0`. | | `list(separator)` | String | String | Converts tab-escaped list into separator separated list. There is no escaping if the value already contains the separator. Defaults to `,`. | | `literal(string)` | None | String | Expands into literally the value. If variable is used, works like lookup. Input is ignored. | | `lookup(name)` | None | String | Lookup var from table. If var is variable, the name is taken from variable's contents. Input is ignored. | | `lower` | String | String | Lowercases input. | | `md5(rounds=number, salt=string)` | Bytes | Bytes | Alias for hash with method md5. | | `regexp(expression, replacement)` | String | String | Performs regular expression replacement using [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html). Supports up to 9 capture groups. | | `reverse` | Any | Any | Reverse input bytes. | | `rfill(width, filler)` | Any | Any | Pads value to right with filler until length is width. Default filler is `0`. | | `sha1(rounds=number, salt=string)` | Bytes | Bytes | Alias for hash with method sha1. | | `sha256(rounds=number, salt=string)` | Bytes | Bytes | Alias for hash with method sha256. | | `sha384(rounds=number, salt=string)` | Bytes | Bytes | Alias for hash with method sha384. | | `sha512(rounds=number, salt=string)` | Bytes | Bytes | Alias for hash with method sha512. | | `substr(offset, length)` | Any | Any | Extracts a substring out of input and returns it. First character is at offset zero. If offset is negative, starts that far back from the end of the string. If length is omitted, returns everything through the end of the string. If length is negative, leaves that many characters off the end of the string. | | `switch(left, operator, condition1, value1, condition2, value2[, ...][, default])` | String | String | Evaluates given comparison with left variable and returns matching condition value or default. See [conditionals](#conditionals). Added: 2.4.3 | | `switch(operator, right, condition1, value1, condition2, value2[, ...][, default])` | String | String | Evaluates given comparison against pipeline and returns matching condition value or default. See [conditionals](#conditionals). Added: 2.4.3 | | `text` | Bytes | String | Sanitize input into text and clear binary tag. | | `truncate(len, bits=number)` | Bytes | Bytes | Truncate to len bytes, or number of bits. The parameters are mutually exclusive. | | `unbase64(pad=boolean, url=boolean)` | String | Bytes | Base64 decode given input, defaults to pad and not url scheme. | | `unhex` | String | Number | Convert base-16 number to base-10 number. | | `unhexlify` | String | Bytes | Convert hex encoded input into bytes. | | `upper` | String | String | Uppercases input. | | `username` | String | String | Provides user part of user@domain value. | | `safe` | String | String | Don't escape the output of the whole pipeline. This must always be the last filter in the pipeline. This is mainly intended for `[ldap_base](/main/core/summaries/settings.html#ldap_base)` when the DN comes from a variable. Added: 2.4.3 | ## Global providers Global providers that work everywhere are: | Long Name | Description | | --- | --- | | `date:` | Deprecated: 2.4.5 Get a local-time date field, available keys are `year`, `month`, `day`. See [replacements](#time-variables). | | `dovecot:` | Get a distribution variable, see [below](#distribution-variables) for a list of supported names. | | `env:` | Environment variable . Returns empty string if unset. | | `event:` | Get an event field. Returns empty string if no such field is found from event. | | `process:` | Get a process variable, see [below](#process-variables) for list of supported names. | | `system:` | Get a system variable, see [below](#system-variables) for list of supported names. | | `time:unix` | Added: 2.4.5 Returns the current time as a `.` UNIX timestamp. See [below](#time-variables). | | `time:` | Deprecated: 2.4.5 Get a local-time field, available keys are `hour`, `min`, `minute`, `sec`, `second`, `usec`. See [replacements](#time-variables). | | `generate:` | Generate a GUID/UUID. Available keys are `guid`, `guid128`, `uuid`, `uuid:record`, `uuid:compact` and `uuid:microsoft`. | ## System Variables ### `cpu_count` Number of CPUs available. Works only on Linux and FreeBSD-like systems. Can be overridden with `NCPU` environment variable. This needs to be included in `[import_environment](/main/core/summaries/settings.html#import_environment)`. ### `hostname` Hostname (without domain). Can be overridden with `DOVECOT_HOSTNAME` environment variable. This needs to be included in `[import_environment](/main/core/summaries/settings.html#import_environment)`. ### `os` OS name reported by `uname()` call. (Similar to `uname -s` output.) ### `os-version` OS version reported by `uname()` call. (Similar to `uname -r` output.) ## Process Variables ### `pid` Current process ID. ### `uid` Effective user ID of the current process. ### `gid` Effective group ID of the current process. ## Distribution Variables ### `name` Name of distributed package. (Default: `Dovecot`) ### `version` Dovecot version. ### `support-url` Support webpage set in Dovecot distribution. (Default: [https://www.dovecot.org/](https://www.dovecot.org/)) ### `support-email` Support email set in Dovecot distribution. (Default: `dovecot@dovecot.org`) ### `revision` Short commit hash of Dovecot git source tree HEAD. (Same as the commit hash reported in `dovecot --version`.) ## Time variables ### `unix` Returns the current time as a `.` UNIX timestamp (for example `1749379200.123456789`). This is the starting point for the [`epoch`](#list-of-filters), [`from_epoch`](#list-of-filters) and [`date`](#list-of-filters) filters. These also work on UNIX timestamps coming from any other variable, not just the current time: ``` # Current time as a UNIX timestamp with nanosecond precision: %{time:unix} -> 1749379200.123456789 # Current time in milliseconds: %{time:unix | epoch('ms')} -> 1749379200123 # Format an arbitrary UNIX timestamp variable in UTC: %{mytimestamp | date('%Y-%m-%d %H:%M:%S')} -> 2025-06-08 10:40:00 # ...or in the local timezone: %{mytimestamp | date('%H:%M', 'local')} # Convert a millisecond timestamp from input and format it: %{ms_timestamp | from_epoch('ms') | date('%Y-%m-%d')} # Current time as an ISO 8601 / RFC 3339 string: %{time:unix | iso8601} -> 2025-06-08T10:40:00Z ``` ## User Variables TIP See also: - [Global Variables](#global-variables) Variables that work nearly everywhere where there is a username: | Variable | Description | | --- | --- | | `user` | Full username (e.g. user@domain) | | `session` | Session ID for this client connection (unique for 9 years) | | `auth_user` | SASL authentication ID (e.g. if master user login is done, this contains the master username). If username changes during authentication, this value contains the original username. Otherwise the same as `user`. | ## Mail Service User Variables TIP See also: - [Global Variables](#global-variables), and - [User Variables](#user-variables). | Variable | Description | | --- | --- | | `service` | imap, pop3, smtp, lda (and doveadm, etc.) | | `local_ip` | local IP address | | `remote_ip` | remote IP address | | `local_port` | local port | | `remote_port` | remote port | | `userdb:` | Return userdb extra field "name". | ## Mail User Variables TIP See also: - [Global Variables](#global-variables), - [User Variables](#user-variables), and - [Mail Service User Variables](#mail-service-user-variables). | Variable | Description | | --- | --- | | `home` | home directory. Use of `~/` is better whenever possible. | | `hostname` | Expands to the hostname setting. Overrides the global `hostname`. | ## Login Variables TIP See also: - [Global Variables](#global-variables), and - [User Variables](#user-variables). | Variable | Description | | --- | --- | | `protocol` | imap, pop3, smtp, lda (and doveadm, etc.) Added: 2.4.0 Renamed from `service` variable. | | `local_name` | TLS SNI hostname, if given. | | `local_ip` | Local IP address. | | `remote_ip` | Remote IP address. | | `local_port` | Local port. | | `remote_port` | Remote port. | | `real_remote_ip` | Same as `remote_ip`, except in proxy setups contains the remote proxy's IP instead of the client's IP. | | `real_local_ip` | Same as `local_ip`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP. | | `real_remote_port` | Similar to `real_remote_ip` except for port instead of IP. | | `real_local_port` | Similar to `real_local_ip` except for port instead of IP. | | `mechanism` | [SASL](/main/core/admin/sasl.html), e.g., PLAIN. | | `secured` | "TLS" with established SSL/TLS connections, "TLS handshaking", or "TLS \[handshaking\]: error text" if disconnecting due to TLS error. "secured" with secured connections (see: `[ssl](/main/core/summaries/settings.html#ssl)`). Otherwise empty. | | `ssl_security` | TLS session security string. If HAProxy is configured and it terminated the TLS connection, contains "(proxied)". | | `ssl_ja3` | [JA3 string](/main/core/config/ssl.html#ja3-identifier) composed from TLS Client Hello. | | `ssl_ja3_hash` | MD5 hash from [JA3 string](/main/core/config/ssl.html#ja3-identifier) composed from TLS Client Hello. | | `ssl_client_cert_fp` | `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)` of client certificate. | | `ssl_client_cert_pubkey_fp` | `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)` of client certificate public key. | | `mail_pid` | PID for process that handles the mail session post-login. | | `original_user` | Same as `user`, except using the original username the client sent before any changes by auth process. With master user logins (also with `[auth_master_user_separator](/main/core/summaries/settings.html#auth_master_user_separator)` based logins),this contains only the original master username. | | `listener` | Socket listener name as specified in config file, which accepted the client connection. | | `owner_user` | For shared storage this is the `user` variable of the owner, otherwise it is the same as `user`. Added: 2.4.0 | | `passdb:` | Return passdb extra field "name". | | `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [forwarding fields](/main/core/config/proxy/overview.html#forwarding-fields). | ## Authentication Variables TIP See also: - [Global Variables](#global-variables), and - [User Variables](#user-variables). | Variable | Description | | --- | --- | | `protocol` | imap, pop3, smtp, lda (and doveadm, etc.) Added: 2.4.0 Renamed from `service` variable. | | `domain_first` | For "username@domain\_first@domain\_last" style usernames. | | `domain_last` | For "username@domain\_first@domain\_last" style usernames. | | `local_name` | TLS SNI hostname, if given. | | `local_ip` | Local IP address. | | `remote_ip` | Remote IP address | | `local_port` | Local port. | | `remote_port` | Remote port. | | `real_remote_ip` | Same as `remote_ip`, except in proxy setups contains the remote proxy's IP instead of the client's IP. | | `real_local_ip` | Same as `local_ip`, except in proxy setups contains the local proxy's IP instead of the remote proxy's IP. | | `real_remote_port` | Similar to `real_remote_ip` except for port instead of IP. | | `real_local_port` | Similar to `real_local_ip` except for port instead of IP. | | `client_pid` | Process ID of the authentication client. | | `session_pid` | For user logins: The PID of the IMAP/POP3 process handling the session. | | `mechanism` | [SASL](/main/core/admin/sasl.html), e.g., PLAIN. | | `password` | Cleartext password from cleartext authentication mechanism. | | `secured` | "TLS" with established SSL/TLS connections, "secured" with secured connections (see: `[ssl](/main/core/summaries/settings.html#ssl)`). Otherwise empty. | | `ssl_ja3_hash` | MD5 hash from JA3 string composed from TLS Client Hello. | | `ssl_client_cert_fp` | `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)` of client certificate. | | `ssl_client_cert_pubkey_fp` | `[ssl_peer_certificate_fingerprint_hash](/main/core/summaries/settings.html#ssl_peer_certificate_fingerprint_hash)` of client certificate public key. | | `cert` | "valid" if client had sent a valid client certificate, otherwise empty. | | `login_user` | For master user logins: Logged in user@domain. | | `master_user` | For master user logins: The master username. | | `original_user` | Same as `user`, except using the original username the client sent before any changes by auth process. | | `passdb:` | Return passdb extra field "name". | | `userdb:` | Return userdb extra field "name". Note that this can also be used in passdbs to access any userdb\_\* extra fields added by previous passdb lookups. | | `client_id` | If `[imap_id_retain](/main/core/summaries/settings.html#imap_id_retain)` is enabled this variable is populated with the client ID request as IMAP arglist. For directly logging the ID see the `[imap_id_received](/main/core/summaries/events.html#imap_id_received)` event. | | `passdb:forward_` | Used by proxies to pass on extra fields to the next hop, see [forwarding fields](/main/core/config/proxy/overview.html#forwarding-fields). | | `id` | Internal ID number of the current passdb/userdb. | | `token` | Used OAUTH2 token. This is only present in `[oauth2_fields](/main/core/summaries/settings.html#oauth2_fields)`. Added: 2.4.3 | | `oauth2:` | Return oauth2 field "name". This is only present in `[oauth2_fields](/main/core/summaries/settings.html#oauth2_fields)`. | ## Conditionals The following operators are supported: | Operator | Explanation | | --- | --- | | `==` | Numeric equality. | | `!=` | Numeric inequality. | | `<` | Numeric less than. | | `<=` | Numeric less or equal. | | `>` | Numeric greater than. | | `>=` | Numeric greater or equal. | | `eq` | String equality. | | `ne` | String inequality. | | `lt` | String less than. | | `le` | String less or equal. | | `gt` | String greater than. | | `ge` | String greater or equal. | | `*` | Wildcard match (mask on value2). | | `!*` | Wildcard non-match (mask on value2). | | `~` | Regular expression match (pattern on value2, [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html)). | | `!~` | String inequality (pattern on value2, [POSIX Extended Regular Expression syntax](https://www.gnu.org/software/findutils/manual/html_node/find_html/posix_002dextended-regular-expression-syntax.html)). | Dovecot supports two kinds of conditional filters, the `if` and the `switch` filter. The difference is that `if` can be used to do single comparison, and `switch` can do multiple comparisons. WARNING Nested `if` or `switch` statements will not work. You also cannot chain `if` or `switch` in a pipeline to emulate this. If you need to do complicated `if` or `switch` statements, you should use [Lua authentication database](/main/core/config/auth/databases/lua.html) instead. Examples: ``` # If %{user} is "testuser", return "INVALID". Otherwise return %{user} uppercased. %{user | if ("=", "testuser, "invalid", user) | upper } # Select subdomain for tenant postmaster@%{switch(userdb:tenant, 'eq', 'one', 'one.com', 'two', 'two.com', 'default.com')} # Or default to user variable's domain, if it's provided. Note the domain operator at the end since you can't use pipelines in values either. postmaster@%{switch(userdb:tenant, 'eq', 'one', '@one.com', 'two', '@two.com', user) | domain | default('default.com')} # Choose by limit. %{switch(number, '<', 100, 'one', 200, 'two', 300, 'three', 'bigger')} ``` ## Cryptography support ### Parameters | Key | Value | | --- | --- | | key | The encryption key. | | iv | Initialization vector. | | salt | Salt to use in PBKDF2 algorithm. | | hash | Hash to use in PBKDF2. Defaults to `sha256`. | | rounds | Number of rounds to use in PBKDF2. Defaults to 10 000. | | algorithm | Encryption algorithm. Expects OpenSSL naming. Defaults to `aes-256-cbc`. | | raw | When set to 1, will return encrypted result in raw output format. Default is 0. | ### Key, initialization vector and salt. For legacy reasons, this function supports direct keying and salted keying. In direct keying, the `key` and `iv` must be provided hex encoded, and must match the algorithm's requirements. If `key` and `salt` are provided, then the actual encryption key and initialization vector are generated with [PBKDF2 algorithm](https://en.wikipedia.org/wiki/PBKDF2). If only `key` is provided a random salt is generated. Random salt cannot be generated in raw mode, because it would not get stored, so it must be always provided. ### Structured output format Dovecot supports structured encrypted data. If initialization vector is directly provided, the output syntax is `iv$data$`. With salt based keying material generation, the format is `s=salt,r=rounds$data$`. ### Raw output format If raw is used, the raw encryption result is emitted with no salt, rounds or IV included. ### Recommended usage For best results, you should leave salt and IV management to Dovecot. ### Examples doveconf ``` import_environment { SECRET_KEY = %{env:SECRET_KEY} } imapc_password = "%{literal('s=3-?I&-a|,r=10000$e80e6ab3c18c0da69b20bf201eaf6269$') | decrypt(key=env:SECRET_KEY)}" ``` Stores imap client password securely so that it can be decrypted only if `SECRET_KEY` environment variable is provided. To easily generate an encrypted value, you can use `[doveadm user user -e "%{literal('value') | encrypt(key='secret')}"](/main/core/summaries/doveadm.html#user)`. # All Dovecot Doveadm Commands ### `acl add` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-add)` Add ACL rights. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl debug` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-debug)` Explain access rights for a mailbox/user. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl delete` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-delete)` Delete an ACL entry. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl get` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-get)` Show ACLs. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl recalc` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-recalc)` Ensure shared mailboxes exist in ACL shared dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl remove` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-remove)` Remove ACL rights. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl rights` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-rights)` Show current ACL rights. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `acl set` **Man Page:** `[doveadm-acl(1)](/main/core/man/doveadm-acl.1.html#acl-set)` Replaces ACL rights. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `altmove` **Man Page:** `[doveadm-altmove(1)](/main/core/man/doveadm-altmove.1.html#altmove)` Move mails between primary and alternative mailbox storage locations. WARNING Applicable to [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) and [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) mailbox formats only. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `auth cache flush` **Man Page:** `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html#auth-cache-flush)` Flush authentication cache. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `auth cache status` **Man Page:** `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html#auth-cache-status)` **Changes:** - Added: 2.4.5 Show authentication cache statistics. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `auth login` **Man Page:** `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html#auth-login)` Test full login. CLI ### `auth lookup` **Man Page:** `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html#auth-lookup)` Perform a passdb lookup. CLI ### `auth test` **Man Page:** `[doveadm-auth(1)](/main/core/man/doveadm-auth.1.html#auth-test)` Test authentication for a user. CLI ### `backup` **Man Page:** `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html#backup)` Dovecot's mailbox synchronization utility. This command cannot be used safely via API by untrusted users. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `compress connect` **Man Page:** `[doveadm-compress-connect(1)](/main/core/man/doveadm-compress-connect.1.html#compress-connect)` Connects to a compression-enabled IMAP service. CLI ### `config` **Man Page:** `[doveconf(1)](/main/core/man/doveconf.1.html#config)` Read and parse Dovecot's configuration files. CLI ### `copy` **Man Page:** `[doveadm-copy(1)](/main/core/man/doveadm-copy.1.html#copy)` Copy messages matching the given search query into another mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `deduplicate` **Man Page:** `[doveadm-deduplicate(1)](/main/core/man/doveadm-deduplicate.1.html#deduplicate)` Expunge duplicated messages in mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dict get` **Man Page:** `[doveadm-dict(1)](/main/core/man/doveadm-dict.1.html#dict-get)` Get key value from configured dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dict inc` **Man Page:** `[doveadm-dict(1)](/main/core/man/doveadm-dict.1.html#dict-inc)` Increase key value in dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dict iter` **Man Page:** `[doveadm-dict(1)](/main/core/man/doveadm-dict.1.html#dict-iter)` List keys in dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dict set` **Man Page:** `[doveadm-dict(1)](/main/core/man/doveadm-dict.1.html#dict-set)` Set key value in configured dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dict unset` **Man Page:** `[doveadm-dict(1)](/main/core/man/doveadm-dict.1.html#dict-unset)` Unset key value in configured dictionary. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `dump` **Man Page:** `[doveadm-dump(1)](/main/core/man/doveadm-dump.1.html#dump)` Show contents of mailbox index/log files, in human readable format. CLI ### `exec` **Man Page:** `[doveadm-exec(1)](/main/core/man/doveadm-exec.1.html#exec)` Execute commands from within `/usr/libexec/dovecot`. CLI ### `expunge` **Man Page:** `[doveadm-expunge(1)](/main/core/man/doveadm-expunge.1.html#expunge)` Expunge messages matching given search query. WARNING The expunge command REQUIRES a mailbox parameter in the query argument. The expunge command REQUIRES a message range limiter in the query argument. If all messages are desired to be expunged, the "all" query can be used. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fetch` **Man Page:** `[doveadm-fetch(1)](/main/core/man/doveadm-fetch.1.html#fetch)` Fetch mail data from user mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `flags add` **Man Page:** `[doveadm-flags(1)](/main/core/man/doveadm-flags.1.html#flags-add)` Add flags to message(s). CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `flags remove` **Man Page:** `[doveadm-flags(1)](/main/core/man/doveadm-flags.1.html#flags-remove)` Remove flags from message(s). CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `flags replace` **Man Page:** `[doveadm-flags(1)](/main/core/man/doveadm-flags.1.html#flags-replace)` Replace flags with another flag in message or messages. Replaces all current flags with the ones in the parameter list. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `force resync` **Man Page:** `[doveadm-force-resync(1)](/main/core/man/doveadm-force-resync.1.html#force-resync)` Under certain circumstances Dovecot may be unable to automatically solve problems with mailboxes. In such situations the force-resync command may be helpful. It tries to fix all problems. For [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) and [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mailboxes the storage files will be also checked. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs copy` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-copy)` Copy object in storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs delete` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-delete)` Delete object from storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs get` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-get)` Get object from storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs iter` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-iter)` List objects in fs path. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs iter-dirs` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-iter-dirs)` List folders in fs path. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs metadata` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-metadata)` CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs put` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-put)` Store object in storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fs stat` **Man Page:** `[doveadm-fs(1)](/main/core/man/doveadm-fs.1.html#fs-stat)` Retrieve files status for the path provided. Currently, only the total size (in bytes) of the item is returned. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts expand` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-expand)` Expand query using FTS. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts flatcurve check` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-flatcurve-check)` Run a simple check on Dovecot Xapian databases, and attempt to fix basic errors (it is the same checking done by the xapian-check command with the `-F` command-line option). CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts flatcurve remove` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-flatcurve-remove)` Removes all FTS data for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts flatcurve rotate` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-flatcurve-rotate)` Triggers an FTS index rotation for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts flatcurve stats` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-flatcurve-stats)` Returns FTS data for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts lookup` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-lookup)` Search mail with FTS plugin. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts optimize` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-optimize)` Optimize FTS data. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts rescan` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-rescan)` Rebuild FTS indexes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `fts tokenize` **Man Page:** `[doveadm-fts(1)](/main/core/man/doveadm-fts.1.html#fts-tokenize)` Search mail with FTS plugin. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `help` Provide doveadm usage information. CLI ### `import` **Man Page:** `[doveadm-import(1)](/main/core/man/doveadm-import.1.html#import)` Import messages matching given search query. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `index` **Man Page:** `[doveadm-index(1)](/main/core/man/doveadm-index.1.html#index)` Index user mailbox folder or folders. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `indexer add` **Man Page:** `[doveadm-indexer(1)](/main/core/man/doveadm-indexer.1.html#indexer-add)` Add indexing request for the given user and the mailbox to the indexer queue. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `indexer list` **Man Page:** `[doveadm-indexer(1)](/main/core/man/doveadm-indexer.1.html#indexer-list)` List queued index requests. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `indexer remove` **Man Page:** `[doveadm-indexer(1)](/main/core/man/doveadm-indexer.1.html#indexer-remove)` Remove index requests. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `instance list` **Man Page:** `[doveadm-instance(1)](/main/core/man/doveadm-instance.1.html#instance-list)` List Dovecot instances. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `instance remove` **Man Page:** `[doveadm-instance(1)](/main/core/man/doveadm-instance.1.html#instance-remove)` Remove Dovecot instances. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `kick` **Man Page:** `[doveadm-kick(1)](/main/core/man/doveadm-kick.1.html#kick)` Kick user. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `log errors` **Man Page:** `[doveadm-log(1)](/main/core/man/doveadm-log.1.html#log-errors)` Fetch error logs. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `log find` **Man Page:** `[doveadm-log(1)](/main/core/man/doveadm-log.1.html#log-find)` Show the location of logs. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `log reopen` **Man Page:** `[doveadm-log(1)](/main/core/man/doveadm-log.1.html#log-reopen)` Cause master process to reopen all log files. CLI ### `log test` **Man Page:** `[doveadm-log(1)](/main/core/man/doveadm-log.1.html#log-test)` Write a test message to the log files. CLI ### `mail fs copy` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-copy)` Copy object in storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs delete` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-delete)` Delete object from storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs get` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-get)` Get object from storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs iter` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-iter)` List objects in fs path. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs iter-dirs` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-iter-dirs)` List folders in fs path. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs metadata` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-metadata)` CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs put` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-put)` Store object in storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mail fs stat` **Man Page:** `[doveadm-mail-fs(1)](/main/core/man/doveadm-mail-fs.1.html#mail-fs-stat)` Retrieve files status for the path provided. Currently, only the total size (in bytes) of the item is returned. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cache decision` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-cache-decision)` List or change caching decisions for field(s). CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cache purge` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-cache-purge)` Purge the dovecot.index.cache file. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cache remove` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-cache-remove)` Remove matching mails from the cache. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox create` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-create)` Create mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey export` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-export)` Exports user or folder private keys. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey generate` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-generate)` Generate new keypair for user or folder. To generate new active user key and re-encrypt all your keys with it can be done with sh ``` doveadm mailbox cryptokey generate -u username -UR ``` This can be used to generate new user keypair and re-encrypt and create folder keys. INFO You must provide a password if you want to generate password-protected keypair right away. You can also use `[doveadm mailbox cryptokey password]()` to secure it. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey list` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-list)` List all keys for user or mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox cryptokey password` **Man Page:** `[doveadm-mailbox-cryptokey(1)](/main/core/man/doveadm-mailbox-cryptokey.1.html#mailbox-cryptokey-password)` Sets, changes or clears password for user's private key. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox delete` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-delete)` Delete mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox list` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-list)` Get list of existing mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox metadata get` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-metadata-get)` Get metadata for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox metadata list` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-metadata-list)` List metadata for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox metadata set` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-metadata-set)` Set metadata for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox metadata unset` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-metadata-unset)` Unset metadata for a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox mutf7` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-mutf7)` Convert mailbox names from mUTF-7 to UTF-8. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox path` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-path)` Returns filesystem paths for the mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox rename` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-rename)` Rename mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox status` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-status)` Show status of mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox subscribe` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-subscribe)` Subscribe to mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox unsubscribe` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-unsubscribe)` Unsubscribe from mailboxes. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `mailbox update` **Man Page:** `[doveadm-mailbox(1)](/main/core/man/doveadm-mailbox.1.html#mailbox-update)` Set internal mailbox metadata. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `move` **Man Page:** `[doveadm-move(1)](/main/core/man/doveadm-move.1.html#move)` Move messages to new mailbox. WARNING The move command REQUIRES a mailbox parameter in the query argument. The move command REQUIRES a message range limiter in the query argument. If all messages are desired to be moved, the "all" query can be used. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `penalty` **Man Page:** `[doveadm-penalty(1)](/main/core/man/doveadm-penalty.1.html#penalty)` CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `process status` **Man Page:** `[doveadm-process-status(1)](/main/core/man/doveadm-process-status.1.html#process-status)` CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `proxy kick` **Man Page:** `[doveadm-proxy(1)](/main/core/man/doveadm-proxy.1.html#proxy-kick)` **Changes:** - Changed: 2.4.0 - `host` argument has been changed to `dest-host`. - `user` argument has been changed to `mask`. Kick user. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `proxy list` **Man Page:** `[doveadm-proxy(1)](/main/core/man/doveadm-proxy.1.html#proxy-list)` **Changes:** - Added: 2.4.0 `separate-connections`, `passdb-field`, and `mask` arguments added. - Changed: 2.4.0 The response format has changed. Dovecot now returns different formats based on the value of `separate-connections`. If `separate-connections` is true, `pid`, `ip`, `dest_ip`, and list of `alt_username_fields` (from anvil) is returned. Show who is logged into the Dovecot server. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `purge` **Man Page:** `[doveadm-purge(1)](/main/core/man/doveadm-purge.1.html#purge)` Remove all messages with refcount=0 from a user's mail storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `pw` **Man Page:** `[doveadm-pw(1)](/main/core/man/doveadm-pw.1.html#pw)` Generate password hashes. CLI ### `quota get` **Man Page:** `[doveadm-quota(1)](/main/core/man/doveadm-quota.1.html#quota-get)` Display current quota usage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `quota recalc` **Man Page:** `[doveadm-quota(1)](/main/core/man/doveadm-quota.1.html#quota-recalc)` Recalculate current quota usage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `rebuild attachments` **Man Page:** `[doveadm-rebuild(1)](/main/core/man/doveadm-rebuild.1.html#rebuild-attachments)` Rebuild attachment detection information in index. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `reload` **Man Page:** `[doveadm(1)](/main/core/man/doveadm.1.html#reload)` Reload Dovecot configuration. CLI ### `save` **Man Page:** `[doveadm-save(1)](/main/core/man/doveadm-save.1.html#save)` **Changes:** - Added: 2.4.0 `received-date`, `uid`, and `gid` arguments added. Save messages to a mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `search` **Man Page:** `[doveadm-search(1)](/main/core/man/doveadm-search.1.html#search)` Find matching messages in mailbox. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `service status` **Man Page:** `[doveadm-service-status(1)](/main/core/man/doveadm-service-status.1.html#service-status)` Show information about Dovecot services. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `service stop` **Man Page:** `[doveadm-service-stop(1)](/main/core/man/doveadm-service-stop.1.html#service-stop)` Stop Dovecot services. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve activate` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-activate)` Mark active Sieve script. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve deactivate` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-deactivate)` Deactivate Sieve script. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve delete` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-delete)` Delete Sieve scripts. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve get` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-get)` Retrieve a Sieve script. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve list` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-list)` List Sieve scripts. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve put` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-put)` Add Sieve script to storage. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sieve rename` **Man Page:** `[doveadm-sieve(1)](/main/core/man/doveadm-sieve.1.html#sieve-rename)` Rename Sieve script. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `sis find` CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `stats add` **Man Page:** `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html#stats-add)` Add metrics to statistics. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `stats dump` **Man Page:** `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html#stats-dump)` Output statistics. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `stats remove` **Man Page:** `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html#stats-remove)` Remove metrics from statistics. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `stats reopen` **Man Page:** `[doveadm-stats(1)](/main/core/man/doveadm-stats.1.html#stats-reopen)` Reopen file exporter files. CLI ### `stop` **Man Page:** `[doveadm(1)](/main/core/man/doveadm.1.html#stop)` Stop Dovecot. CLI ### `sync` **Man Page:** `[doveadm-sync(1)](/main/core/man/doveadm-sync.1.html#sync)` Dovecot's mailbox synchronization utility. This command cannot be used safely via API by untrusted users. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `user` **Man Page:** `[doveadm-user(1)](/main/core/man/doveadm-user.1.html#user)` Lookup user in Dovecot's userdbs. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) ### `who` **Man Page:** `[doveadm-who(1)](/main/core/man/doveadm-who.1.html#who)` Show who is logged into the Dovecot server. CLI[HTTP API](/main/core/admin/doveadm.html#http-api) # All Dovecot Events ## Events ### `auth_client_passdb_lookup_finished` Authentication client finishes a passdb lookup. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username to lookup. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_passdb_lookup_started` Authentication client starts a passdb lookup. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username to lookup. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_request_challenged` Authentication client receives a request from server to continue SASL authentication. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if present. | | `original_user` | Original username, if present. | | `auth_user` | Auth username, if present. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_request_continued` Authentication client continues SASL authentication by sending a response to server request. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if present. | | `original_user` | Original username, if present. | | `auth_user` | Auth username, if present. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_request_finished` Authentication client receives response from server that authentication is finished, either success or failure. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if present. | | `original_user` | Original username, if present. | | `auth_user` | Auth username, if present. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_request_started` Authentication client starts authentication request. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if present. | | `original_user` | Original username, if present. | | `auth_user` | Auth username, if present. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_userdb_list_finished` Authentication client finishes userdb iteration. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user_mask` | User mask to list. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_userdb_list_started` Authentication client starts userdb iteration. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user_mask` | User mask to list. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_userdb_lookup_finished` Authentication client finishes a userdb lookup. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username to lookup. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_client_userdb_lookup_started` Authentication client starts a userdb lookup. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username to lookup. | | `error` | Error string, if error occurred. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `session` | Session identifier. | | `certificate_user` | Username from certificate. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `local_name` | TLS SNI. | | `local_ip` | Local IP client connected to. | | `remote_ip` | Remote IP of client. | | `local_port` | Local port client connected to. | | `remote_port` | Remote port of client. | | `real_local_ip` | Real local IP as seen by the server. | | `real_remote_ip` | Real remote IP as seen by the server. | | `real_local_port` | Real local port as seen by the server. | | `real_remote_port` | Real remote port as seen by the server. | | `tls_cipher` | Cipher name used, e.g. `TLS_AES_256_GCM_SHA384`. | | `tls_cipher_bits` | Cipher bits, e.g. `256`. | | `tls_pfs` | Perfect forward-security mechanism, e.g. `KxANY`, `KxECDHE`. | | `tls_protocol` | TLS protocol name, e.g. `TLSv1.3`. | ### `auth_master_client_login_finished` Authentication master login request finished. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username of the user. | | `error` | Error message if the request failed. | | `id` | Login request ID. | | `local_ip` | Client connection's local (server) IP. | | `local_port` | Client connection's local (server) port. | | `remote_ip` | Client connection's remote (client) IP. | | `remote_port` | Client connection's remote (client) port. | ### `auth_master_client_login_started` Authentication master login request started. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `id` | Login request ID. | | `local_ip` | Client connection's local (server) IP. | | `local_port` | Client connection's local (server) port. | | `remote_ip` | Client connection's remote (client) IP. | | `remote_port` | Client connection's remote (client) port. | ### `auth_passdb_request_finished` Processing has ended for a passdb block. Most useful for debugging authentication flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `result` | - `ok` - `password_mismatch` - `user_unknown` - `pass_expired` - `user_disabled` - `scheme_not_available` - `internal_failure` - `next` | | `cache` | - `miss`: Was not cached - `hit`: Found from cache | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `passdb` | passdb { name }. - Changed: 2.4.0 | | `passdb_driver` | Driver name. - Added: 2.4.0 | | `passdb_name` | - Removed: 2.4.0: Renamed to `passdb`. | | `passdb_id` | Internal ID number of the passdb. May be useful to identify the passdb if it has no name. | ### `auth_passdb_request_started` Processing has begun for a passdb block. Most useful for debugging authentication flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `passdb` | passdb { name }. - Changed: 2.4.0 | | `passdb_driver` | Driver name. - Added: 2.4.0 | | `passdb_name` | - Removed: 2.4.0: Renamed to `passdb`. | | `passdb_id` | Internal ID number of the passdb. May be useful to identify the passdb if it has no name. | ### `auth_policy_request_finished` Processing has ended for an auth policy request. Most useful for debugging authentication flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mode` | Either `allow` or `report`. | | `policy_result` | Value returned from policy server (number). | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | ### `auth_request_finished` Authentication request finished. Most useful for tracking status of authentication/login attempts. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Set when error happens. | | `success` | `yes`, when authentication succeeded. | | `policy_penalty` | Time of penalty added by policy server. | | `policy_result` | `ok`, `delayed`, or `refused`. | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | ### `auth_userdb_request_finished` Processing has ended for a userdb block. This event is also sent for userdb iterations. Most useful for debugging authentication flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `result` | - `ok` - `user_unknown` - `internal_failure` | | `cache` | - `miss`: Was not cached - `hit`: Found from cache | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `userdb` | userdb { name }. - Changed: 2.4.0 | | `userdb_driver` | Driver name. - Added: 2.4.0 | | `userdb_name` | - Removed: 2.4.0: Renamed to `userdb`. | | `userdb_id` | Internal ID number of the userdb. May be useful to identify the userdb if it has no name. | ### `auth_userdb_request_started` Processing has begun for a userdb block. This event is also sent for userdb iterations. Most useful for debugging authentication flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Full username. This can change during authentication, for example due to passdb lookups. | | `original_user` | Original username exactly as provided by the client. | | `translated_user` | Similar to `original_user`, except after `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` translations are applied. | | `login_user` | When doing a master user login, the user we are logging in as. Otherwise not set. | | `master_user` | When doing a master user login, the master username. Otherwise not set. | | `mechanism` | Name of used SASL mechanism (e.g. PLAIN). | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Service doing the lookup (e.g. `imap`, `pop3`, ...). - Added: 2.4.0 | | `session` | Session ID. | | `client_id` | Expands to client ID request as IMAP arglist. Needs `[imap_id_retain = yes](/main/core/summaries/settings.html#imap_id_retain)`. | | `remote_ip` | Remote IP address of the client connection. | | `local_ip` | Local IP address where client connected to. | | `remote_port` | Remote port of the client connection. | | `local_port` | Local port where the client connected to. | | `real_remote_ip` | Same as `remote_ip`, except if the connection was proxied this is the proxy's IP address. | | `real_local_ip` | Same as `local_ip`, except if the connection was proxied this is the proxy's IP where proxy connected to. | | `real_remote_port` | Same as `remote_port`, except if the connection was proxied this is the proxy connection's port. | | `real_local_port` | Same as `local_port`, except if the connection was proxied this is the local port where the proxy connected to. | | `local_name` | TLS SNI hostname, if given. | | `transport` | Transport security indicator. Values: - `insecure` - `secured` - Changed: 2.4.0 Secure non-TLS connections (e.g. from localhost) are now `secured` rather than `trusted`. - `TLS` | | `userdb` | userdb { name }. - Changed: 2.4.0 | | `userdb_driver` | Driver name. - Added: 2.4.0 | | `userdb_name` | - Removed: 2.4.0: Renamed to `userdb`. | | `userdb_id` | Internal ID number of the userdb. May be useful to identify the userdb if it has no name. | ### `auth_worker_request_finished` An authentication worker request has finished. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `command` | Command received by auth worker. | | `command_id` | Command ID received by auth worker (integer). | | `error` | Error message (if request ended in error). | ### `client_connection_connected` Server accepted an incoming client connection. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `local_ip` | Local server IP address where TCP client connected to. | | `remote_ip` | Remote TCP client's IP address. | | `remote_port` | Remote TCP client's source port. | | `remote_pid` | Remote UNIX socket client's process ID. | | `remote_uid` | Remote UNIX socket client's system user ID. | ### `client_connection_disconnected` Client connection is terminated. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `net_in_bytes` | Amount of data read, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `reason` | Disconnection reason. | | `local_ip` | Local server IP address where TCP client connected to. | | `remote_ip` | Remote TCP client's IP address. | | `remote_port` | Remote TCP client's source port. | | `remote_pid` | Remote UNIX socket client's process ID. | | `remote_uid` | Remote UNIX socket client's system user ID. | ### `dict_created` Dictionary is initialized. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `dict_name` | Name of the dict as set in configurations. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_destroyed` Dictionary is destroyed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `dict_name` | Name of the dict as set in configurations. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_iteration_finished` Dictionary iteration finished. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `rows` | Number of rows returned. | | `user` | Username, if it's not empty. | | `key` | Key name, starts with `priv/` or `shared/`. | | `key_not_found` | Set to `yes` if key not found. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_lookup_finished` Dictionary lookup finishes. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if it's not empty. | | `key` | Key name, starts with `priv/` or `shared/`. | | `key_not_found` | Set to `yes` if key not found. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_server_iteration_finished` Dictionary server finishes iteration. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `rows` | Number of rows returned. | | `user` | Username, if it's not empty. | | `key` | Key name, starts with `priv/` or `shared/`. | | `key_not_found` | Set to `yes` if key not found. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_server_lookup_finished` Dictionary server finishes lookup. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if it's not empty. | | `key` | Key name, starts with `priv/` or `shared/`. | | `key_not_found` | Set to `yes` if key not found. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_server_transaction_finished` Dictionary server finishes transaction. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if it's not empty. | | `rollback` | Set to `yes` when transaction was rolled back. | | `write_uncertain` | Set to `yes` if write was not confirmed. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dict_transaction_finished` Dictionary transaction has been committed or rolled back. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `user` | Username, if it's not empty. | | `rollback` | Set to `yes` when transaction was rolled back. | | `write_uncertain` | Set to `yes` if write was not confirmed. | | `dict_driver` | Name of the dictionary driver, e.g. `sql` or `proxy`. | | `error` | Error, if one occurred. | ### `dns_request_finished` DNS request sent by DNS client library to DNS worker process has been finished. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `cached` | Set to `yes` or `no` depending if it was a cached reply or not. - Added: 2.4.0 | | `error` | Human readable error. | | `error_code` | Error code usable with net\_gethosterror(). | ### `dns_request_started` DNS request sent by DNS client library to DNS worker process. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | ### `dns_worker_request_finished` DNS request finished being processed by DNS worker process. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Human readable error. | | `error_code` | Error code usable with net\_gethosterror(). | ### `dns_worker_request_started` DNS request started being processed by DNS worker process. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | ### `fs` May be inherited from various different parents (e.g. "Mail User" event) or even from no parent. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | ### `fs_file` Inherits from fs or any other specified event (e.g. mail). | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | ### `fts_flatcurve_expunge` Added: 2.4.0 Emitted when a message is expunged from a mailbox. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The UID that was expunged from FTS index | ### `fts_flatcurve_index` Added: 2.4.0 Emitted when a message is indexed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The UID that was added to the FTS index | ### `fts_flatcurve_last_uid` Added: 2.4.0 Emitted when the system queries for the last UID indexed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | | `uid` | The last UID contained in the FTS index | ### `fts_flatcurve_optimize` Added: 2.4.0 Emitted when a mailbox is optimized. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | ### `fts_flatcurve_query` Added: 2.4.0 Emitted when a query is completed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `count` | The number of messages matched | | `mailbox` | The mailbox name | | `maybe` | Are the results uncertain? \[yes | no\] | | `query` | The query text sent to Xapian | | `uids` | The list of UIDs returned by the query | ### `fts_flatcurve_rescan` Added: 2.4.0 Emitted when a rescan is completed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `expunged` | The list of UIDs that were expunged during rescan | | `mailbox` | The mailbox name | | `status` | Status of the rescan \[expunge\_msgs | missing\_msgs | ok\] | | `uids` | The list of UIDs that triggered a non-ok status response | ### `fts_flatcurve_rotate` Added: 2.4.0 Emitted when a mailbox has its underlying Xapian DB rotated. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | The mailbox name | ### `http_request_finished` HTTP request is complete. This event is useful to track and monitor external services. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `attempts` | Amount of individual HTTP request attempts (number of retries after failures + 1). | | `net_in_bytes` | Amount of data read, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `dest_host` | Destination host. | | `dest_ip` | Destination IP address. | | `dest_port` | Destination port. | | `method` | HTTP verb used uppercased, e.g. `GET`. | | `redirects` | Number of redirects done while processing request. | | `status_code` | HTTP result status code (integer). | | `target` | Request path with parameters, e.g. `/path/?delimiter=%2F&prefix=test%2F`. | ### `http_request_redirected` Intermediate event emitted when an HTTP request is being redirected. The `[http_request_finished](/main/core/summaries/events.html#http_request_finished)` is still sent at the end of the request. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `attempts` | Amount of individual HTTP request attempts (number of retries after failures + 1). | | `net_in_bytes` | Amount of data read, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `dest_host` | Destination host. | | `dest_ip` | Destination IP address. | | `dest_port` | Destination port. | | `method` | HTTP verb used uppercased, e.g. `GET`. | | `redirects` | Number of redirects done while processing request. | | `status_code` | HTTP result status code (integer). | | `target` | Request path with parameters, e.g. `/path/?delimiter=%2F&prefix=test%2F`. | ### `http_request_retried` Intermediate event emitted when an HTTP request is being retried. The `[http_request_finished](/main/core/summaries/events.html#http_request_finished)` is still sent at the end of the request. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `attempts` | Amount of individual HTTP request attempts (number of retries after failures + 1). | | `net_in_bytes` | Amount of data read, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `dest_host` | Destination host. | | `dest_ip` | Destination IP address. | | `dest_port` | Destination port. | | `method` | HTTP verb used uppercased, e.g. `GET`. | | `redirects` | Number of redirects done while processing request. | | `status_code` | HTTP result status code (integer). | | `target` | Request path with parameters, e.g. `/path/?delimiter=%2F&prefix=test%2F`. | ### `http_server_request_finished` HTTP request is fully completed, i.e. the incoming request body is read and the full response to the request has been sent to the client. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `net_in_bytes` | Amount of request data read, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of response data written, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `status_code` | HTTP result status code (integer). | | `request_id` | Assigned ID of the received request. | | `method` | HTTP verb used uppercased, e.g. `GET`. | | `target` | Request path with parameters, e.g. `/path/?delimiter=%2F&prefix=test%2F`. | | `local_ip` | Local server IP address where TCP client connected to. | | `remote_ip` | Remote TCP client's IP address. | | `remote_port` | Remote TCP client's source port. | | `remote_pid` | Remote UNIX socket client's process ID. | | `remote_uid` | Remote UNIX socket client's system user ID. | ### `http_server_request_started` A new HTTP request has been received and the request headers (but not body payload) are parsed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `request_id` | Assigned ID of the received request. | | `method` | HTTP verb used uppercased, e.g. `GET`. | | `target` | Request path with parameters, e.g. `/path/?delimiter=%2F&prefix=test%2F`. | | `local_ip` | Local server IP address where TCP client connected to. | | `remote_ip` | Remote TCP client's IP address. | | `remote_port` | Remote TCP client's source port. | | `remote_pid` | Remote UNIX socket client's process ID. | | `remote_uid` | Remote UNIX socket client's system user ID. | ### `imap_client_hibernated` IMAP client is hibernated or the hibernation attempt failed. TIP For failures, this event can be logged by either imap or imap-hibernate process depending on which side the error was detected in. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | Mailbox name where hibernation was started in. | | `error` | Reason why hibernation attempt failed. | | `user` | Username of the user. | | `session` | Session ID of the IMAP connection. | | `local_ip` | IMAP connection's local (server) IP. | | `local_port` | IMAP connection's local (server) port. | | `remote_ip` | IMAP connection's remote (client) IP. | | `remote_port` | IMAP connection's remote (client) port. | ### `imap_client_unhibernate_retried` An IMAP client is attempted to be unhibernated, but imap processes are busy and the unhibernation attempt is retried. This event is sent each time when retrying is done. The `[imap_client_unhibernated](/main/core/summaries/events.html#imap_client_unhibernated)` event is still sent when unhibernation either succeeds or fails permanently. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Reason why unhibernation failed. | | `user` | Username of the user. | | `session` | Session ID of the IMAP connection. | | `local_ip` | IMAP connection's local (server) IP. | | `local_port` | IMAP connection's local (server) port. | | `remote_ip` | IMAP connection's remote (client) IP. | | `remote_port` | IMAP connection's remote (client) port. | ### `imap_client_unhibernated` IMAP client is unhibernated or the unhibernation attempt failed. TIP For failures, this event can be logged by either imap or imap-hibernate process depending on which side the error was detected in. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | Mailbox name where hibernation was started in. | | `reason` | Reason why client was unhibernated: - `idle_done`: IDLE command was stopped with DONE. - `idle_bad_reply`: IDLE command was stopped with some other command than DONE. - `mailbox_changes`: Mailbox change notifications need to be sent to the client. | | `hibernation_usecs` | Number of microseconds how long the client was hibernated. | | `user` | Username of the user. | | `session` | Session ID of the IMAP connection. | | `local_ip` | IMAP connection's local (server) IP. | | `local_port` | IMAP connection's local (server) port. | | `remote_ip` | IMAP connection's remote (client) IP. | | `remote_port` | IMAP connection's remote (client) port. | ### `imap_command_finished` IMAP command is completed. This event is useful to track individual command usage, debug specific sessions, and/or detect broken clients. TIP This event is currently not sent for pre-login IMAP commands. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `tagged_reply_state` | Values: - `OK` - `NO` - `BAD` | | `tagged_reply` | Full tagged reply (e.g. `OK SELECT finished.`). | | `last_run_time` | Timestamp when the command was running last time. (Command may be followed by internal "mailbox sync" that can take some time to complete.) | | `running_usecs` | How many usecs this command has spent running. | | `lock_wait_usecs` | How many usecs this command has spent waiting for locks. | | `net_in_bytes` | Amount of data read for this command, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written for this command, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `cmd_tag` | IMAP command tag. | | `cmd_name` | IMAP command name uppercased (e.g. `FETCH`). Contains `unknown` for unknown command names. | | `cmd_input_name` | IMAP command name exactly as sent (e.g. `fetcH`) regardless of whether or not it is valid. | | `cmd_args` | IMAP command's full parameters (e.g. `1:* FLAGS`). | | `cmd_human_args` | IMAP command's full parameters, as human-readable output. Often it's the same as `cmd_args`, but it is guaranteed to contain only valid UTF-8 characters and no control characters. Multi-line parameters are written only as ``. | | `user` | Username of the user. | | `session` | Session ID of the IMAP connection. | | `local_ip` | IMAP connection's local (server) IP. | | `local_port` | IMAP connection's local (server) port. | | `remote_ip` | IMAP connection's remote (client) IP. | | `remote_port` | IMAP connection's remote (client) port. | ### `imap_id_received` Added: 2.4.0 This event is emitted when the IMAP ID command was received, both for pre- as well as post-login. The parameters slightly differ for an unauthenticated client, e.g. there is no user id. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `internal` | If `yes`, ID command parameters include the internally known `x-*` fields to update e.g. the IP or session ID. Typically these fields would be sent only by Dovecot proxies. The internal fields are not actually used, unless `trusted=yes` also. - Added: 2.4.3 | | `external` | If `yes`, ID command parameters include fields sent by a regular IMAP client (non-internal fields). Dovecot proxy can send an ID command to a backend containing both internal and external fields. If the IMAP client sends only `tag ID NIL` command, the event is sent but `external` is not set. - Added: 2.4.3 | | `trusted` | If `yes`, the ID command came from an IP matching `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)`. If any internal fields were sent, they were processed. - Added: 2.4.3 | | `id_param_` | Received parameters. The event name is the lowercase parameter key prefixed with `id_param_`, the value is the parameter value. | | `id_invalid` | Each key that contains invalid characters are enumerated starting with 1. Valid characters are latin alphabetic characters (= `a` .. `z`), numerals (= `0` .. `9`), the dash (= `-`) and the underscore (= `_`), every other character is considered invalid. The value of this field is the original parameter key including invalid characters, followed by a space character, and finally the original value concatenated into a single string. | | `cmd_tag` | IMAP command tag. | | `cmd_name` | IMAP command name uppercased (e.g. `FETCH`). Contains `unknown` for unknown command names. | | `cmd_input_name` | IMAP command name exactly as sent (e.g. `fetcH`) regardless of whether or not it is valid. | | `cmd_args` | IMAP command's full parameters (e.g. `1:* FLAGS`). | | `cmd_human_args` | IMAP command's full parameters, as human-readable output. Often it's the same as `cmd_args`, but it is guaranteed to contain only valid UTF-8 characters and no control characters. Multi-line parameters are written only as ``. | | `user` | Username of the user. | | `session` | Session ID of the IMAP connection. | | `local_ip` | IMAP connection's local (server) IP. | | `local_port` | IMAP connection's local (server) port. | | `remote_ip` | IMAP connection's remote (client) IP. | | `remote_port` | IMAP connection's remote (client) port. | ### `indexer_worker_indexing_finished` Indexer worker process completed an indexing transaction. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `message_count` | Number of messages indexed. | | `first_uid` | UID of the first indexed message. | | `last_uid` | UID of the last indexed message. | | `user_cpu_usecs` | Total user CPU spent on the indexing transaction in microseconds. | | `mailbox` | Full mailbox name in UTF-8 | | `mailbox_guid` | Mailbox GUID | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `login_aborted` Added: 2.4.0 `reason` values: | Reason | Description | | --- | --- | | `anonymous_auth_disabled` | Anonymous authentication is not allowed. | | `authorization_failed` | Master user authentication succeeded, but authorization to access the requested login user wasn't allowed. | | `auth_aborted_by_client` | Client started SASL authentication, but returned "\*" reply to abort it. | | `auth_failed` | Generic authentication failure. Possibly due to invalid username/password, but could have been some other unspecified reason also. | | `auth_nologin_referral` | Authentication returned auth referral to redirect the client to another server. This is normally configured to be sent only when the client is a Dovecot proxy, which handles the redirection. For example site1-proxy1 connects to site2-proxy2, and site2-proxy2 decides to ask site1-proxy1 to connect to site3 instead. See also `proxy_dest_redirected` code, which is used when the proxy itself does the redirection. | | `auth_process_comm_fail` | Internal error communicating with the auth process. | | `auth_process_not_ready` | Client disconnected before auth process was ready. This may indicate a hanging auth process if `connected_usecs` is large. | | `auth_waiting_client` | Client started SASL authentication, but disconnected instead of sending the next SASL continuation reply. | | `cleartext_auth_disabled` | Authentication using cleartext mechanism is not allowed at this point. It would be allowed if SSL/TLS was enabled. | | `client_ssl_cert_untrusted` | Client sent an SSL certificate that is untrusted with `[auth_ssl_require_client_cert = yes](/main/core/summaries/settings.html#auth_ssl_require_client_cert)`. | | `client_ssl_cert_missing` | Client didn't send SSL certificate, but `[auth_ssl_require_client_cert = yes](/main/core/summaries/settings.html#auth_ssl_require_client_cert)`. | | `client_ssl_not_started` | Client didn't even start SSL with `[auth_ssl_require_client_cert = yes](/main/core/summaries/settings.html#auth_ssl_require_client_cert)`. | | `connection_limit` | Client reached `[mail_max_userip_connections](/main/core/summaries/settings.html#mail_max_userip_connections)` limit. | | `internal_failure` | Internal failure. The error log has more details. | | `invalid_base64` | Client sent invalid base64 in SASL response. | | `invalid_credentials` | Added: 2.4.5 Credentials contain control characters. | | `invalid_mech` | Unknown SASL authentication mechanism requested. | | `login_disabled` | The user has the [passdb: Authentication \`nologin\` Extra Field](/main/core/config/auth/passdb.html#nologin) field set in passdb and is thereby not able to login. | | `no_auth_attempts` | Client didn't send any authentication attempts. | | `password_expired` | The user's password is expired. | | `process_full` | [service configuration (client\_limit)](/main/core/config/service.html#client-limit) and [service configuration (process\_limit)](/main/core/config/service.html#process-limit) was hit and this login session was killed. | | `shutting_down` | The process is shutting down so the login is aborted. | | `tls_handshake_not_finished` | TLS handshake failed or was not finished. | | `user_disabled` | User is in deny passdb, or in some other way disabled passdb. | Proxying `reason` values: | Reason | Description | | --- | --- | | `proxy_dest_connect_failed` | Local authentication succeeded, but connection to destination hop failed. | | `proxy_dest_internal_failure` | Local authentication succeeded, but internal failure occurred after that. | | `proxy_dest_remote_failure` | Local authentication succeeded, but destination hop reported unspecified failure. | | `proxy_dest_protocol_failure` | Local authentication succeeded, but destination hop unexpectedly violated the protocol standard. | | `proxy_dest_auth_failed` | Local authentication succeeded, but proxying failed to authenticate to the destination hop. | | `proxy_dest_auth_temp_failed` | Local authentication succeeded, but proxying failed to temporarily authenticate to the destination hop. | | `proxy_dest_redirected` | Local authentication succeeded, but destination hop redirected to another host. A new `proxy_session_started` event is started for the redirection by this same proxy. See also `auth_nologin_referral` code, which is used when the connecting proxy is asked to do the redirection. | | `proxy_dest_connection_limit` | Added: 2.4.3 Login to backend failed because client reached `[mail_max_userip_connections](/main/core/summaries/settings.html#mail_max_userip_connections)` limit. | | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reason` | Short reason; see the [short reason to description mapping](#pre_login_client_reason_values). | | `auth_successes` | Number of successful authentications, which eventually failed due to other reasons. | | `auth_attempts` | Total number of authentication attempts, both successful and failed. | | `auth_usecs` | How long ago the first authentication attempt was started. | | `connected_usecs` | How long ago the client connection was created. | | `local_ip` | Local IP address. | | `local_name` | TLS SNI hostname, if given. - Added: 2.4.0 | | `local_port` | Local port. | | `remote_ip` | Remote IP address. | | `remote_port` | Remote port. | | `user` | Full username. | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service e.g. `submission`, `imap`. - Added: 2.4.0 | ### `mail_cache_corrupted` Cache file was found to be corrupted and the whole file is deleted. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reason` | Reason string why cache was found to be corrupted. | ### `mail_cache_decision_changed` A field's caching decision changed. Decisions: | Decision | Description | | --- | --- | | `no` | The field is not cached. | | `temp` | The field is cached for 1 week and dropped on the next purge. | | `yes` | The field is cached permanently. If the field isn't accessed for 30 days it's dropped. | | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `field` | Cache field name (e.g. `imap.body` or `hdr.from`). | | `last_used` | UNIX timestamp of when the field was accessed the last time. This is updated only once per 24 hours. | | `reason` | Reason why the caching decision changed: - `add`: no -> temp decision change, because a new field was added to cache. - `old_mail`: temp -> yes decision change, because a mail older than 1 week was accessed. - `unordered_access`: temp -> yes decision change, because mails weren't accessed in ascending order. - Other values indicate a reason for cache purging, which changes the caching decision yes -> temp. | | `uid` | IMAP UID number that caused the decision change. This is set only for some reasons, not all. | | `old_decision` | Old [cache decision](#mail_cache_decision_changed_decisions). | | `new_decision` | New [cache decision](#mail_cache_decision_changed_decisions). | ### `mail_cache_decision_rejected` The decision to promote a field (from `no` to `temp`) was rejected. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `field` | Cache field name (e.g. `hdr.from`). | | `reason` | Reason why the caching decision changed: - `too_many_headers` : This can happen when the count of headers in the cache exceeds the maximum configured with `[mail_cache_max_headers_count](/main/core/summaries/settings.html#mail_cache_max_headers_count)`. | ### `mail_cache_purge_drop_field` Existing field is dropped from the cache file because it hadn't been accessed for 30 days. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `field` | Cache field name (e.g. `imap.body` or `hdr.from`). | | `decision` | Old caching decision: `temp`, or `yes`. | | `last_used` | UNIX timestamp of when the field was accessed the last time. This is updated only once per 24 hours. | | `file_seq` | Sequence of the new cache file that is created. | | `prev_file_seq` | Sequence of the cache file that is to be purged. | | `prev_file_size` | Size of the cache file that is to be purged. | | `prev_deleted_records` | Number of records (mails) marked as deleted in the cache file that is to be purged. | | `reason` | Reason string for purging the cache file: - doveadm mailbox cache purge - copy cache decisions - creating cache - cache is too large - syncing - rebuilding index | ### `mail_cache_purge_finished` | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `file_size` | Size of the new cache file. | | `max_uid` | IMAP UID of the last mail in the cache file. | | `file_seq` | Sequence of the new cache file that is created. | | `prev_file_seq` | Sequence of the cache file that is to be purged. | | `prev_file_size` | Size of the cache file that is to be purged. | | `prev_deleted_records` | Number of records (mails) marked as deleted in the cache file that is to be purged. | | `reason` | Reason string for purging the cache file: - doveadm mailbox cache purge - copy cache decisions - creating cache - cache is too large - syncing - rebuilding index | ### `mail_cache_purge_started` Cache file purging is started. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `file_seq` | Sequence of the new cache file that is created. | | `prev_file_seq` | Sequence of the cache file that is to be purged. | | `prev_file_size` | Size of the cache file that is to be purged. | | `prev_deleted_records` | Number of records (mails) marked as deleted in the cache file that is to be purged. | | `reason` | Reason string for purging the cache file: - doveadm mailbox cache purge - copy cache decisions - creating cache - cache is too large - syncing - rebuilding index | ### `mail_cache_record_corrupted` Cache record for a specific mail was found to be corrupted and the record is deleted. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reason` | Reason string why cache was found to be corrupted. | | `uid` | IMAP UID of the mail whose cache record is corrupted. | ### `mail_delivery_finished` Message delivery is completed. This event is useful for logging and tracking mail deliveries. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Error message if the delivery failed. | | `message_id` | Message-ID header value (truncated to 200 bytes). | | `message_subject` | Subject header value, in UTF-8 (truncated to 80 bytes). | | `message_from` | Email address in the From header (e.g. `user@example.com`). | | `message_size` | Size of the message, in bytes. | | `message_vsize` | Size of the message with CRLF linefeeds, in bytes. | | `rcpt_to` | Recipient address. | | `rcpt_param_notify` | The value of the NOTIFY parameter for the RCPT command. | | `rcpt_param_orcpt` | The address value of the ORCPT parameter for the RCPT command. | | `rcpt_param_orcpt_type` | The address type (typically "rfc822") of the ORCPT parameter for the RCPT command. | | `session` | The session ID for this connection (same as `connection_id`). | | `transaction_id` | Transaction ID used by the server for this transaction (this ID is logged, mentioned in the DATA reply and part of the "Received:" header). It is based on the connection\_id with a ":" sequence number suffix. | | `mail_from` | Sender address. | | `mail_param_auth` | The value of the AUTH parameter for the MAIL command. | | `mail_param_body` | The value of the BODY parameter for the MAIL command. | | `mail_param_envid` | The value of the ENVID parameter for the MAIL command. | | `mail_param_ret` | The value of the RET parameter for the MAIL command. | | `mail_param_size` | The value of the SIZE parameter for the MAIL command. | | `data_size` | The number data of bytes received from the client. This field is only present when the transaction finished receiving the DATA command. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | ### `mail_delivery_started` Message delivery has started. This event is useful for debugging mail delivery flow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `message_id` | Message-ID header value (truncated to 200 bytes). | | `message_subject` | Subject header value, in UTF-8 (truncated to 80 bytes). | | `message_from` | Email address in the From header (e.g. `user@example.com`). | | `message_size` | Size of the message, in bytes. | | `message_vsize` | Size of the message with CRLF linefeeds, in bytes. | | `rcpt_to` | Recipient address. | | `rcpt_param_notify` | The value of the NOTIFY parameter for the RCPT command. | | `rcpt_param_orcpt` | The address value of the ORCPT parameter for the RCPT command. | | `rcpt_param_orcpt_type` | The address type (typically "rfc822") of the ORCPT parameter for the RCPT command. | | `session` | The session ID for this connection (same as `connection_id`). | | `transaction_id` | Transaction ID used by the server for this transaction (this ID is logged, mentioned in the DATA reply and part of the "Received:" header). It is based on the connection\_id with a ":" sequence number suffix. | | `mail_from` | Sender address. | | `mail_param_auth` | The value of the AUTH parameter for the MAIL command. | | `mail_param_body` | The value of the BODY parameter for the MAIL command. | | `mail_param_envid` | The value of the ENVID parameter for the MAIL command. | | `mail_param_ret` | The value of the RET parameter for the MAIL command. | | `mail_param_size` | The value of the SIZE parameter for the MAIL command. | | `data_size` | The number data of bytes received from the client. This field is only present when the transaction finished receiving the DATA command. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | ### `mail_expunge_requested` A mail is set to be expunged. TIP Expunges can be rolled back later on, this event is emitted when an expunge is requested. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `seq` | Mail sequence number. | | `uid` | Mail IMAP UID number. | | `mailbox` | Full mailbox name in UTF-8 | | `mailbox_guid` | Mailbox GUID | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `mail_expunged` A mail was expunged from the mailbox. TIP This event inherits from mailbox, not mail. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `uid` | UID of the expunged mail. | | `mailbox` | Full mailbox name in UTF-8 | | `mailbox_guid` | Mailbox GUID | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `mail_index_recreated` A mail index file was recreated. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `filepath` | Path to the index file being recreated. | | `reason` | Human-readable reason why the mail index was recreated. | ### `mail_metadata_accessed` Added: 2.4.0 A mail was opened for reading its metadata. TIP This event is not sent when mails' body is accessed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `seq` | Mail sequence number. | | `uid` | Mail IMAP UID number. | | `mailbox` | Full mailbox name in UTF-8 | | `mailbox_guid` | Mailbox GUID | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `mail_opened` A mail was opened, e.g., for reading its body. TIP This event is not sent when mails' metadata is accessed, even if it causes opening the mail file. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reason` | Reason why the mail was opened. (optional) | | `seq` | Mail sequence number. | | `uid` | Mail IMAP UID number. | | `mailbox` | Full mailbox name in UTF-8 | | `mailbox_guid` | Mailbox GUID | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `mail_user_session_finished` | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `utime` | User CPU time used in microseconds. | | `stime` | System CPU time used in microseconds. | | `minor_faults` | Page reclaims (soft page faults). | | `major_faults` | Page faults (hard page faults). | | `vol_cs` | Voluntary context switches. | | `invol_cs` | Involuntary context switches. | | `rss` | Resident set size in bytes. (Skipped in non-Linux environments.) | | `vsz` | Virtual memory size in bytes. (Skipped in non-Linux environments.) | | `rchar` | I/O counter: chars (bytes) read from storage. (Skipped in non-Linux environments.) | | `wchar` | I/O counter: chars (bytes) written to storage. (Skipped in non-Linux environments.) | | `syscr` | Number of read syscalls. (Skipped in non-Linux environments.) | | `syscw` | Number of write syscalls. (Skipped in non-Linux environments.) | | `net_in_bytes` | Bytes received during this session (for applicable processes.) - Added: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Bytes sent during this session (for applicable processes.) - Added: 2.4.0: This was previously `out_bytes`. | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `managesieve_command_finished` Finished the ManageSieve command. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `script_name` | Name for the Sieve script this command operated on (if any). | | `old_script_name` | Old name of the Sieve script (only set for RENAMESCRIPT). | | `new_script_name` | New name for the Sieve script (only set for RENAMESCRIPT). | | `compile_errors` | The number of compile errors that occurred (only set for PUTSCRIPT, CHECKSCRIPT and SETACTIVE when compile fails). | | `compile_warnings` | The number of compile warnings that occurred (only set for PUTSCRIPT, CHECKSCRIPT and SETACTIVE when script is compiled). | | `cmd_name` | Name of the ManageSieve command. | | `cmd_args` | Arguments for the ManageSieve command. | | `error` | Error message for when the command failed. | ### `pop3_command_finished` Added: 2.4.0 POP3 command is completed. This event is useful to track individual command usage, debug specific sessions, and/or detect broken clients. TIP This event is currently not sent for pre-login POP3 commands. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reply` | POP3 reply. Values: - `OK` - `FAIL` | | `net_in_bytes` | Amount of data read for this command, in bytes. | | `net_out_bytes` | Amount of data written for this command, in bytes. | | `cmd_name` | POP3 command name uppercased (e.g. `UIDL`). | | `cmd_args` | POP3 command's full parameters (e.g. `1 1`). | | `user` | Username of the user. | | `session` | Session ID of the POP3 connection. | | `local_ip` | POP3 connection's local (server) IP. | | `local_port` | POP3 connection's local (server) port. | | `remote_ip` | POP3 connection's remote (client) IP. | | `remote_port` | POP3 connection's remote (client) port. | ### `proxy_session_established` Connection to proxy destination is established and user is successfully logged into the backend. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `source_port` | Source port where proxy connection originated from. | | `reconnect_attempts` | Number of times connection failed and reconnection was attempted. | | `dest_host` | Host name of the proxy destination (if proxying is configured with IP address, will have the same value as `dest_ip`). | | `dest_ip` | Proxy destination IP. | | `dest_port` | Proxy destination port. | | `source_ip` | Source IP where proxy connection originated from. | | `master_user` | If proxying is done with a master user authentication, contains the full username of master user. | | `local_ip` | Local IP address. | | `local_name` | TLS SNI hostname, if given. - Added: 2.4.0 | | `local_port` | Local port. | | `remote_ip` | Remote IP address. | | `remote_port` | Remote port. | | `user` | Full username. | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service e.g. `submission`, `imap`. - Added: 2.4.0 | ### `proxy_session_finished` Connection to proxy destination has ended, either successfully or with error. List of error codes: | Error Code | Explanation | | --- | --- | | `authorization_failed` | User authorization failed. | | `temp_fail` | Auth service reported temporary failure. | | `user_disabled` | User is disabled. | | `password_expired` | Password is expired. | | `invalid_base64` | Challenge response was invalid base64 encoded. | | `login_disabled` | Login is disabled. | | `invalid_mech` | Used mechanism isn't supported. | | `cleartext_auth_disabled` | Cleartext authentication is not enabled, use TLS. | | `anonymous_auth_disabled` | Anonymous authentication is not enabled. | | `auth_nologin_referral` | Authentication returned auth referral to redirect the client to another server. This is normally configured to be sent only when the client is a Dovecot proxy, which handles the redirection. For example site1-proxy1 connects to site2-proxy2, and site2-proxy2 decides to ask site1-proxy1 to connect to site3 instead. See also `proxy_dest_redirected` code, which is used when the proxy itself does the redirection. | | `invalid_credentials` | Added: 2.4.5 Credentials contain control characters. | Proxying error codes: | Reason | Description | | --- | --- | | `proxy_dest_connect_failed` | Local authentication succeeded, but connection to destination hop failed. | | `proxy_dest_internal_failure` | Local authentication succeeded, but internal failure occurred after that. | | `proxy_dest_remote_failure` | Local authentication succeeded, but destination hop reported unspecified failure. | | `proxy_dest_protocol_failure` | Local authentication succeeded, but destination hop unexpectedly violated the protocol standard. | | `proxy_dest_auth_failed` | Local authentication succeeded, but proxying failed to authenticate to the destination hop. | | `proxy_dest_auth_temp_failed` | Local authentication succeeded, but proxying failed to temporarily authenticate to the destination hop. | | `proxy_dest_redirected` | Local authentication succeeded, but destination hop redirected to another host. A new `proxy_session_started` event is started for the redirection by this same proxy. See also `auth_nologin_referral` code, which is used when the connecting proxy is asked to do the redirection. | | `proxy_dest_connection_limit` | Added: 2.4.3 Login to backend failed because client reached `[mail_max_userip_connections](/main/core/summaries/settings.html#mail_max_userip_connections)` limit. | | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | If login to destination failed, contains the error. | | `error_code` | If login to destination failed, contains the [error code](#proxy_session_finished_error_code). - Added: 2.4.0 | | `disconnect_side` | Which side disconnected: `client`, `server`, `proxy`. | | `disconnect_reason` | Reason for disconnection (empty = clean disconnect). | | `idle_usecs` | Number of seconds the connection was idling before getting disconnected. - Changed: 2.4.0: This was previously named `idle_secs`. | | `net_in_bytes` | Amount of data read from client, in bytes. - Changed: 2.4.0: This was previously `in_bytes`. | | `net_out_bytes` | Amount of data written to client, in bytes. - Changed: 2.4.0: This was previously `out_bytes`. | | `source_port` | Source port where proxy connection originated from. | | `reconnect_attempts` | Number of times connection failed and reconnection was attempted. | | `dest_host` | Host name of the proxy destination (if proxying is configured with IP address, will have the same value as `dest_ip`). | | `dest_ip` | Proxy destination IP. | | `dest_port` | Proxy destination port. | | `source_ip` | Source IP where proxy connection originated from. | | `master_user` | If proxying is done with a master user authentication, contains the full username of master user. | | `local_ip` | Local IP address. | | `local_name` | TLS SNI hostname, if given. - Added: 2.4.0 | | `local_port` | Local port. | | `remote_ip` | Remote IP address. | | `remote_port` | Remote port. | | `user` | Full username. | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service e.g. `submission`, `imap`. - Added: 2.4.0 | ### `proxy_session_reconnecting` Connection to proxy failed, but reconnect will be attempted. `reconnect_attempts=1` for the first event and increases for each subsequent event. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Reason for the attempt failure. | | `error_code` | [Error code](#proxy_session_finished_error_code) for the attempt failure. | | `source_port` | Source port where proxy connection originated from. | | `reconnect_attempts` | Number of times connection failed and reconnection was attempted. | | `dest_host` | Host name of the proxy destination (if proxying is configured with IP address, will have the same value as `dest_ip`). | | `dest_ip` | Proxy destination IP. | | `dest_port` | Proxy destination port. | | `source_ip` | Source IP where proxy connection originated from. | | `master_user` | If proxying is done with a master user authentication, contains the full username of master user. | | `local_ip` | Local IP address. | | `local_name` | TLS SNI hostname, if given. - Added: 2.4.0 | | `local_port` | Local port. | | `remote_ip` | Remote IP address. | | `remote_port` | Remote port. | | `user` | Full username. | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service e.g. `submission`, `imap`. - Added: 2.4.0 | ### `proxy_session_started` Connection to proxy destination has started. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `dest_host` | Host name of the proxy destination (if proxying is configured with IP address, will have the same value as `dest_ip`). | | `dest_ip` | Proxy destination IP. | | `dest_port` | Proxy destination port. | | `source_ip` | Source IP where proxy connection originated from. | | `master_user` | If proxying is done with a master user authentication, contains the full username of master user. | | `local_ip` | Local IP address. | | `local_name` | TLS SNI hostname, if given. - Added: 2.4.0 | | `local_port` | Local port. | | `remote_ip` | Remote IP address. | | `remote_port` | Remote port. | | `user` | Full username. | | `service` | Same as `protocol` - Removed: 2.4.0 | | `protocol` | Name of service e.g. `submission`, `imap`. - Added: 2.4.0 | ### `push_notification_finished` Push notification event was sent. See [push notification stats](/main/core/config/statistics.html#push-notifications). | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `mailbox` | Mailbox for event. | | `user` | Username of the user. | | `session` | Session ID for the storage session. | | `service` | Name of the service. Examples: `imap`, `pop3`, `lmtp`, ... - Added: 2.4.0 | ### `server_connection_connected` Outgoing server connection was either successfully established or failed. TIP Currently it is not possible to know which one happened. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `source_ip` | Source IP address used for the outgoing TCP connection. This is set only if a specific source IP was explicitly requested. | | `dest_ip` | TCP connection's destination IP address. | | `dest_port` | TCP connection's destination port. | | `dest_host` | TCP connection's destination hostname, if known. | | `socket_path` | UNIX socket connection's path. | | `remote_pid` | Remote UNIX socket server's process ID. | | `remote_uid` | Remote UNIX socket server's system user ID. | ### `server_connection_disconnected` Server connection is terminated. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `reason` | Disconnection reason. | | `source_ip` | Source IP address used for the outgoing TCP connection. This is set only if a specific source IP was explicitly requested. | | `dest_ip` | TCP connection's destination IP address. | | `dest_port` | TCP connection's destination port. | | `dest_host` | TCP connection's destination hostname, if known. | | `socket_path` | UNIX socket connection's path. | | `remote_pid` | Remote UNIX socket server's process ID. | | `remote_uid` | Remote UNIX socket server's system user ID. | ### `sieve_action_finished` Emitted when sieve action is completed successfully. FieldDescription `duration` Duration of the event (in microseconds) `reason_code` List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. **Action:** `action_name` `action_script_location` The location string for this Sieve action (a combination of ": line ". `redirect_target` The target address for the redirect action. `notify_target` The list of target addresses for the notify action. `report_target` The target address for the report action. `report_type` The feedback type for the report action. `fileinto_mailbox` The target mailbox for the fileinto/keep action. `pipe_program` The name of the program being executed by the pipe action. `message_id` The message-id of the message being filtered. `mail_from` Envelope sender address if available. `rcpt_to` Envelope recipient address if available. `user` Username of the user. ### `sieve_runtime_script_finished` Finished evaluating a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `script_name` | The name of the Sieve script as it is visible to the user. | | `script_location` | The full location string of the Sieve script. | | `binary_path` | The path of the Sieve binary being executed (if it is not only in memory). | | `error` | If present, this field indicates that the script execution has failed. The error message itself is very simple. | | `message_id` | The message-id of the message being filtered. | | `mail_from` | Envelope sender address if available. | | `rcpt_to` | Envelope recipient address if available. | | `user` | Username of the user. | ### `sieve_runtime_script_started` Started evaluating a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `script_name` | The name of the Sieve script as it is visible to the user. | | `script_location` | The full location string of the Sieve script. | | `binary_path` | The path of the Sieve binary being executed (if it is not only in memory). | | `error` | If present, this field indicates that the script execution has failed. The error message itself is very simple. | | `message_id` | The message-id of the message being filtered. | | `mail_from` | Envelope sender address if available. | | `rcpt_to` | Envelope recipient address if available. | | `user` | Username of the user. | ### `sieve_script_activated` Activated a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_script_closed` Closed a Sieve script (after reading it). | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_script_deleted` Deleted a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_script_opened` Opened a Sieve script for reading (e.g. for ManageSieve GETSCRIPT or compiling it at delivery). | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_script_renamed` Renamed a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `old_script_name` | Old name of the Sieve script. | | `new_script_name` | New name for the Sieve script. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_storage_save_finished` Finished saving a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `script_name` | Name of the Sieve script. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `sieve_storage_save_started` Started saving a Sieve script. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `script_name` | Name of the Sieve script. | | `storage_driver` | The driver name of the Sieve storage (`file`, `ldap`, or `dict`). | | `script_location` | The location string for the Sieve script. | | `error` | Error message for when storage operation has failed. | | `user` | Username of the user. | ### `smtp_server_command_finished` The command is finished. Either a success reply was sent for it or it failed somehow. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `status_code` | SMTP status code for the (first) reply. This is = 9000 for aborted commands (e.g., when the connection is closed prematurely). | | `enhanced_code` | SMTP enhanced status code for the (first) reply. This is "9.0.0" for aborted commands (e.g., when the connection is closed prematurely). | | `error` | Error message for the reply. There is no field for a success message. | | `cmd_name` | Name of the command. | | `cmd_input_name` | SMTP command name exactly as sent (e.g. `MaIL`) regardless of whether or not it is valid. | | `cmd_args` | SMTP command's full parameters (e.g. ``). | | `cmd_human_args` | SMTP command's full parameters, as human-readable output. For SMTP, this is currently identical to `cmd_args`. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | | `session` | The session ID for this connection (same as `connection_id`). | ### `smtp_server_command_started` The command is received from the client. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `cmd_name` | Name of the command. | | `cmd_input_name` | SMTP command name exactly as sent (e.g. `MaIL`) regardless of whether or not it is valid. | | `cmd_args` | SMTP command's full parameters (e.g. ``). | | `cmd_human_args` | SMTP command's full parameters, as human-readable output. For SMTP, this is currently identical to `cmd_args`. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | | `session` | The session ID for this connection (same as `connection_id`). | ### `smtp_server_transaction_finished` Transaction is finished or failed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `status_code` | SMTP status code for the (first failure) reply. This is = 9000 for aborted commands (e.g., when the connection is closed prematurely). | | `enhanced_code` | SMTP enhanced status code for the (first failure) reply. This is "9.0.0" for aborted commands (e.g., when the connection is closed prematurely). | | `error` | Error message for the first failure reply. There is no field for a success message. | | `recipients` | Total number of recipients. | | `recipients_aborted` | The number of recipients that got aborted before these could either finish or fail. This means that the transaction failed early somehow while these recipients were still being processed by the server. | | `recipients_denied` | The number of recipients denied by the server using a negative reply to the RCPT command. | | `recipients_failed` | The number of recipients that failed somehow (includes denied recipients, but not aborted recipients). | | `recipients_succeeded` | The number of recipients for which the transaction finally succeeded. | | `is_reset` | The transaction was reset (RSET) rather than finishing with a DATA/BDAT command as it normally would. This happens when client side issues the RSET command. Note that a reset event is a success (no error field is present). | | `transaction_id` | Transaction ID used by the server for this transaction (this ID is logged, mentioned in the DATA reply and part of the "Received:" header). It is based on the connection\_id with a ":" sequence number suffix. | | `session` | The session ID for this connection (same as `connection_id`). | | `mail_from` | Sender address. | | `mail_param_auth` | The value of the AUTH parameter for the MAIL command. | | `mail_param_body` | The value of the BODY parameter for the MAIL command. | | `mail_param_envid` | The value of the ENVID parameter for the MAIL command. | | `mail_param_ret` | The value of the RET parameter for the MAIL command. | | `mail_param_size` | The value of the SIZE parameter for the MAIL command. | | `data_size` | The number data of bytes received from the client. This field is only present when the transaction finished receiving the DATA command. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | ### `smtp_server_transaction_rcpt_finished` The transaction is finished or failed for this particular recipient. When successful, this means the DATA command for the transaction yielded success for that recipient (even for SMTP this event is generated for each recipient separately). Recipients can fail at various stages, particularly at the actual RCPT command where the server can deny the recipient. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `status_code` | SMTP status code for the reply. This is = 9000 for aborted commands (e.g., when the connection is closed prematurely). | | `enhanced_code` | SMTP enhanced status code for the reply. This is "9.0.0" for aborted commands (e.g., when the connection is closed prematurely). | | `error` | Error message for the reply if it is a failure. There is no field for a success message. | | `dest_host` | LMTP proxying only: Proxy destination hostname. - Added: 2.4.0 | | `dest_ip` | LMTP proxying only: Proxy destination IP address. - Added: 2.4.0 | | `rcpt_to` | Recipient address. | | `rcpt_param_notify` | The value of the NOTIFY parameter for the RCPT command. | | `rcpt_param_orcpt` | The address value of the ORCPT parameter for the RCPT command. | | `rcpt_param_orcpt_type` | The address type (typically "rfc822") of the ORCPT parameter for the RCPT command. | | `session` | The session ID for this connection (same as `connection_id`). | | `transaction_id` | Transaction ID used by the server for this transaction (this ID is logged, mentioned in the DATA reply and part of the "Received:" header). It is based on the connection\_id with a ":" sequence number suffix. | | `mail_from` | Sender address. | | `mail_param_auth` | The value of the AUTH parameter for the MAIL command. | | `mail_param_body` | The value of the BODY parameter for the MAIL command. | | `mail_param_envid` | The value of the ENVID parameter for the MAIL command. | | `mail_param_ret` | The value of the RET parameter for the MAIL command. | | `mail_param_size` | The value of the SIZE parameter for the MAIL command. | | `data_size` | The number data of bytes received from the client. This field is only present when the transaction finished receiving the DATA command. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | ### `smtp_server_transaction_started` The transaction is started. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `transaction_id` | Transaction ID used by the server for this transaction (this ID is logged, mentioned in the DATA reply and part of the "Received:" header). It is based on the connection\_id with a ":" sequence number suffix. | | `session` | The session ID for this connection (same as `connection_id`). | | `mail_from` | Sender address. | | `mail_param_auth` | The value of the AUTH parameter for the MAIL command. | | `mail_param_body` | The value of the BODY parameter for the MAIL command. | | `mail_param_envid` | The value of the ENVID parameter for the MAIL command. | | `mail_param_ret` | The value of the RET parameter for the MAIL command. | | `mail_param_size` | The value of the SIZE parameter for the MAIL command. | | `data_size` | The number data of bytes received from the client. This field is only present when the transaction finished receiving the DATA command. | | `connection_id` | The session ID for this connection. The connection ID is forwarded through proxies, allowing correlation between sessions on frontend and backend systems. | | `protocol` | The protocol used by the connection; i.e., either `smtp` or `lmtp`. | ### `smtp_submit_finished` Finished the message submission. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Error message for submission failure. | | `mail_from` | The envelope sender for the outgoing message. | | `recipients` | The number of recipients for the outgoing message. | | `data_size` | The size of the outgoing message. | ### `smtp_submit_started` Started message submission. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | ### `sql_connection_finished` Connection to SQL server is closed. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `sql_driver` | Name of the sql driver, e.g. `mysql` or `cassandra`. | ### `sql_query_finished` Response was received to SQL query. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Human readable error. | | `error_code` | Error code (if available). | | `error_code_extended` | [Extended result code](https://sqlite.org/rescode.html#extrc) for the error, which describes the failure more precisely than `error_code` (SQLite only). - Added: 2.4.5 | | `query_first_word` | First word of the query (e.g. `SELECT`). | | `consistency` | Requested consistency for the query (Cassandra only). - Added: 2.4.0 | | `error_consistency` | Consistency attempted to be used by Cassandra for the failed query (Cassandra only). - Added: 2.4.0 | | `sql_driver` | Name of the sql driver, e.g. `mysql` or `cassandra`. | ### `sql_transaction_finished` SQL transaction was committed or rolled back. | Field | Description | | --- | --- | | `duration` | Duration of the event (in microseconds) | | `reason_code` | List of reason code strings why the event happened. See [event reasons](./events.html#event-reasons) for possible values. | | `error` | Human readable error. | | `error_code` | Error code (if available). | | `error_code_extended` | [Extended result code](https://sqlite.org/rescode.html#extrc) for the error, which describes the failure more precisely than `error_code` (SQLite only). - Added: 2.4.5 | | `sql_driver` | Name of the sql driver, e.g. `mysql` or `cassandra`. | ## Event Categories | Category | Description | Category Type | | --- | --- | --- | | `auth` | Authentication (server mainly) | `root` | | `auth-client` | Authentication client library | `root` | | `cassandra` | [Cassandra CQL](/main/core/config/sql/cassandra.html) events | `sql` | | `dict` | [Dictionary](/main/core/config/dict.html) library and drivers | `root` | | `dict-server` | [Dictionary](/main/core/config/dict.html) Dictionary server/proxy (dict process) | `root` | | `dns` | DNS client library | `root` | | `dns-worker` | DNS client library | `root` | | `fs` | FS library | `root` | | `fts` | [fts plugin](/main/core/plugins/fts.html) | `root` | | `fts-flatcurve` | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | `root` | | `http-client` | HTTP client library | `root` | | `http-server` | HTTP server library | `root` | | `imap` | [IMAP](/main/core/config/imap.html) process | `root` | | `imap-hibernate` | imap-hibernate process | `root` | | `imap-urlauth` | imap-urlauth process | `root` | | `imapc` | [imapc](/main/core/config/mailbox_formats/imapc.html) storage | `storage` | | `lda` | dovecot-lda process | `root` | | `lmtp` | [LMTP Server](/main/core/config/delivery/lmtp.html) process | `root` | | `local-delivery` | LDA/LMTP local delivery | `root` | | `lua` | [Lua script](/main/core/admin/lua/index.html) | `root` | | `mail-cache` | `dovecot.index.cache` file handling | `root` | | `mail-index` | `dovecot.index*` file handling | `root` | | `mailbox` | Mailbox (folder) | `storage` | | `maildir` | [Maildir](/main/core/config/mailbox_formats/maildir.html) storage | `storage` | | `managesieve` | [ManageSieve](/main/core/config/sieve/managesieve.html) | `root` | | `mbox` | [mbox](/main/core/config/mailbox_formats/mbox.html) storage | `storage` | | `mdbox` | [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) storage | `storage` | | `mysql` | [MySQL](/main/core/config/sql/mysql.html) events | `sql` | | `pgsql` | [PostgreSQL](/main/core/config/sql/postgresql.html) events | `sql` | | `pop3` | [POP3](/main/core/config/pop3.html) process | `root` | | `pop3c` | [pop3c](/main/core/config/mailbox_formats/pop3c.html) storage | `storage` | | `push-notification` | [push-notification plugin](/main/core/plugins/push_notification.html) | `root` | | `quota-status` | quota-status process | `root` | | `sdbox` | [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) storage | `storage` | | `service:` | Named service, e.g. `service:imap` or `service:auth` | `root` | | `sieve` | [Sieve](/main/core/config/sieve/overview.html) parent category | `sieve` | | `sieve-action` | Individual [Sieve](/main/core/config/sieve/overview.html) actions executed | `sieve` | | `sieve-execute` | [Sieve](/main/core/config/sieve/overview.html) script(s) being executed for a particular message; this envelops all of Sieve execution, both runtime and action execution | `sieve` | | `sieve-runtime` | Evaluation of individual [Sieve](/main/core/config/sieve/overview.html) scripts | `sieve` | | `sieve-storage` | [Sieve](/main/core/config/sieve/overview.html) storage | `sieve` | | `smtp-client` | SMTP/LMTP client | `root` | | `smtp-server` | SMTP/LMTP server | `root` | | `smtp-submit` | SMTP submission client | `root` | | `sql` | SQL parent category | `sql` | | `sqlite` | [SQLite](/main/core/config/sql/sqlite.html) events | `sql` | | `sqlpool` | SQL is used internally via "SQL connection pools" | `sql` | | `ssl` | [SSL/TLS](/main/core/config/ssl.html) connections | `root` | | `ssl-client` | Incoming [SSL/TLS](/main/core/config/ssl.html) connections | `root` | | `ssl-server` | Outgoing [SSL/TLS](/main/core/config/ssl.html) connections | `root` | | `storage` | Mail storage parent category | `storage` | | `submission` | [submission server](/main/core/config/submission.html) process | `root` | ## Event Reasons | Reason | Description | | --- | --- | | `doveadm:cmd_` | Doveadm command is being run. | | `fts:index` | Message is being added to the full text search index. Note that this reason won't be used for email opening events, because the emails are already opened by the indexer precache searching code. So usually the `indexer:index_mailbox` reason is what is wanted to be used. | | `fts:lookup` | Searching is accessing full text search index. | | `imap:cmd_` | IMAP command is being run. | | `imap:fetch_body` | A part of the message body is explicitly being fetched. If set, any other `imap:fetch_*` reasons aren't set since this alone is a reason for opening the mail. | | `imap:fetch_bodystructure` | IMAP BODY / BODYSTRUCTURE is being fetched. These should normally come from cache. | | `imap:fetch_header` | The full header (except maybe some listed headers) are being fetched. If set, any other `imap:fetch_*` reasons aren't set since this alone is a reason for opening the mail. | | `imap:fetch_header_fields` | Specific header fields are being fetched. These should normally come from cache. | | `imap:fetch_size` | RFC822.SIZE is being fetched. This should normally come from cache. | | `imap:notify_update` | The active NOTIFY command is sending updates to client. | | `imap:unhibernate` | IMAP client is being unhibernated. | | `indexer:index_mailbox` | Mailbox is being indexed. | | `lazy_expunge:expunge` | lazy\_expunge plugin is handling an expunge. Use for checking the refcount and for actually doing the lazy\_expunge move. | | `lmtp:cmd_data` | DATA command is being run. | | `lmtp:cmd_mail` | MAIL command is being run. | | `lmtp:cmd_rcpt` | RCPT command is being run. | | `mail:attachment_keywords` | `$HasAttachment` or `$HasNoAttachment` keyword is being generated. Only emitted when a mail body is opened (not when read from cache). | | `mail:date` | Date header. Only emitted when a mail body is opened (not when read from cache). | | `mail:header_fields` | A specified list of headers. These are normally expected to be returned from cache. Only emitted when a mail body is opened (not when read from cache). | | `mail:imap_bodystructure` | IMAP BODY / BODYSTRUCTURE Only emitted when a mail body is opened (not when read from cache). | | `mail:imap_envelope` | IMAP ENVELOPE Only emitted when a mail body is opened (not when read from cache). | | `mail:mime_parts` | MIME part structure. Only emitted when a mail body is opened (not when read from cache). | | `mail:snippet` | Message snippet / IMAP PREVIEW The other reasons may give further details why. Only emitted when a mail body is opened (not when read from cache). | | `mailbox:create` | Mailbox is being created | | `mailbox:delete` | Mailbox is being deleted | | `mailbox:rename` | Mailbox is being renamed | | `mailbox:sort` | Mails are being sorted (IMAP SORT) | | `mailbox:subscribe` | Mailbox is being subscribed | | `mailbox:thread` | Threading is being built for mails (IMAP THREAD) | | `mailbox:unsubscribe` | Mailbox is being unsubscribed | | `mailbox:update` | Mailbox metadata is being updated (e.g. `[doveadm mailbox update]()`) | | `mailbox:vsize` | Mailbox vsize is requested or updated | | `mdbox:rebuild` | mdbox storage is being rebuilt | | `pop3:cmd_` | POP3 command is being run. | | `pop3:initialize` | POP3 mailbox is being opened. | | `quota:count` | quota plugin is counting the mailbox's full size. This is normal with `quota=count` driver. | | `quota:recalculate` | Quota is being recalculated (e.g. `[doveadm quota recalc]()`) | | `storage:autoexpunge` | Mails are being autoexpunged | | `storage:mailbox_list_rebuild` | Mailbox list index is being rebuilt | | `submission:cmd_data` | DATA command is being run. | | `submission:cmd_mail` | MAIL command is being run. | | `submission:cmd_rcpt` | RCPT command is being run. | | `trash:clean` | trash plugin cleaning space | | `virtual:config_read` | virtual plugin mailbox configuration is being read. This may cause mailbox metadata to be accessed. | # Dovecot RFC Support INFO This is a non-exhaustive list of RFCs that Dovecot implements/supports. ## Email - Standard for ARPA Internet Text ([RFC 822](https://datatracker.ietf.org/doc/html/rfc822)) - Internet Message Format (updated RFC 822) ([RFC 2822](https://datatracker.ietf.org/doc/html/rfc2822)) - Multipurpose Internet Mail Extensions (MIME) (part 1) ([RFC 2045](https://datatracker.ietf.org/doc/html/rfc2045)) - Multipurpose Internet Mail Extensions (MIME) (part 2) ([RFC 2046](https://datatracker.ietf.org/doc/html/rfc2046)) - Multipurpose Internet Mail Extensions (MIME) (part 3) ([RFC 2047](https://datatracker.ietf.org/doc/html/rfc2047)) - Multipurpose Internet Mail Extensions (MIME) (part 4) ([RFC 2048](https://datatracker.ietf.org/doc/html/rfc2048)) - Multipurpose Internet Mail Extensions (MIME) (part 5) ([RFC 2049](https://datatracker.ietf.org/doc/html/rfc2049)) ## Authentication - Anonymous SASL Mechanism ([RFC 2245](https://datatracker.ietf.org/doc/html/rfc2245)) - Using TLS with IMAP, POP3 and ACAP ([RFC 2595](https://datatracker.ietf.org/doc/html/rfc2595)) - Using Digest Authentication as a SASL Mechanism (DIGEST-MD5) ([RFC 2831](https://datatracker.ietf.org/doc/html/rfc2831)) - Salted Challenge Response Authentication Mechanism (SCRAM) ([RFC 5802](https://datatracker.ietf.org/doc/html/rfc5802)) - SCRAM-SHA-256 and SCRAM-SHA-256-PLUS Simple Authentication and Security Layer (SASL) Mechanisms ([RFC 7677](https://datatracker.ietf.org/doc/html/rfc7677)) - GSS-API (Kerberos V5) SASL Mechanism ([RFC 4752](https://datatracker.ietf.org/doc/html/rfc4752)) - A Set of Simple Authentication and Security Layer (SASL) Mechanisms for OAuth ([RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628)) - [Google XOAUTH2 Protocol](https://developers.google.com/gmail/xoauth2_protocol) - Channel Bindings for TLS 1.3 ([RFC 9266](https://datatracker.ietf.org/doc/html/rfc9266)) ## POP3 - Post Office Protocol - Version 3 ([RFC 1939](https://datatracker.ietf.org/doc/html/rfc1939)) - POP3 Extension Mechanism ([RFC 2449](https://datatracker.ietf.org/doc/html/rfc2449)) - Using TLS with IMAP, POP3 and ACAP ([RFC 2595](https://datatracker.ietf.org/doc/html/rfc2595)) - The SYS and AUTH POP Response Codes ([RFC 3206](https://datatracker.ietf.org/doc/html/rfc3206)) - The Post Office Protocol (POP3) - Simple Authentication and Security Layer (SASL) Authentication Mechanism ([RFC 5034](https://datatracker.ietf.org/doc/html/rfc5034)) ## IMAP (Base) - IMAP4rev1 ([RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501)) - IMAP4 Multi-Accessed Mailbox Practice ([RFC 2180](https://datatracker.ietf.org/doc/html/rfc2180)) - Using TLS with IMAP, POP3 and ACAP ([RFC 2595](https://datatracker.ietf.org/doc/html/rfc2595)) - IMAP4 Implementation Recommendations ([RFC 2683](https://datatracker.ietf.org/doc/html/rfc2683)) ## IMAP (Extensions) - IMAP4 QUOTA extension ([RFC 2087](https://datatracker.ietf.org/doc/html/rfc2087)) - IMAP4 non-synchronizing literals (LITERAL+) ([RFC 2088](https://datatracker.ietf.org/doc/html/rfc2088)) - IMAP4 IDLE command ([RFC 2177](https://datatracker.ietf.org/doc/html/rfc2177)) - IMAP4 Login Referrals ([RFC 2221](https://datatracker.ietf.org/doc/html/rfc2221)) - IMAP4 Namespace ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) - IMAP4 ID extension ([RFC 2971](https://datatracker.ietf.org/doc/html/rfc2971)) - IMAP4 Child Mailbox Extension ([RFC 3348](https://datatracker.ietf.org/doc/html/rfc3348)) - IMAP4 MULTIAPPEND Extension ([RFC 3502](https://datatracker.ietf.org/doc/html/rfc3502)) - IMAP4 Binary Content Extension ([RFC 3516](https://datatracker.ietf.org/doc/html/rfc3516)) - IMAP4 UNSELECT command ([RFC 3691](https://datatracker.ietf.org/doc/html/rfc3691)) - IMAP4 Access Control List (ACL) Extension ([RFC 4314](https://datatracker.ietf.org/doc/html/rfc4314)) - IMAP UIDPLUS extension ([RFC 4315](https://datatracker.ietf.org/doc/html/rfc4315)) - IMAP URLAUTH Extension ([RFC 4467](https://datatracker.ietf.org/doc/html/rfc4467)) - IMAP CATENATE Extension ([RFC 4469](https://datatracker.ietf.org/doc/html/rfc4469)) - IMAP Extension for Conditional STORE Operation or Quick Flag Changes Resynchronization ([RFC 4551](https://datatracker.ietf.org/doc/html/rfc4551)) - IMAP4 Extension to SEARCH Command for Controlling What Kind of Information Is Returned ([RFC 4731](https://datatracker.ietf.org/doc/html/rfc4731)) - IMAP Extension for Simple Authentication and Security Layer (SASL) Initial Client Response ([RFC 4959](https://datatracker.ietf.org/doc/html/rfc4959)) - The IMAP COMPRESS Extension ([RFC 4978](https://datatracker.ietf.org/doc/html/rfc4978)) - WITHIN Search Extension to the IMAP Protocol ([RFC 5032](https://datatracker.ietf.org/doc/html/rfc5032)) - The IMAP ENABLE Extension ([RFC 5161](https://datatracker.ietf.org/doc/html/rfc5161)) - IMAP4 Extensions for Quick Mailbox Resynchronization ([RFC 5162](https://datatracker.ietf.org/doc/html/rfc5162)) - IMAP Extension for Referencing the Last SEARCH Result ([RFC 5182](https://datatracker.ietf.org/doc/html/rfc5182)) - IMAP Internationalization (I18NLEVEL=1 only) ([RFC 5255](https://datatracker.ietf.org/doc/html/rfc5255)) - IMAP SORT and THREAD Extensions ([RFC 5256](https://datatracker.ietf.org/doc/html/rfc5256)) - IMAP4 - LIST Command Extensions ([RFC 5258](https://datatracker.ietf.org/doc/html/rfc5258)) - Contexts for IMAP4 (ESORT and CONTEXT=SEARCH only) ([RFC 5267](https://datatracker.ietf.org/doc/html/rfc5267)) - The IMAP METADATA Extension ([RFC 5464](https://datatracker.ietf.org/doc/html/rfc5464)) - The IMAP NOTIFY Extension ([RFC 5465](https://datatracker.ietf.org/doc/html/rfc5465)) - Extended URLFETCH for Binary and Converted Parts ([RFC 5524](https://datatracker.ietf.org/doc/html/rfc5524)) - IMAP Response Codes ([RFC 5530](https://datatracker.ietf.org/doc/html/rfc5530)) - Support for PARTIAL in CATENATE and URLAUTH ([RFC 5550](https://datatracker.ietf.org/doc/html/rfc5550), Section 5.7.1) - IMAP4 Extension for Returning STATUS Information in Extended LIST ([RFC 5819](https://datatracker.ietf.org/doc/html/rfc5819)) - Display-Based Address Sorting for the IMAP4 SORT Extension ([RFC 5957](https://datatracker.ietf.org/doc/html/rfc5957)) - IMAP LIST Extension for Special-Use Mailboxes (SPECIAL-USE only) ([RFC 6154](https://datatracker.ietf.org/doc/html/rfc6154)) - IMAP4 Extension for Fuzzy Search ([RFC 6203](https://datatracker.ietf.org/doc/html/rfc6203)) - Support for IMAP Events in Sieve ([RFC 6785](https://datatracker.ietf.org/doc/html/rfc6785)) - Internet Message Access Protocol (IMAP) - MOVE Extension ([RFC 6851](https://datatracker.ietf.org/doc/html/rfc6851)) - IMAP Extensions: Quick Flag Changes Resynchronization (CONDSTORE) and Quick Mailbox Resynchronization (QRESYNC) (updated RFC 4551 and RFC 5162) ([RFC 7162](https://datatracker.ietf.org/doc/html/rfc7162)) - IMAP4 Non-synchronizing Literals (updated RFC 2088) ([RFC 7888](https://datatracker.ietf.org/doc/html/rfc7888)) - IMAP4 APPENDLIMIT Extension ([RFC 7889](https://datatracker.ietf.org/doc/html/rfc7889)) - IMAP Extension for STATUS=SIZE ([RFC 8438](https://datatracker.ietf.org/doc/html/rfc8438)) - IMAP4 Extension for Returning MYRIGHTS Information in Extended LIST ([RFC 8440](https://datatracker.ietf.org/doc/html/rfc8440)) - IMAP “$Important” Keyword and “\\Important” Special-Use Attribute ([RFC 8457](https://datatracker.ietf.org/doc/html/rfc8457)) - IMAP REPLACE Extension ([RFC 8508](https://datatracker.ietf.org/doc/html/rfc8508)) - Internet Message Access Protocol (IMAP) - SAVEDATE Extension ([RFC 8514](https://datatracker.ietf.org/doc/html/rfc8514)) - IMAP4 Extension: Message Preview Generation ([RFC 8970](https://datatracker.ietf.org/doc/html/rfc8970)) - IMAP Response Code for Command Progress Notification ([RFC 9585](https://datatracker.ietf.org/doc/html/rfc9585)) ## SMTP/LMTP (Base) - Simple Mail Transfer Protocol ([RFC 821](https://datatracker.ietf.org/doc/html/rfc821)) - Simple Mail Transfer Protocol (updated RFC 821) ([RFC 2821](https://datatracker.ietf.org/doc/html/rfc2821)) - Simple Mail Transfer Protocol (updated RFC 2821) ([RFC 5321](https://datatracker.ietf.org/doc/html/rfc5321)) - Local Mail Transfer Protocol ([RFC 2023](https://datatracker.ietf.org/doc/html/rfc2023)) - Message Submission for Mail ([RFC 6409](https://datatracker.ietf.org/doc/html/rfc6409)) ## SMTP/LMTP (Extensions) - SMTP Service Extension for Message Size Declaration ([RFC 1870](https://datatracker.ietf.org/doc/html/rfc1870)) - SMTP Service Extension for Returning Enhanced Error Codes ([RFC 2034](https://datatracker.ietf.org/doc/html/rfc2034)) - SMTP Service Extension for Command Pipelining ([RFC 2920](https://datatracker.ietf.org/doc/html/rfc2920)) - SMTP Service Extensions for Transmission of Large and Binary MIME Messages ([RFC 3030](https://datatracker.ietf.org/doc/html/rfc3030)) - SMTP Service Extension for Secure SMTP over Transport Layer Security ([RFC 3207](https://datatracker.ietf.org/doc/html/rfc3207)) - Message Submission BURL Extension ([RFC 4468](https://datatracker.ietf.org/doc/html/rfc4468)) - SMTP Service Extension for Authentication ([RFC 4954](https://datatracker.ietf.org/doc/html/rfc4954)) - SMTP Service Extension for 8-bit MIME Transport ([RFC 6152](https://datatracker.ietf.org/doc/html/rfc6152)) ## Dovecot Specific Extensions ### SEARCH=MIMEPART [SEARCH=MIMEPART IMAP Extension](/main/rfcs/draft-bosch-imap-search-mimepart-00.txt) - Body and Header searches not yet implemented. # All Dovecot Settings ## Settings ### `@mailbox_defaults` **Default:** _\[None\]_ **Value:** [Groups Includes](/main/core/settings/syntax.html#groups-includes) **Allowed Values:** `english` **See Also:** - `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)` **Changes:** - Changed: 2.4.3 Changed default `[mailbox_auto](/main/core/summaries/settings.html#mailbox_auto)` value for the mailboxes from `no` to `subscribe`. Group that expands to recommended English language mailbox names with `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)` flags added. ### `@metric_defaults` **Default:** _\[None\]_ **Value:** [Groups Includes](/main/core/settings/syntax.html#groups-includes) **Allowed Values:** `proxy``backend` **See Also:** - `[metric](/main/core/summaries/settings.html#metric)` Group that expands to recommended `[metric](/main/core/summaries/settings.html#metric)` settings in proxies or backends. ### `acl` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[acl_id](/main/core/summaries/settings.html#acl_id)` - `[acl_rights](/main/core/summaries/settings.html#acl_rights)` **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 Specifies an ACL entry on global, namespace or mailbox level. The filter name refers to the `[acl_id](/main/core/summaries/settings.html#acl_id)` setting. Has two settings: `[acl_id](/main/core/summaries/settings.html#acl_id)` The identifier in ACL. `[acl_rights](/main/core/summaries/settings.html#acl_rights)` The permission to grant - or deny - for this user. ### `acl_defaults_from_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [acl plugin](/main/core/plugins/acl.html) If enabled, the default ACLs for private and shared namespaces (but not public namespaces) are taken from the INBOX. This means that giving somebody access to your INBOX will give them access to all your other mailboxes as well, unless the specific mailboxes' ACLs override the INBOX's. ### `acl_dict_index` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.2 Should ACL dict updates assume that there is a reverse lookup index. This should be used with SQL/CQL based dicts. ### `acl_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 The ACL driver to use. This setting is **REQUIRED** - if empty, the acl plugin is disabled. Currently, there is a single driver available: `vfile`. This driver supports two ways of defining the ACL configuration: - _global_: ACL rules are applied to all users. - _per-mailbox_: Each mailbox has separate ACL rules. They are stored in a `dovecot-acl` file in each mailbox (or `[mail_control_path](/main/core/summaries/settings.html#mail_control_path)`) directory. This is the default. ### `acl_global_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Removed: 2.4.3 Location of global ACL configuration file. This option is deprecated, you should use `[acl](/main/core/summaries/settings.html#acl)` instead. ### `acl_globals_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [acl plugin](/main/core/plugins/acl.html) If enabled, don't try to find `dovecot-acl` files from mailbox directories. This reduces unnecessary disk I/O when only global ACLs are used. ### `acl_groups` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [acl plugin](/main/core/plugins/acl.html) A comma-separated string which contains all the groups the user belongs to. A user's UNIX groups have no effect on ACLs (you can enable them by using a special [post-login scripting](/main/core/config/post_login_scripting.html). The default ACL for mailboxes is to give the mailbox owner all permissions and other users none. Mailboxes in public namespaces don't have owners, so by default no one can access them. ### `acl_id` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` - [ACL File Format](/main/core/plugins/acl.html#acl-file-format) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 Specifies identity to match. See [ACL File Format](/main/core/plugins/acl.html#acl-file-format) for values. The `[acl](/main/core/summaries/settings.html#acl)` filter name refers to this setting. ### `acl_ignore` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 Can be used in global config, namespace, or mailbox level to ignore ACLs. ``` namespace ignore { acl_ignore = yes } ``` ### `acl_rights` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[acl](/main/core/summaries/settings.html#acl)` - [ACL File Format](/main/core/plugins/acl.html#acl-file-format) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 Specifies rights for this acl. See [ACL File Format](/main/core/plugins/acl.html#acl-file-format) for values. This is usually used in `[acl](/main/core/summaries/settings.html#acl)` block. ### `acl_sharing_map` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [dictionary](/main/core/config/dict.html) **Plugin:** [acl plugin](/main/core/plugins/acl.html) **Changes:** - Added: 2.4.0 A shared mailbox dictionary that defines which users may LIST mailboxes shared by other users. Not every dictionary driver is supported here. The map needs dict\_iterate which is not supported by redis and ldap. See [shared mailbox listing](/main/core/config/shared_mailboxes.html#shared-mailbox-listing) for further details on the contents of the dictionary entries. Example: ``` acl_sharing_map { dict file { path = /var/lib/dovecot/shared-mailboxes } } ``` ### `acl_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [ACL Master Users](/main/core/config/auth/master_users.html#acls) **Plugin:** [acl plugin](/main/core/plugins/acl.html) See `[auth_master_user_separator](/main/core/summaries/settings.html#auth_master_user_separator)` for the format of this setting. ### `apparmor_hats` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [apparmor plugin](/main/core/plugins/apparmor.html) List of AppArmor "hats" to change to when a user is loaded. Example: ``` apparmor_hats { hat_name = yes another_hat = yes } ``` ### `auth_allow_cleartext` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 If `no`, disables the LOGIN command and all other cleartext authentication unless SSL/TLS is used (LOGINDISABLED capability) or the connection is secured (see `[ssl](/main/core/summaries/settings.html#ssl)`). See [SSL configuration](/main/core/config/ssl.html#configuration-overview) for more detailed explanation of how this setting interacts with the `[ssl](/main/core/summaries/settings.html#ssl)` setting. This setting replaces the `disable_plaintext_auth` setting. ### `auth_allow_weak_schemes` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 Controls whether password schemes marked as weak are allowed to be used. See [Password Schemes](/main/core/config/auth/schemes.html) for disabled by default schemes. If enabled, will emit warning to logs. If a disabled scheme is used, an error is logged. Notably, any explicitly cleartext schemes (such as PLAIN), CRAM-MD5, and DIGEST-MD5 are not affected by this setting. ### `auth_anonymous_username` **Default:** `anonymous` **Value:** [string](/main/core/settings/types.html#string) This specifies the username to be used for users logging in with the ANONYMOUS SASL mechanism. ### `auth_cache_negative_ttl` **Default:** `1hour` **Value:** [time](/main/core/settings/types.html#time) This sets the time to live for negative hits to passdb or userdb (i.e., when the user is not found or there is a password mismatch). The value `0` completely disables caching of these hits. ### `auth_cache_size` **Default:** _\[None\]_ **Value:** [size](/main/core/settings/types.html#size) The authentication cache size (e.g., `10M`). `[auth_cache_size = 0](/main/core/summaries/settings.html#auth_cache_size)` disables use of the authentication cache. A typical passdb cache entry is around 50 bytes and a typical userdb cache entry is around 100-200 bytes, depending on the amount of information your user and password database lookups return. ### `auth_cache_ttl` **Default:** `1hour` **Value:** [time](/main/core/settings/types.html#time) Time to live for cache entries. After the TTL expires, the cached record is no longer used, unless the main database look-up returns internal failure. Entries are removed from the cache only when the cache is full and a new entry is to be added. ### `auth_cache_verify_password_with_worker` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) The auth master process by default is responsible for the hash verifications. Setting this to `yes` moves the verification to auth-worker processes. This allows distributing the hash calculations to multiple CPU cores, which could make sense if strong hashes are used. ### `auth_debug` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Deprecated: 2.4.0 The setting is obsolete, and kept only for backwards compatibility. Use `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)` instead. Enables all authentication debug logging (also enables `[auth_verbose](/main/core/summaries/settings.html#auth_verbose)`). Passwords are logged as ``. ### `auth_debug_passwords` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) This setting adjusts log verbosity. In the event of password mismatches, the passwords and the scheme used are logged so that the problem can be debugged. Note: You also need to enable `[log_debug = category=auth](/main/core/summaries/settings.html#log_debug)`. ### `auth_default_domain` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 This setting indicates the default realm/domain to use if none has been specified. The setting is used for both SASL realms and appending an `@domain` element to the username in cleartext logins. ### `auth_failure_delay` **Default:** `2secs` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - `[auth_internal_failure_delay](/main/core/summaries/settings.html#auth_internal_failure_delay)` This is the delay before replying to failed authentication attempts. Using [passdb: nodelay Extra Field](/main/core/config/auth/passdb.html#nodelay) bypasses this setting. This setting defines the interval for which the authentication process flushes all auth failures. Thus, this is the maximum interval a user may encounter. However, there can be additional delays added by [Authentication Penalty](/main/core/config/auth/penalty.html). This setting doesn't affect internal failures. See `[auth_internal_failure_delay](/main/core/summaries/settings.html#auth_internal_failure_delay)`. ### `auth_gssapi_hostname` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) This supplies the hostname to use in Generic Security Services API (GSSAPI) principal names. Use `"$ALL"` (with the quotation marks) to allow all keytab entries. ### `auth_internal_failure_delay` **Default:** `2secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[auth_failure_delay](/main/core/summaries/settings.html#auth_failure_delay)` **Changes:** - Added: 2.4.0 The delay before replying to client when authentication fails with internal failure. An additional 0..50% delay is added on top of this to prevent thundering herd issues. This setting is intended to prevent clients from hammering the server with immediate retries. ### `auth_krb5_keytab` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) This specifies the Kerberos keytab to use for the GSSAPI mechanism. Note: You may need to set the auth service to run as root in order for this file to be readable. ### `auth_master_user_separator` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The separator to use to enable master users to login by specifying the master username within the normal username string (i.e., not using the SASL mechanism's master support). Example: ``` # Allows master login of the format * # E.g. if user = foo, and master_user = muser, # login username = foo*muser auth_master_user_separator = * ``` ### `auth_mechanisms` **Default:** `plain` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html) Here you can supply a space-separated list of the authentication mechanisms you wish to use. Example: ``` auth_mechanisms = plain login ``` ### `auth_policy` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [Authentication Policy](/main/core/config/auth/policy.html) **Changes:** - Added: 2.4.0 Filter for auth policy specific settings. ### `auth_policy_check_after_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Do policy lookup after authentication is completed? ### `auth_policy_check_before_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Do policy lookup before authentication is started? ### `auth_policy_hash_mech` **Default:** `sha256` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `md4``md5``sha1``sha256``sha512` **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Hash mechanism to use for password. ### `auth_policy_hash_nonce` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Cluster-wide nonce to add to hash. This should contain a secret randomly generated string, which is the same for each Dovecot server within the cluster. REQUIRED configuration when you want to use authentication policy. Example: ``` auth_policy_hash_nonce = ``` ### `auth_policy_log_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Only log what the policy server response would do? If `yes`, no request is made to the policy server. ### `auth_policy_reject_on_fail` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` If policy request fails for some reason, should users be rejected? ### `auth_policy_report_after_auth` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Report authentication result? If `no`, there will be no report for the authentication result. ### `auth_policy_request_attributes` **Default:** `login=%{requested_username} pwhash=%{hashed_password} remote=%{remote_ip} device_id=%{client_id} protocol=%{protocol} session_id=%{session} fail_type=%{fail_type}` **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` **Changes:** - Changed: 2.4.0 Default has changed. Request attributes specification. See [Auth Policy Variables](/main/core/config/auth/policy.html#list-of-fields) for variables that can be used for this setting. ### `auth_policy_server_api_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` Header and value to add to request (for API authentication). Note: See [https://en.wikipedia.org/wiki/Basic\_access\_authentication#Client\_side](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side) This can be used when you are using the weakforced policy server and the web listener password is "super": ``` $ echo -n wforce:super | base64 d2ZvcmNlOnN1cGVy ``` Then the correct value for this setting is: ``` auth_policy_server_api_header = Authorization: Basic d2ZvcmNlOnN1cGVy ``` ### `auth_policy_server_url` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) URL of the policy server. URL is appended with `?command=allow/report`. If URL ends with `&`, the `?` is not appended. REQUIRED configuration when you want to use authentication policy. Example: ``` auth_policy_server_url = http://example.com:4001/ ``` ### `auth_realms` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) This setting supplies a list of realms for those SASL authentication mechanisms that need them. Realms are an integral part of Digest-MD5. You will need to specify realms you want to advertise to the client in the config file: Example: ``` auth_realms = example.com another.example.com foo ``` ### `auth_ssl_require_client_cert` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl_server_ca_file](/main/core/summaries/settings.html#ssl_server_ca_file)` - `[ssl_server_request_client_cert](/main/core/summaries/settings.html#ssl_server_request_client_cert)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) If `yes`, authentication fails when a valid SSL client certificate is not provided. ### `auth_ssl_username_from_cert` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl_server_cert_username_field](/main/core/summaries/settings.html#ssl_server_cert_username_field)` Setting to `yes` indicates that the username should be taken from the client's SSL certificate. Generally, this will be either `commonName` or `x500UniqueIdentifier`. The text is looked up from subject DN's specified field using OpenSSL's X509\_NAME\_get\_text\_by\_NID() function. By default the CommonName field is used. You can change the field with `[ssl_server_cert_username_field = name](/main/core/summaries/settings.html#ssl_server_cert_username_field)` setting (parsed using OpenSSL's OBJ\_txt2nid() function). `x500UniqueIdentifier` is a common choice. ### `auth_use_winbind` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) By default, the NTLM mechanism is handled internally. If `yes`, perform NTLM and GSS-SPNEGO authentication with Samba's winbind daemon and ntlm\_auth helper. This option is useful when you need to authenticate users against a Windows domain (either AD or NT). ### `auth_username_chars` **Default:** `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@` **Value:** [string](/main/core/settings/types.html#string) The list of the characters allowed in a username. If the user-supplied username contains a character not listed here, login automatically fails. This is an additional check to make sure the user can't exploit any quote-escaping vulnerabilities that may be connected with SQL/LDAP databases. If you want to allow all characters, leave the value empty. ### `auth_username_format` **Default:** `%{user | lower}` **Value:** [string](/main/core/settings/types.html#string) When this setting is used globally, it changes the username, including `%{user}` variable, for all passdb and userdb lookups. This setting can also be used in [passdb/userdb passwd\_file { auth\_username\_format }](/main/core/config/auth/databases/passwd_file.html) to change the username for the duration of the lookup. The `%{user}` variable is not changed. If used inside other passdbs/userdbs the setting is ignored. You can use the standard variables here. Examples: - `%{user | lower}`: Lowercases the username - `%{user | username}`: Drops the domain if one was supplied - `%{user | username}-AT-%{user | domain}`: Changes the "@" symbol into "-AT-" before lookup This translation is done after the changes specified with the `[auth_username_translation](/main/core/summaries/settings.html#auth_username_translation)` setting. ### `auth_verbose` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Adjust log verbosity. If `yes`, log unsuccessful authentication attempts and why they failed. ### `auth_verbose_passwords` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``yes``plain``sha1` In case of password mismatches, log the attempted password. You can also truncate the logged password to `n` chars by appending `:n` (e.g. `sha1:6`). Available transformations: - `plain`, `yes`: Output cleartext password (NOT RECOMMENDED) - `sha1`: Output SHA1 hashed password ### `auth_winbind_helper_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) This setting tells the system the path for Samba's ntlm\_auth helper binary. Example: ``` auth_winbind_helper_path = /usr/bin/ntlm_auth ``` ### `cassandra_connect_timeout` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Connection timeout. ### `cassandra_debug_queries` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to log CQL queries. ### `cassandra_delete_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when deleting from the database. See [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency). ### `cassandra_delete_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when deleting from the database fails with primary consistency. ### `cassandra_execution_retry_interval` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) If the driver supports speculative execution policy, configures constant speculative execution policy. See [https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative\_execution/index.html](https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative_execution/index.html) WARNING If Cassandra is completely unavailable and speculative execution is used, Cassandra cpp-driver library starts hanging all queries due to a bug. This may cause problems in the dict process even after Cassandra is back online. When this happens, "Dict server timeout" errors are being logged. ### `cassandra_execution_retry_times` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) If the driver supports speculative execution policy, configures constant speculative execution policy. See [https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative\_execution/index.html](https://docs.datastax.com/en/developer/java-driver/latest/manual/core/speculative_execution/index.html) WARNING If Cassandra is completely unavailable and speculative execution is used, Cassandra cpp-driver library starts hanging all queries due to a bug. This may cause problems in the dict process even after Cassandra is back online. When this happens, "Dict server timeout" errors are being logged. ### `cassandra_heartbeat_interval` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) How often to send keepalive packets to cassandra nodes. ### `cassandra_hosts` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of hosts or IP addresses to connect. ### `cassandra_idle_timeout` **Default:** `0` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to idle before disconnecting. ### `cassandra_io_thread_count` **Default:** `_[driver dependent]_` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Set number of IO threads to handle query requests. ### `cassandra_keyspace` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specifies the keyspace name to use. ### `cassandra_latency_aware_routing` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) When turned on, latency-aware routing tracks the latency of queries to avoid sending new queries to poorly performing Cassandra nodes. ### `cassandra_log_level` **Default:** `warn` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `critical``error``warn``info``debug``trace` Driver log level. ### `cassandra_log_retries` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to log about failed requests that are retried (which may or may not succeed after the retry). ### `cassandra_metrics` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path where to write JSON metrics. See [Cassandra: Metrics](/main/core/config/sql/cassandra.html#metrics). ### `cassandra_page_size` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) When a query returns many rows, it can be sometimes inefficient to return them as a single response message. Instead, the driver can break the results into pages which get returned as they are needed. This setting controls the size of each page. Set to `0` to disable. ### `cassandra_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication. ### `cassandra_port` **Default:** `9042` **Value:** [Port Number](/main/core/settings/types.html#port-number) CQL port to use. ### `cassandra_protocol_version` **Default:** `depends on driver version` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `3``4``5` Cassandra protocol version to use. It is good idea to specify this to avoid warnings about version handshake if the driver supports a higher protocol version than the server. INFO If you want to use server-side prepared statements, you need to use at least `4`. ### `cassandra_read_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Read consistency. ### `cassandra_read_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Read consistency if primary consistency fails. ### `cassandra_request_timeout` **Default:** `60s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait for a query to finish. ### `cassandra_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``cert-only``cert-ip` **See Also:** - [SSL configuration](/main/core/config/ssl.html#configuration-overview) - `[ssl_client_require_valid_cert](/main/core/summaries/settings.html#ssl_client_require_valid_cert)` Whether to use SSL when connecting to Cassandra, and how to verify the certificate: `no` Don't use SSL `cert-only` Verify the certificate, but not the IP address or host name. `cert-ip` Verify the certificate, and require IP address to match the certificate's common name or one of its subject alternative names. You can also skip certificate validation by setting `[ssl_client_require_valid_cert = yes](/main/core/summaries/settings.html#ssl_client_require_valid_cert)`. The `cassandra_ssl` setting value must something else than `no`. Configure SSL certificates using the `ssl_client_*` settings. See [SSL configuration](/main/core/config/ssl.html#configuration-overview). ### `cassandra_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Username for authentication. ### `cassandra_warn_timeout` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Emit warning if query takes longer than this. ### `cassandra_write_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when updating or inserting to the database. ### `cassandra_write_fallback_consistency` **Default:** `local-quorum` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``local-serial``serial``one``two``three``local-quorum``quorum``each-quorum``all` **See Also:** - [Cassandra: Consistency](/main/core/config/sql/cassandra.html#consistency) Write consistency when updating or inserting to the database fails with primary consistency. ### `cdb_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to the CDB database file. ### `charset_aliases` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **Plugin:** [charset-alias plugin](/main/core/plugins/charset_alias.html) List of `=` charsets. The "from" charsets will be treated as "to" charsets when decoding to UTF-8. Example: ``` charset_aliases { shift_jis = sjis-win euc-jp = eucjp-win iso-2022-jp = iso-2022-jp-3 } ``` ### `compress_bz2_block_size_100k` **Default:** `9` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [mail-compress plugin](/main/core/plugins/mail_compress.html) The compression block size to use. Must be between `1` (`100 000` bytes) and `9` (`900 000` bytes). ### `compress_deflate_level` **Default:** `6` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [mail-compress plugin](/main/core/plugins/mail_compress.html) The compression level to use for deflate compression. Must be between `0` (no compression) and `9`. ### `compress_gz_level` **Default:** `6` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [mail-compress plugin](/main/core/plugins/mail_compress.html) The compression level to use for gz compression. Must be between `0` (no compression) and `9`. ### `compress_zstd_level` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [mail-compress plugin](/main/core/plugins/mail_compress.html) The compression level to use for zstd compression. Must be between `1` and `22`. ### `crypt_acl_require_secure_key_sharing` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) If enabled, you cannot share a key to groups or someone without a public key. ### `crypt_global_private_key` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` - `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) List of global private key(s) to decrypt mails. Add `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` and optionally `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` inside each filter. ### `crypt_global_public_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Public key to encrypt files. Key must be in [PEM pkey format](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey). The PEM key may additionally be base64-encoded into a single line, which can make it easier to store into userdb extra fields. ### `crypt_private_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - [Mail Crypt Plugin: Converting EC key to PKEY](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey) - `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Private key in [Mail Crypt Plugin: Converting EC key to PKEY](/main/core/plugins/mail_crypt.html#converting-ec-key-to-pkey). The PEM key may additionally be base64-encoded into a single line, which can make it easier to store into userdb extra fields. Used inside `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` and `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` lists. ### `crypt_private_key_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Name of the private key inside `[crypt_global_private_key](/main/core/summaries/settings.html#crypt_global_private_key)` or `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)`. ### `crypt_private_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Password to decrypt `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)`. ### `crypt_user_key_curve` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Defines the elliptic curve to use for key generation. A key pair is generated for the user, and a key pair is generated for each folder. The folder key is encrypted using the user key. This must be set if you wish to use [folder keys](/main/core/plugins/mail_crypt.html#folder-keys) rather than [global keys](/main/core/plugins/mail_crypt.html#global-keys). With global keys (either RSA or EC keys), all keying material is taken from the global key settings and no key generation is performed. Any valid curve supported by the underlying cryptographic library is allowed. Example: ``` crypt_user_key_curve = secp521r1 ``` For EdDSA, you need to use X448 or X25519, case sensitive. ### `crypt_user_key_encryption_key` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` - `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) List of private key(s) to decrypt user's master private key. Add `[crypt_private_key_file](/main/core/summaries/settings.html#crypt_private_key_file)` and optionally `[crypt_private_key_password](/main/core/summaries/settings.html#crypt_private_key_password)` inside each filter. ### `crypt_user_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Password to decrypt user's master private key. ### `crypt_user_key_require_encrypted` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` - `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)` **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) If yes, require user's master private key to be encrypted with `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` or `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)`. If they are unset new user key generation will fail. This setting doesn't affect already existing non-encrypted keys. ### `crypt_write_algorithm` **Default:** `aes-256-gcm-sha256` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [mail-crypt plugin](/main/core/plugins/mail_crypt.html) Set the encryption algorithm for newly written mails. If empty, new mails are not encrypted. Existing encrypted mails (with any algorithm) can be decrypted regardless of this setting. ### `debug_log_path` **Default:** `[info_log_path](/main/core/summaries/settings.html#info_log_path)` **Value:** [string](/main/core/settings/types.html#string) The log file to use for debug messages. ### `default_client_limit` **Default:** `1000` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Default value for `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`, if not overridden by service-specific configuration. ### `default_idle_kill_interval` **Default:** `1 min` **Value:** [time](/main/core/settings/types.html#time) Default value for `[service_idle_kill_interval](/main/core/summaries/settings.html#service_idle_kill_interval)`, if not overridden by service-specific configuration. ### `default_internal_group` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` - `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` Define the default internal group. The `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` uses this as the default for giving mail processes UNIX socket access to various services. For proper security, no files or directories should be made writable for this group. ### `default_internal_user` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` Define the default internal user. Unprivileged processes run under the ID of the internal user. This user should be distinct from the login user, to prevent login processes from disturbing other processes. ### `default_login_user` **Default:** `dovenull` **Value:** [string](/main/core/settings/types.html#string) The user the login process should run as. This is the least trusted user in Dovecot: this user should not have access to anything at all. ### `default_process_limit` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Default value for `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`, if not overridden by service-specific configuration. ### `default_vsz_limit` **Default:** `256 M` **Value:** [size](/main/core/settings/types.html#size) Default value for `[service_vsz_limit](/main/core/summaries/settings.html#service_vsz_limit)`, if not overridden by service-specific configuration. ### `deliver_log_format` **Default:** `msgid=%{msgid}: %{message}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The format to use for logging mail deliveries. Variables that can be used for this setting (see `[Global variables](/main/core/settings/variables.html#global-variables)`): | Variable Name | Description | | --- | --- | | `%{message}` | Delivery status message (e.g., saved to INBOX) | | `%{msgid}` | | | `%{subject}` | Subject | | `%{from}` | From address | | `%{from_envelope}` | SMTP FROM envelope | | `%{size}` | Physical size | | `%{vsize}` | Virtual size | | `%{to_envelope}` | RCPT TO envelope | | `%{delivery_time}` | How many milliseconds to deliver the mail | | `%{session_time}` | LMTP session duration, not including `%{delivery_time}` | | `%{storage_id}` | Backend-specific ID for mail, e.g. Maildir filename | Example: ``` deliver_log_format = stime=%{session_time} msgid=%{msgid}: %{message} ``` ### `dict` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_name](/main/core/summaries/settings.html#dict_name)` - `[dict_driver](/main/core/summaries/settings.html#dict_driver)` Creates a new dict. The filter name refers to the `[dict_name](/main/core/summaries/settings.html#dict_name)` setting. Example: ``` dict file { # ... } ``` Since an empty `[dict_driver](/main/core/summaries/settings.html#dict_driver)` defaults to `[dict_name](/main/core/summaries/settings.html#dict_name)`, there is no need to specify the `[dict_driver](/main/core/summaries/settings.html#dict_driver)` setting explicitly. ### `dict_driver` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) The dict driver to use. Defaults to `[dict_name](/main/core/summaries/settings.html#dict_name)`. ### `dict_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path for the dictionary file. ### `dict_map` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` Creates a new dict mapping. The filter name refers to the `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` setting. ### `dict_map_expire_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Field in the SQL table to use for tracking dict key expiration. This field is optional if no expiration is used by the code accessing the dict map. ### `dict_map_key_field` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` Creates a new field for the dict map. The filter name refers to the `[dict_map_key_field_value](/main/core/summaries/settings.html#dict_map_key_field_value)` setting. The fields are part of the SQL query looking up the dict key. ### `dict_map_key_field_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Field in the SQL table to use for the `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. ### `dict_map_key_field_type` **Default:** `string` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `string``int``uint``double``hexblob``uuid` **See Also:** - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Type of the field in the SQL table for the `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. ### `dict_map_key_field_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` - `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)` Variable in the `[dict_map_pattern](/main/core/summaries/settings.html#dict_map_pattern)` that maps to this `[dict_map_key_field](/main/core/summaries/settings.html#dict_map_key_field)`. The value must always begin with `$`. ### `dict_map_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The ldap filter to use to find the ldap entry. This setting is required for ldap `[dict_map](/main/core/summaries/settings.html#dict_map)` ### `dict_map_pattern` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Pattern that is matched to the accessed dict keys. The `[dict_map](/main/core/summaries/settings.html#dict_map)` filter name refers to this setting. If the pattern matches the key, this dict map (and no other) is used. The dict maps are processed in the order listed in the configuration file. ### `dict_map_sql_table` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL table to use for accessing this dict map. ### `dict_map_username_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Field in the SQL table to use for accessing private dict keys in this dict map. This setting is optional if only shared keys are accessed. ### `dict_map_value_field` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` - sql: Creates a new value for the dict map. The filter name refers to the `[dict_map_value_field_name](/main/core/summaries/settings.html#dict_map_value_field_name)` setting. Dict supports reading/writing multiple values for the same key. - ldap: Value to be returned from an ldap search, as a [variable-expression](/main/core/settings/variables.html) ### `dict_map_value_field_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)` Field in the SQL table to use for the `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)`. ### `dict_map_value_field_type` **Default:** `string` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `string``int``uint``double``hexblob``uuid` **See Also:** - `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)` Type of the field in the SQL table for the `[dict_map_value_field](/main/core/summaries/settings.html#dict_map_value_field)`. ### `dict_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_driver](/main/core/summaries/settings.html#dict_driver)` Name of the dict. The `[dict_driver](/main/core/summaries/settings.html#dict_driver)` setting defaults to this. ### `dict_proxy_idle_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to keep the connection open to dict server before disconnecting. `0` means immediate disconnection after finishing the operation. ### `dict_proxy_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[dict_name](/main/core/summaries/settings.html#dict_name)` Name of the dict to access in the dict server. This refers to the `[dict_name](/main/core/summaries/settings.html#dict_name)` setting. ### `dict_proxy_slow_warn` **Default:** `5s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Log a warning about dict lookups that take longer than this interval. ### `dict_proxy_socket_path` **Default:** `dict` **Value:** [string](/main/core/settings/types.html#string) Points to the dict server's UNIX socket. The path is relative to the the `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. This should be changed to `dict-async` if the dict driver supports asynchronous lookups (e.g. ldap, pgsql, cassandra, NOT mysql). The `dict-async` service allows more than one client, so this configuration prevents creating unnecessarily many dict processes. ### `dict_server` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[dict](/main/core/summaries/settings.html#dict)` Named filter for the dict server settings. Add the available named dicts for the dict server under this filter using the `[dict](/main/core/summaries/settings.html#dict)` settings. For example: ``` dict_server { dict quota { driver = sql # ... } dict acl { driver = file # ... } } ``` ### `dns_client_timeout` **Default:** `10s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for DNS lookups. ### `dotlock_use_excl` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, rely on O\_EXCL to work when creating dotlock files. NFS has supported O\_EXCL since version 3, so `yes` should be safe to use by default. ### `doveadm_allowed_commands` **Default:** `ALL` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Lists the commands that the client may use with the doveadm server. The setting `ALL` allows all commands. WARNING This setting provides rather weak security. Do not assume that it is safe to give doveadm access to untrusted users by simply limiting the allowed commands. Many commands (especially `sync`, `backup` and `import`) have parameters that cannot safely be accessed by untrusted users. ### `doveadm_api_key` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[doveadm_password](/main/core/summaries/settings.html#doveadm_password)` Set an API key for use of the HTTP API for the doveadm server. If set, this allows HTTP clients to authenticate to the doveadm HTTP server using the X-API-Key header, which contains the API key base64 encoded. This API key must be kept secret. The doveadm API allows doing almost anything in the server, including accessing users' mailboxes. It's possible to use both `[doveadm_password](/main/core/summaries/settings.html#doveadm_password)` and API key. This allows clients to use either method to authenticate. ### `doveadm_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[doveadm_api_key](/main/core/summaries/settings.html#doveadm_api_key)` Specify the password, which is used by both doveadm server and client. The client sends this password to the server for authentication, and the server accepts only this password. Alternatively, doveadm HTTP API can be authenticated to using `[doveadm_api_key](/main/core/summaries/settings.html#doveadm_api_key)`. This password must be kept secret. The doveadm API allows doing almost anything in the server, including accessing users' mailboxes. If doveadm server is accessed via UNIX socket that has 0600 permissions, the authentication is skipped. However, the doveadm HTTP API always requires authentication. ### `doveadm_port` **Default:** _\[None\]_ **Value:** [Port Number](/main/core/settings/types.html#port-number) The destination port to be used for the next doveadm proxying hop. This implicitly enables doing a passdb lookup for finding the proxy settings. A value of `0` means that proxying is not in use. This also means no passdb lookup is done - only userdb lookup. ### `doveadm_server` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) Filter for doveadm server specific settings. ### `doveadm_socket_path` **Default:** `doveadm-server` **Value:** [string](/main/core/settings/types.html#string) The UNIX socket or host (`host:port` syntax is allowed) for connecting to the doveadm server. ### `doveadm_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``ssl``starttls` TODO ### `doveadm_username` **Default:** `doveadm` **Value:** [string](/main/core/settings/types.html#string) The username for authentication to the doveadm service. ### `doveadm_worker_count` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) If the worker count set here is non-zero, mail commands are run via this many connections to the doveadm service. If `0`, commands are run directly in the same process. ### `dovecot_config_version` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Dovecot configuration version. It uses the same versioning as Dovecot in general, e.g. `3.0.5`. This must be the first setting in the configuration file. It specifies the configuration syntax, the used setting names and the expected default values. When there are default configuration changes in newer Dovecot versions, the existing installations will continue to work the same as before with the same default settings until this version number is increased. If there are other configuration changes, the old configuration will either keep working or there will be a clear failure at startup. ### `dovecot_storage_version` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Dovecot storage file format version. It uses the same versioning as Dovecot in general, e.g. `3.0.5`. It specifies the oldest Dovecot version that must be able to read files written by this Dovecot instance. The intention is that when upgrading Dovecot cluster, this setting is first kept as the old Dovecot version. Once the cluster is fully upgraded to a new version and there is no intention to rollback to the old version anymore, this version number can be increased. ### `dsync_alt_char` **Default:** `_` **Value:** [string](/main/core/settings/types.html#string) When the source and destination mailbox formats are different, it's possible for a mailbox name to exist on one source that isn't valid for the destination. Any invalid characters are replaced with the character indicated here. ### `dsync_commit_msgs_interval` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Dsync will commit this number of messages incrementally, to avoid huge transactions that fail. ### `dsync_features` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) This setting specifies features and workarounds that can be used with dsync. Options are specified in this setting via a space-separated list. Available options: `empty-header-workaround` Workaround for servers (e.g. Zimbra) that sometimes send FETCH replies containing no headers. `no-header-hashes` When this setting is enabled and one dsync side doesn't support mail GUIDs (i.e. imapc), there is no fallback to using header hashes. Instead, dsync assumes that all mails with identical IMAP UIDs contain the same mail contents. This can significantly improve dsync performance with some IMAP servers that don't support caching Date/Message-ID headers. ### `event_exporter` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` Creates a new event exporter. The filter name refers to the `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` setting. ### `event_exporter_driver` **Default:** `log` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `log``file``unix``http-post``drop` **See Also:** - [Event Export: Drivers](/main/core/config/events/export.html#drivers) The event exporter driver to use. ### `event_exporter_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to event log file with `[event_exporter_driver = file](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_format` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Event Export: Formats](/main/core/config/events/export.html#formats) Format used for serializing the event. ### `event_exporter_http_post_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Target URL for `[event_exporter_driver = http-post](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)` Name of the event exporter. It is referred by the `[metric_exporter](/main/core/summaries/settings.html#metric_exporter)` settings. ### `event_exporter_time_format` **Default:** `rfc3339` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `rfc3339``unix` **See Also:** - [Event Export: Formats](/main/core/config/events/export.html#formats) `rfc3339` Serialize timestamps as strings using the [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) format (`YYYY-MM-DDTHH:MM:SS.uuuuuuZ`). `unix` Serialize timestamps as a floating point number of seconds since the Unix epoch. ### `event_exporter_unix_connect_timeout` **Default:** `250ms` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[event_exporter_unix_path](/main/core/summaries/settings.html#event_exporter_unix_path)` Timeout when connecting to unix socket with `[event_exporter_driver = unix](/main/core/summaries/settings.html#event_exporter_driver)`. ### `event_exporter_unix_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[event_exporter_unix_connect_timeout](/main/core/summaries/settings.html#event_exporter_unix_connect_timeout)` Path to event unix socket with `[event_exporter_driver = unix](/main/core/summaries/settings.html#event_exporter_driver)`. ### `execute` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Execute Scripts](/main/core/config/execute.html) Configure external execution script. Used by various different features, such as [welcome](/main/core/plugins/welcome.html). Currently only a single execute block (per feature) is allowed. ### `execute_args` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) External execution script arguments. The parameters are split by space characters. Currently escape characters are not supported. ### `execute_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `unix``fork``tcp` **See Also:** - [Execute Scripts](/main/core/config/execute.html) How to execute the external script: `unix` Use UNIX socket connection to a `script` service listening in `[execute_unix_socket_path](/main/core/summaries/settings.html#execute_unix_socket_path)`. `fork` Fork and execute the `[execute_fork_path](/main/core/summaries/settings.html#execute_fork_path)` binary directly. `tcp` Use TCP connection to a `script` service listening in `[execute_tcp_host](/main/core/summaries/settings.html#execute_tcp_host)`:`[execute_tcp_port](/main/core/summaries/settings.html#execute_tcp_port)`. ### `execute_fork_path` **Default:** `[execute_name](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) Path to the binary that is executed with `[execute_driver = fork](/main/core/summaries/settings.html#execute_driver)`. ### `execute_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the execution script. This is the `[execute](/main/core/summaries/settings.html#execute)` named filter name. It is also used to provide a default driver-specific settings: `unix` Used as the default for `[execute_unix_socket_path](/main/core/summaries/settings.html#execute_unix_socket_path)`. `fork` Used as the default for `[execute_fork_path](/main/core/summaries/settings.html#execute_fork_path)`. `tcp` Used as the default for `[execute_tcp_host](/main/core/summaries/settings.html#execute_tcp_host)`:`[execute_tcp_port](/main/core/summaries/settings.html#execute_tcp_port)`. ### `execute_tcp_host` **Default:** `[execute_name = host:](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) TCP host where to connect to with `[execute_driver = tcp](/main/core/summaries/settings.html#execute_driver)`. ### `execute_tcp_port` **Default:** `[execute_name = :port](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) TCP port where to connect to with `[execute_driver = tcp](/main/core/summaries/settings.html#execute_driver)`. ### `execute_unix_socket_path` **Default:** `[execute_name](/main/core/summaries/settings.html#execute_name)` **Value:** [string](/main/core/settings/types.html#string) UNIX socket path where to connect to with `[execute_driver = unix](/main/core/summaries/settings.html#execute_driver)`. ### `fifo_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[fifo_listener_path](/main/core/summaries/settings.html#fifo_listener_path)` Creates a new FIFO listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[fifo_listener_path](/main/core/summaries/settings.html#fifo_listener_path)` setting. ### `fifo_listener_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Group of the listener file. Empty (default) means GID 0 (root/wheel). ### `fifo_listener_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode of the file. Note that `0600` is an octal value, while `600` is a different decimal value. Setting mode to `0` disables the listener. ### `fifo_listener_path` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` - `[fifo_listener](/main/core/summaries/settings.html#fifo_listener)` Path to the FIFO, relative to `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. The `[fifo_listener](/main/core/summaries/settings.html#fifo_listener)` filter name refers to this setting. ### `fifo_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `fifo_listener_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Owner of the listener file. Empty (default) means UID 0 (root). ### `first_valid_gid` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[last_valid_gid](/main/core/summaries/settings.html#last_valid_gid)` This setting and `[last_valid_gid](/main/core/summaries/settings.html#last_valid_gid)` specify the valid GID range for users. A user whose primary GID is outside this range is not allowed to log in. If the user belongs to any supplementary groups, the corresponding IDs are not set. ### `first_valid_uid` **Default:** `500` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[last_valid_uid](/main/core/summaries/settings.html#last_valid_uid)` This setting and `[last_valid_uid](/main/core/summaries/settings.html#last_valid_uid)` specify the valid UID range for users. A user whose UID is outside this range is not allowed to log in. ### `fs` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Dovecot Filesystems](/main/core/config/fs.html) - `[fs_name](/main/core/summaries/settings.html#fs_name)` - `[fs_driver](/main/core/summaries/settings.html#fs_driver)` Create a new [fs](/main/core/config/fs.html) to the list of filesystems. The filter name refers to `[fs_name](/main/core/summaries/settings.html#fs_name)` setting. Example: ``` fs posix { # ... } ``` Since an empty `[fs_driver](/main/core/summaries/settings.html#fs_driver)` default to `[fs_name](/main/core/summaries/settings.html#fs_name)` there is no need to specify `[fs_driver](/main/core/summaries/settings.html#fs_driver)` explicitly. It's possible to specify the same `fs` multiple times by separating the `[fs_name](/main/core/summaries/settings.html#fs_name)` and `[fs_driver](/main/core/summaries/settings.html#fs_driver)` settings: ``` fs compress1 { fs_driver = compress } fs compress2 { fs_driver = compress } ``` ### `fs_compress_read_plain_fallback` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [fs-compress plugin](/main/core/plugins/fs_compress.html) By default [fs-compress plugin](/main/core/plugins/fs_compress.html) fails if the file wasn't compressed. If this setting is enabled the file is returned as-is (i.e. allows reading plaintext files). ### `fs_compress_write_method` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Compression Methods](/main/core/plugins/mail_compress.html#compression-methods) Which [Compression Method](/main/core/plugins/mail_compress.html#compression-methods) to use for writing new files. Empty value means that compression is disabled. ### `fs_crypt_read_plain_fallback` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled files that are not encrypted are returned as-is. By default it results in a read error. ### `fs_dict_value_encoding` **Default:** `raw` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `raw``hex``base64` How to encode file contents into the dict value. ### `fs_driver` **Default:** `[fs_name](/main/core/summaries/settings.html#fs_name)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fs](/main/core/summaries/settings.html#fs)` - `[fs_name](/main/core/summaries/settings.html#fs_name)` The `[fs](/main/core/summaries/settings.html#fs)` driver to use. Defaults to `[fs_name](/main/core/summaries/settings.html#fs_name)`. ### `fs_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fs](/main/core/summaries/settings.html#fs)` - `[fs_driver](/main/core/summaries/settings.html#fs_driver)` Name of the `[fs](/main/core/summaries/settings.html#fs)`. The `[fs_driver](/main/core/summaries/settings.html#fs_driver)` setting default to this. ### `fs_posix_fsync` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Configure whether `fsync()` is called after writes to guarantee that the file is written to disk. ### `fs_posix_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode to use for creating files. ### `fs_posix_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Directory prefix where files are read from/written to. INFO The trailing `/` is not automatically added, so using e.g. `/tmp/foo` as prefix will cause `/tmp/foofilename` to be created. ### `fts` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Configures the used fts driver to perform [fts plugin](/main/core/plugins/fts.html) indexing. If not specified, FTS is disabled. The filter name refers to the `[fts_driver](/main/core/summaries/settings.html#fts_driver)` setting. Example: ``` fts solr { # ... } ``` ### `fts_autoindex` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[fts_autoindex_max_recent_msgs](/main/core/summaries/settings.html#fts_autoindex_max_recent_msgs)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) If enabled, index mail as it is delivered or appended. It can be overridden at the mailbox level, e.g. you can disable autoindexing for selected mailboxes using this setting: Example: ``` fts_autoindex = yes # ... mailbox trash { special_use = Trash fts_autoindex = no } mailbox spam { special_use = Junk fts_autoindex = no } mailbox storage/* { fts_autoindex = no } ``` ### `fts_autoindex_max_recent_msgs` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[fts_autoindex](/main/core/summaries/settings.html#fts_autoindex)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) To exclude infrequently accessed mailboxes from automatic indexing, set this value to the maximum number of `Recent` flagged messages that exist in the mailbox. A value of `0` means to ignore this setting. Mailboxes with more flagged `Recent` messages than this value will not be autoindexed, even though they get deliveries or appends. This is useful for, e.g., inactive Junk folders. Any folders excluded from automatic indexing will still be indexed, if a search on them is performed. Example: ``` fts_autoindex_max_recent_msgs = 999 ``` ### `fts_decoder_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** ```script``tika` **Plugin:** [fts plugin](/main/core/plugins/fts.html) Optional setting. If set, decode attachments to plaintext using the selected service and index the resulting plaintext. ### `fts_decoder_script_socket_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [fts plugin](/main/core/plugins/fts.html) **Changes:** - Changed: 2.4.0 Renamed from `fts_decoder`. Name of the script service used to decode the attachments. We strongly recommend using `[fts_decoder_driver = tika](/main/core/summaries/settings.html#fts_decoder_driver)` instead. Example: ``` fts_decoder_driver = script fts_decoder_script_socket_path = fts-decoder service fts-decoder { executable = script /path/to/fts-decoder.sh user = vmail unix_listener fts-decoder { mode = 0666 } } ``` ### `fts_decoder_tika` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[fts_decoder_tika_url](/main/core/summaries/settings.html#fts_decoder_tika_url)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) Named filter used for [Apache Tika](https://tika.apache.org/) HTTP lookups. For example: ``` fts_decoder_tika { http_client_request_timeout = 10s } ``` ### `fts_decoder_tika_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[fts_decoder_tika](/main/core/summaries/settings.html#fts_decoder_tika)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) **Changes:** - Added: 2.4.0 Basic authentication support (via URL) is added. - Changed: 2.4.0 Renamed from `fts_tika`. URL for [Apache Tika](https://tika.apache.org/) decoder for attachments. Example: ``` fts_decoder_driver = tika fts_decoder_tika_url = http://tikahost:9998/tika/ ``` ### `fts_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `dovecot``solr``flatcurve` **Plugin:** [fts plugin](/main/core/plugins/fts.html) Configures the used fts driver to perform [fts plugin](/main/core/plugins/fts.html) indexing. The `[fts](/main/core/summaries/settings.html#fts)` filter name refers to this setting. ### `fts_flatcurve_substring_search` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) If enabled, allows substring searches ([RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501) compliant). However, this requires significant additional storage space. Many users today expect "Google-like" behavior, which is prefix searching, so substring searching is arguably not the modern expected behavior anyway. Therefore, even though it is not strictly RFC compliant, prefix (non-substring) searching is enabled by default. ### `fts_header_excludes` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [fts plugin](/main/core/plugins/fts.html) The list of headers to include or exclude. - The default is the preexisting behavior, i.e. index all headers. - `includes` take precedence over `excludes`: if a header matches both, it is indexed. - The terms are case insensitive. - An asterisk `*` at the end of a header name matches anything starting with that header name. - The asterisk can only be used at the end of the header name. Prefix and infix usage of asterisk are not supported. Example: ``` fts_header_excludes { Received = yes DKIM-* = yes X-* = yes Comments = yes } fts_header_includes { X-Spam-Status = yes Comments = yes } ``` - `Received` headers, all `DKIM-` headers and all `X-` experimental headers are excluded, with the following exceptions: - `Comments` and `X-Spam-Status` are indexed anyway, as they match **both** `excludes` and `includes` lists. - All other headers are indexed. Example: ``` fts_header_excludes { * = yes } fts_header_includes { From = yes To = yes Cc = yes Bcc = yes Subject = yes Message-ID = yes In-* = yes X-CustomApp-* = yes } ``` - No headers are indexed, except those specified in the `includes`. ### `fts_header_includes` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[fts_header_excludes](/main/core/summaries/settings.html#fts_header_excludes)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) ### `fts_message_max_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **Plugin:** [fts plugin](/main/core/plugins/fts.html) **Changes:** - Added: 2.4.0 Maximum body size that is processed by fts. ### `fts_search_add_missing` **Default:** `body-search-only` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `body-search-only``yes` **Plugin:** [fts plugin](/main/core/plugins/fts.html) Should missing mails be added to FTS indexes before search? With `body-search-only` this is done only when the search query requests searching message bodies, i.e. header searches are not updating the FTS index. The unindexed mails are searched without FTS, i.e. either getting the headers from `dovecot.index.cache` or by opening the emails if the headers aren't in cache. This may be a useful optimization if the user's client only uses header searches. INFO Only the `yes` option guarantees consistent search results. Otherwise it's possible that the search results will be different depending on whether the search was performed via FTS index or not. ### `fts_search_read_fallback` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [fts plugin](/main/core/plugins/fts.html) If FTS lookup or indexing fails, fall back to searching without FTS (i.e. possibly opening all emails). This may timeout for large mailboxes and/or slow storage. ### `fts_search_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [fts plugin](/main/core/plugins/fts.html) When the full text search driver detects that the index isn't up-to-date, the indexer is told to index the messages and is given this much time to do so. If this time limit is reached, an error is returned, indicating that the search timed out during waiting for the indexing to complete: `NO [INUSE] Timeout while waiting for indexing to finish`. A value of `0` means no timeout. ### `fts_solr_batch_size` **Default:** `1000` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [fts-solr plugin](/main/core/plugins/fts_solr.html) Configures the number of mails sent to Solr in a single request. - With `[fts_autoindex = yes](/main/core/summaries/settings.html#fts_autoindex)` each new mail gets separately indexed on arrival, so `fts_solr_batch_size` only matters during the initial indexing of a mailbox. - With `[fts_autoindex = no](/main/core/summaries/settings.html#fts_autoindex)` new mails don't get indexed on arrival, so `fts_solr_batch_size` is used when indexing is triggered. ### `fts_solr_soft_commit` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [FTS Solr: Soft Commits](/main/core/plugins/fts_solr.html#soft-commits) **Plugin:** [fts-solr plugin](/main/core/plugins/fts_solr.html) Controls whether new mails are immediately searchable via Solr. ### `fts_solr_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [fts-solr plugin](/main/core/plugins/fts_solr.html) Required base URL for Solr. INFO Remember to add your core name if using solr 7+: `/solr/dovecot`. Example: ``` fts_solr_url = http://solr.example.org:8983/solr/dovecot/ fts_solr_batch_size = 1000 ``` ### `haproxy_timeout` **Default:** `3secs` **Value:** [time](/main/core/settings/types.html#time) When to abort the HAProxy connection when no complete header has been received. ### `haproxy_trusted_networks` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) A space-separated list of trusted network ranges for HAProxy connections. Connections from networks outside these ranges to ports that are configured for HAProxy are aborted immediately. ### `hostname` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) The hostname to be used in email messages sent out by the local delivery agent (such as the Message-ID: header), in LMTP replies, and as the hostname advertised by submission SMTP service. ### `http_client_connect_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` **Changes:** - Added: 2.4.0 Max time to wait for TCP connect and SSL handshake to finish before retrying. `0` = use `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)`. ### `http_client_delete_request_max_attempts` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` for `DELETE` requests. ### `http_client_delete_request_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` for `DELETE` requests. ### `http_client_max_connect_attempts` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of connection attempts to a host before all associated requests fail. If non-zero, the maximum will be enforced across all IPs for that host, meaning that IPs may be tried more than once eventually if the number of IPs is smaller than the specified maximum attempts. If the number of IPs is higher than the maximum attempts not all IPs are tried. If `0`, all IPs are tried at most once. ### `http_client_max_idle_time` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **Changes:** - Added: 2.4.0 Maximum time a connection will idle. If parallel connections are idle, the duplicates will end earlier based on how many idle connections exist to that same service. ### `http_client_max_parallel_connections` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of parallel connections per peer. ### `http_client_max_pipelined_requests` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of pipelined requests per connection. ### `http_client_proxy_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Password for HTTP proxy. ### `http_client_proxy_socket_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[http_client_proxy_url](/main/core/summaries/settings.html#http_client_proxy_url)` **Changes:** - Added: 2.4.0 UNIX socket path for HTTP proxy. Overrides `[http_client_proxy_url](/main/core/summaries/settings.html#http_client_proxy_url)`. ### `http_client_proxy_ssl_tunnel` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 If `no` the HTTP proxy delegates SSL negotiation to proxy, rather than creating a `CONNECT` tunnel through the proxy for the SSL link. ### `http_client_proxy_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[http_client_proxy_socket_path](/main/core/summaries/settings.html#http_client_proxy_socket_path)` **Changes:** - Added: 2.4.0 URL for HTTP proxy. Ignored if `[http_client_proxy_socket_path](/main/core/summaries/settings.html#http_client_proxy_socket_path)` is set. ### `http_client_proxy_username` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Username for HTTP proxy. ### `http_client_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Directory for writing raw log data for debugging purposes. WARNING Must be writable by the process creating this log. ### `http_client_read_request_max_attempts` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` for `GET` and `HEAD` requests. ### `http_client_read_request_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` for `GET` and `HEAD` requests. ### `http_client_request_absolute_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **Changes:** - Added: 2.4.0 Max total time to wait for HTTP request to finish, including all retries. `0` means no limit. ### `http_client_request_max_attempts` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of attempts for a request. ### `http_client_request_max_redirects` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Added: 2.4.0 Maximum number of redirects for a request. `0` = redirects refused. ### `http_client_request_timeout` **Default:** `1 min` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **Changes:** - Added: 2.4.0 Max time to wait for HTTP requests to finish before retrying. ### `http_client_write_request_max_attempts` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` for `PUT` and `POST` requests. ### `http_client_write_request_timeout` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) **See Also:** - `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` **Changes:** - Added: 2.4.0 If non-zero, override `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` for `PUT` and `POST` requests. ### `http_server_max_idle_time` **Default:** _\[None\]_ **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Maximum time a connection will idle. ### `http_server_max_pipelined_requests` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of pipelined requests per connection. ### `http_server_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Directory for writing raw log data for debugging purposes. ### `imap_acl_allow_anyone` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [imap-acl plugin](/main/core/plugins/imap_acl.html) By default Dovecot doesn't allow using the IMAP `anyone` or `authenticated` identifier, because it would be an easy way to spam other users in the system. If [mail-crypt plugin](/main/core/plugins/mail_crypt.html) is used, users who have different set of encryption keys cannot share mails, but sharing is possible within the scope of a key. ### `imap_capability` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Override the IMAP CAPABILITY response. Example of modifying capability banner by adding QUOTA and ACL, and removing IDLE: ``` imap_capability { QUOTA = yes ACL = yes IDLE = no } ``` Example of setting capability banner to exactly IMAP4rev1 SASL-IR IDLE: ``` imap_capability = IMAP4rev1 SASL-IR IDLE ``` ### `imap_client_workarounds` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Workarounds for various IMAP client bugs can be enabled here. The following values are currently supported: `delay-newmail` EXISTS/RECENT new-mail notifications are sent only in replies to NOOP and CHECK commands. Some clients, such as pre-2.1 versions of Mac OS X Mail, ignore them otherwise, and, worse, Outlook Express may report that the message is no longer on the server (note that the workaround does not help for OE6 if synchronization is set to Headers Only). `tb-extra-mailbox-sep` Because `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)` (mbox and dbox) confuses Thunderbird, causing extra / suffixes to mailbox names, Dovecot can be told to ignore the superfluous character instead of judging the mailbox name to be invalid. `tb-lsub-flags` Without this workaround, Thunderbird doesn't immediately recognize that LSUB replies with `[mailbox_list_layout = fs](/main/core/summaries/settings.html#mailbox_list_layout)` aren't selectable, and users may receive pop-ups with not selectable errors. Showing \\Noselect flags for these replies (e.g., in mbox use) causes them to be grayed out. ### `imap_fetch_failure` **Default:** `disconnect-immediately` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `disconnect-after``disconnect-immediately``no-after` Behavior when IMAP FETCH fails due to some internal error. Options: `disconnect-immediately` The FETCH is aborted immediately and the IMAP client is disconnected. `disconnect-after` The FETCH runs for all the requested mails returning as much data as possible. The client is finally disconnected without a tagged reply. `no-after` Same as disconnect-after, but tagged NO reply is sent instead of disconnecting the client. If the client attempts to FETCH the same failed mail more than once, the client is disconnected. This is to avoid clients from going into infinite loops trying to FETCH a broken mail. ### `imap_hibernate_timeout` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) How long to wait while the client is in IDLE state before moving the connection to the hibernate process, to save on memory use, and close the existing IMAP process. If nothing happens for this long while client is IDLEing, move the connection to imap-hibernate process and close the old imap process. This saves memory, because connections use very little memory in imap-hibernate process. The downside is that recreating the imap process back uses some additional system resources. ### `imap_id_retain` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) When proxying IMAP connections to other hosts, this variable must be enabled to forward the IMAP ID command provided by the client. This setting enables the `%{client_id}` variable for auth processes. See `[Authentication variables](/main/core/settings/variables.html#authentication-variables)`. ### `imap_id_send` **Default:** `name=%{dovecot:name}` **Value:** [String List](/main/core/settings/types.html#string-list) Which ID field names and values to send to clients. You can access the default values by using [Distribution Variables](/main/core/settings/variables.html#distribution-variables). ``` imap_id_send { name = %{dovecot:name} version = %{dovecot:version} support-url = http://example.com/ } ``` ### `imap_literal_minus` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Enable IMAP LITERAL- ([RFC 7888](https://datatracker.ietf.org/doc/html/rfc7888)) extension (replaces LITERAL+)? ### `imap_logout_format` **Default:** `in=%{input} out=%{output} deleted=%{deleted} expunged=%{expunged} trashed=%{trashed} hdr_count=%{fetch_hdr_count} hdr_bytes=%{fetch_hdr_bytes} body_count=%{fetch_body_count} body_bytes=%{fetch_body_bytes}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) This setting specifies the IMAP logout format string. Supported variables, in addition to `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` are: | Variable Name | Description | | --- | --- | | `%{input}` | Total number of bytes read from client | | `%{output}` | Total number of bytes sent to client | | `%{fetch_hdr_count}` | Number of mails with mail header data sent to client | | `%{fetch_hdr_bytes}` | Number of bytes with mail header data sent to client | | `%{fetch_body_count}` | Number of mails with mail body data sent to client | | `%{fetch_body_bytes}` | Number of bytes with mail body data sent to client | | `%{deleted}` | Number of mails where client added Deleted flag | | `%{expunged}` | Number of mails that client expunged, which does not include automatically expunged mails | | `%{autoexpunged}` | Number of mails that were automatically expunged after client disconnected | | `%{trashed}` | Number of mails that client copied/moved to the special\_use=Trash mailbox. | | `%{appended}` | Number of mails saved during the session | ### `imap_metadata` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Dovecot supports the IMAP METADATA extension ([RFC 5464](https://datatracker.ietf.org/doc/html/rfc5464)), which allows per-mailbox, per-user data to be stored and accessed via IMAP commands. Set this parameter's value to `yes` if you wish to activate the IMAP METADATA commands. Note: If activated, a dictionary needs to be configured, via the `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` setting. Example: ``` # Store METADATA information within user's Maildir directory mail_attribute { dict file { path = %{home}/Maildir/dovecot-attributes } } protocol imap { imap_metadata = yes } ``` ### `imap_urlauth_host` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) Specifies the host used for URLAUTH URLs. Only this host is accepted in the client-provided URLs. Using `*` value (not recommended) allows all hosts and the generated URLs use `[hostname](/main/core/summaries/settings.html#hostname)` as the host. An empty value disables the URLAUTH extension entirely. WARNING URLAUTH in current versions of Dovecot is broken in several ways. This will be fixed in the future, but activating URLAUTH support on production systems is not recommended. INFO This setting is REQUIRED for the URLAUTH ([RFC 4467](https://datatracker.ietf.org/doc/html/rfc4467)) extension to be active. ### `imap_urlauth_logout_format` **Default:** `in=%{input} out=%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[imap_urlauth_host](/main/core/summaries/settings.html#imap_urlauth_host)` Specifies the logout format used with the URLAUTH extension in IMAP operation. WARNING This setting is currently not used. Variables allowed: | Name | Description | | --- | --- | | `%{input}` | Total number of bytes read from the client | | `%{output}` | Total number of bytes sent to the client | ### `imap_urlauth_port` **Default:** `143` **Value:** [Port Number](/main/core/settings/types.html#port-number) **See Also:** - `[imap_urlauth_host](/main/core/summaries/settings.html#imap_urlauth_host)` The port is used with the URLAUTH extension in IMAP operation. ### `imapc_cmd_timeout` **Default:** `5 mins` **Value:** [time](/main/core/settings/types.html#time) How long to wait for a reply to an IMAP command sent to the remote IMAP server before disconnecting and retrying. ### `imapc_connection_retry_count` **Default:** `1` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) How many times to retry connection against a remote IMAP server? ### `imapc_connection_retry_interval` **Default:** `1 secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait between retries against a remote IMAP server? ### `imapc_connection_timeout_interval` **Default:** `30secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait before considering a connection attempt as timed out. ### `imapc_features` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Changed: 2.4.0 Several features are now automatically enabled and the respective flags dropped. In their place new flags to disable these features were added. List of features, optimizations, and workarounds that can be enabled. Changed: 2.4.3 Any remote IMAP server extension can now be disabled by using `no-` prefixed with the capability name. For example `no-sort`, `no-sort=display` or `no-quota`. **Features** `no-acl` If the [imap-acl plugin](/main/core/plugins/imap_acl.html) is loaded, the imapc acl feature is automatically enabled. With it IMAP ACL commands (MYRIGHTS, GETACL, SETACL, DELETEACL) are proxied to the imapc remote location. Note that currently these commands are attempted to be used even if the remote IMAP server doesn't advertise the ACL capability. To disable this feature either unload the [imap-acl plugin](/main/core/plugins/imap_acl.html) or provide this feature. Changed: 2.4.0 Earlier versions had an "acl" feature, which is now enabled by default. `no-delay-login` Immediately connect to the remote server. By default this is delayed until a command requires a connection. Changed: 2.4.0 Earlier versions had a "delay-login" feature, which is now enabled by default. `gmail-migration` Enable GMail-specific migration. Use IMAP `X-GM-MSGID` as POP3 UIDL. Add `$GMailHaveLabels` keyword to mails that have `X-GM-LABELS` except for `Muted` keyword (to be used for migrating only archived emails in `All Mails`). Add `[pop3_deleted_flag](/main/core/summaries/settings.html#pop3_deleted_flag)` to mails that don't exist in POP3 server. `no-modseq` Disable access to `MODSEQ` and `HIGHESTMODSEQ` fields. By default these fields are available if the remote server advertises the CONDSTORE or the QRESYNC capability. If modseqs are disabled, or not supported by the new server, they can still be used if imapc is configured to have local index files. Changed: 2.4.0 Earlier versions had a "modseq" feature, which is now enabled by default. `proxyauth` Use Sun/Oracle IMAP-server specific `PROXYAUTH` command to do master user authentication. Normally this would be done using the SASL PLAIN authentication. `send-id` Send session ID with the IMAP ID `x-session-ext-id` parameter. If `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` on the target host is configured to trust the connecting imapc IP, the session ID is preserved as the new imapc connection's session ID prefix. `throttle:::` When receiving \[THROTTLED\] response (from GMail), throttling is applied. **INIT** = initial throttling msecs (default: 50 ms), afterwards each subsequent \[THROTTLED\] doubles the throttling until **MAX** is reached (default: 16000 ms). When \[THROTTLED\] is not received for a while, it's shrunk again. The initial shrinking is done after **SHRINK** (default: 500 ms). If \[THROTTLED\] is received again within this timeout, it's doubled, otherwise both throttling and the next shrinking timeout is shrank to 3/4 the previous value. **Optimizations** `no-fetch-bodystructure` Disable fetching of IMAP `BODY` and `BODYSTRUCTURE` from the remote server. Instead, the whole message body is fetched to regenerate them. Changed: 2.4.0 Earlier versions had a "fetch-bodystructure" feature, which is now enabled by default. `no-fetch-headers` Disable fetching of specific message headers from the remote server using the IMAP `FETCH BODY.PEEK[HEADER.FIELDS(...)]` command. Instead, the whole header is fetched and the wanted headers are parsed from it. Changed: 2.4.0 Earlier versions had a "fetch-headers" feature, which is now enabled by default. `no-fetch-size` Disable fetching of message sizes from the remote server using the IMAP `FETCH RFC822.SIZE` command. Instead, the whole message body is fetched to calculate the size. Changed: 2.4.0 Earlier versions had a "rfc822.size" feature, which is now enabled by default. `no-metadata` Disable the detection of the `METADATA` capability from the remote server. The client will receive a `NO [UNAVAILABLE]` response for any request that requires access to metadata on the remote server (the same happens if the server does not announce the capability at all). `no-search` Disable searching messages using the IMAP `SEARCH` command. Instead, all the message headers/bodies are fetched to perform the search locally. Changed: 2.4.0 Earlier versions had a "search" feature, which is now enabled by default. **Workarounds** `fetch-fix-broken-mails` If a `FETCH` returns `NO` (but not `NO [LIMIT]` or `NO [SERVERBUG]`), assume the mail is broken in server and just treat it as if it were an empty email. DANGER This is often a dangerous option! It's not safe to assume that `NO` means a permanent error rather than a temporary error. This feature should be enabled only for specific users who have been determined to be broken. `fetch-msn-workarounds` Try to ignore wrong message sequence numbers in `FETCH` replies whenever possible, preferring to use the returned UID number instead. `no-examine` Use `SELECT` instead of `EXAMINE` even when we don't want to modify anything in the mailbox. This is a Courier-workaround where it didn't permanently assign `UIDVALIDITY` to an `EXAMINE`d mailbox, but assigned it for `SELECT`ed mailbox. `no-qresync` Added: 2.4.0 This can be used to work around a Zimbra bug where it doesn't send untagged "OK \[CLOSED\]" imap-resp-code when selecting a folder. `zimbra-workarounds` Fetch full message using `BODY.PEEK[HEADER] BODY.PEEK[TEXT]` instead of just `BODY.PEEK[]` because the header differs between these two when there are illegal control chars or 8bit chars. This mainly caused problems with dsync, but this should no longer be a problem and there's probably no need to enable this workaround. ### `imapc_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The remote IMAP host to connect to. ### `imapc_list_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Access only mailboxes under this prefix. Example, for a source IMAP server that uses an INBOX namespace prefix: ``` imapc_list_prefix = INBOX ``` ### `imapc_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_password](/main/core/summaries/settings.html#imapc_password)` - `[imapc_user](/main/core/summaries/settings.html#imapc_user)` The master username to authenticate as on the remote IMAP host. To authenticate as a master user but use a separate login user, the following configuration should be employed, where the credentials are represented by masteruser and masteruser-secret: ``` imapc_user = %{user} imapc_master_user = masteruser imapc_password = masteruser-secret ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `imapc_max_idle_time` **Default:** `29 mins` **Value:** [time](/main/core/settings/types.html#time) Send a command to the source IMAP server as a keepalive after no other command has been sent for this amount of time. Dovecot will send either `NOOP` or `DONE` to the source IMAP server. ### `imapc_max_line_length` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) The maximum line length to accept from the remote IMAP server. This setting is used to limit maximum memory usage. ### `imapc_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_master_user](/main/core/summaries/settings.html#imapc_master_user)` - `[imapc_user](/main/core/summaries/settings.html#imapc_user)` - `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` The authentication password for the remote IMAP server. If using master users, this setting will be the password of the master user. ### `imapc_port` **Default:** `143` **Value:** [Port Number](/main/core/settings/types.html#port-number) The port on the remote IMAP host to connect to. ### `imapc_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Log all IMAP traffic input/output to this directory. ### `imapc_sasl_mechanisms` **Default:** `plain` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The [SASL](/main/core/admin/sasl.html) mechanisms to use for authentication when connection to a remote IMAP server. The first one advertised by the remote IMAP sever is used. ``` imapc_sasl_mechanisms { external = yes plain = yes login = yes } ``` Supported mechanisms are: - ANONYMOUS - EXTERNAL - LOGIN - OAUTHBEARER - PLAIN - SCRAM-SHA-1 - SCRAM-SHA-1-PLUS - SCRAM-SHA-256 - SCRAM-SHA-256-PLUS - XOAUTH2 Note that `[imapc_password](/main/core/summaries/settings.html#imapc_password)` is ignored for `ANONYMOUS` and `EXTERNAL` mechanisms. For `OAUTHBEARER` and `XOAUTH2` `[imapc_password](/main/core/summaries/settings.html#imapc_password)` should be bearer token. ### `imapc_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``imaps``starttls` Use TLS to connect to the remote IMAP server. | Value | Description | | --- | --- | | `no` | No TLS | | `imaps` | Explicitly connect to remote IMAP port using TLS | | `starttls` | Use IMAP STARTTLS command to switch to TLS connection | ### `imapc_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[imapc_ssl](/main/core/summaries/settings.html#imapc_ssl)` **Changes:** - Removed: 2.4.0 Dropped in favor of using the global `[ssl_client_require_valid_cert](/main/core/summaries/settings.html#ssl_client_require_valid_cert)`. Verify remote IMAP TLS certificate? Verification may be disabled during testing, but should be enabled during production use. Only used if `[imapc_ssl](/main/core/summaries/settings.html#imapc_ssl)` is enabled. ### `imapc_user` **Default:** `%{owner_user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imapc_master_user](/main/core/summaries/settings.html#imapc_master_user)` - `[imapc_password](/main/core/summaries/settings.html#imapc_password)` - `[imapc_sasl_mechanisms](/main/core/summaries/settings.html#imapc_sasl_mechanisms)` The user identity to be used for performing a regular IMAP LOGIN to the source IMAP server. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `imapsieve_expunge_discarded` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) **Plugin:** [imap-sieve plugin](/main/core/plugins/imap_sieve.html) This setting determines whether de IMAPSieve plugin implicitly also expunges messages that were discarded by the executed Sieve script sequence; i.e., Sieve yielded no (implicit) keep. ### `imapsieve_from` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) **Plugin:** [imap-sieve plugin](/main/core/plugins/imap_sieve.html) Only execute the administrator Sieve scripts for the mailbox configured with `[sieve_script](/main/core/summaries/settings.html#sieve_script)` and type [before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after) when the message originates from the indicated mailbox. The filter name refers to the `[imapsieve_from_name](/main/core/summaries/settings.html#imapsieve_from_name)` setting. Therefore, the contained `[sieve_script](/main/core/summaries/settings.html#sieve_script)` blocks only apply when the source mailbox of the IMAP action match this filter. ### `imapsieve_from_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) **Plugin:** [imap-sieve plugin](/main/core/plugins/imap_sieve.html) The name of the source mailbox for IMAPSieve [before](/main/core/plugins/sieve.html#script-storage-type-before) or [after](/main/core/plugins/sieve.html#script-storage-type-after) scripts. The `[imapsieve_from](/main/core/summaries/settings.html#imapsieve_from)` filter refers to this setting. ### `imapsieve_url` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) **See Also:** - [sieve-imapsieve plugin](/main/core/plugins/sieve_imapsieve.html) **Plugin:** [imap-sieve plugin](/main/core/plugins/imap_sieve.html) If set, support for user Sieve scripts in IMAP is enabled. The value is an URL pointing to the ManageSieve server that users must use to upload their Sieve scripts. Leave this setting empty if you don't want users to have the ability to associate Sieve scripts with mailboxes. This has no effect on the administrator-controlled Sieve scripts. ``` imapsieve_url = sieve://sieve.example.com ``` ### `import_environment` **Default:** `TZ=%{env:TZ} CORE_OUTOFMEM=%{env:CORE_OUTOFMEM} CORE_ERROR=%{env:CORE_ERROR} PATH=%{env:PATH} MALLOC_MMAP_THRESHOLD_=131072 Also systemd environments: LISTEN_PID=%{env:LISTEN_PID} LISTEN_FDS=%{env:LISTEN_FDS} NOTIFY_SOCKET=%{env:NOTIFY_SOCKET}` **Value:** [String List](/main/core/settings/types.html#string-list) A list of environment key=value pairs, that are preserved and passed to all child processes. The value can be determined from the existing environment upon Dovecot startup or directly specified. Example: ``` import_environment { TZ = :/etc/localtime TMPDIR = /dovecot-tmp } ``` ### `inet_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[inet_listener_name](/main/core/summaries/settings.html#inet_listener_name)` Creates a new network socket listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[inet_listener_name](/main/core/summaries/settings.html#inet_listener_name)` setting. ### `inet_listener_haproxy` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, this listener is configured for use with HAProxy. It expects a Proxy Protocol header right after accepting the connection. Connections are aborted immediately if this protocol is violated. ### `inet_listener_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[inet_listener_type](/main/core/summaries/settings.html#inet_listener_type)` - `[inet_listener](/main/core/summaries/settings.html#inet_listener)` Name of this listener. It is meant to be descriptive for humans (e.g. `imap`, `imaps`), but it is also used by services to determine the socket type, unless `[inet_listener_type](/main/core/summaries/settings.html#inet_listener_type)` overrides it. The `[inet_listener](/main/core/summaries/settings.html#inet_listener)` filter name refers to this setting. ### `inet_listener_port` **Default:** _\[None\]_ **Value:** [Port Number](/main/core/settings/types.html#port-number) **See Also:** - `[listen](/main/core/summaries/settings.html#listen)` Port number where to listen. `0` disables the listener. ### `inet_listener_ssl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` If `yes`, the listener does an immediate SSL/TLS handshake after accepting a connection. This is needed for e.g. the `imaps` and `pop3s` ports. INFO All listeners with `ssl=yes` will be removed if the global `[ssl](/main/core/summaries/settings.html#ssl)` setting is `no`. Regardless of the value for listener's ssl setting, some services will still try to initialize encryption if the global `[ssl](/main/core/summaries/settings.html#ssl)` is `yes`. This is for example done to accommodate STARTTLS commands for IMAP/SUBMISSION/LMTP protocols. In other words, SSL is truly disabled only when the global `[ssl](/main/core/summaries/settings.html#ssl)` is `no`. Note: Do not confuse this with the `[ssl](/main/core/summaries/settings.html#ssl)` setting. When used as `inet_listener { ssl }`, it expands to this `inet_listener_ssl` setting. ### `inet_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `info_log_path` **Default:** `[log_path](/main/core/summaries/settings.html#log_path)` **Value:** [string](/main/core/settings/types.html#string) The log file to use for informational messages. ### `instance_name` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) For multi-instance setups, supply the unique name of this Dovecot instance. This simplifies use of commands such as doveadm: rather than using `-c` and the config path, you can use the `-i` flag with the relevant instance name. ### `language` **Default:** `` **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[language_default](/main/core/summaries/settings.html#language_default)` **See Also:** - `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)` - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Defines a language to be used in tokenization. At least one language must be specified and one single language must be flagged as the default language using `[language_default = yes](/main/core/summaries/settings.html#language_default)`. The language listed first is the default and is used when language recognition fails. The filters used for stemming and stopwords are language dependent. TIP For better performance it's recommended to synchronize this setting with the textcat configuration file; see `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)`. Example: ``` language en { default = yes } language de { } ``` ### `language_default` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Dependencies:** - `[language](/main/core/summaries/settings.html#language)` **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) The language marked as default will be used when language detection cannot identify the proper language of the text being processed. Exactly one language must be marked with this flag. ### `language_filter_normalizer_icu_id` **Default:** `Any-Lower; NFKD; [: Nonspacing Mark :] Remove; [\x20] Remove` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Description of the normalizing/transliterating rules to use. See [Normalizer Format](https://unicode-org.github.io/icu/userguide/transforms/general/#transliterator-identifiers) for syntax. ### `language_filter_stopwords_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Path to the directory containing stopword files. The files inside the directory have names with the form `/stopwords_.txt`. See [Languages](/main/core/plugins/fts.html#languages) for the list of stopword files that are currently distributed with Dovecot. More languages can be obtained from: - [Apache Lucene](https://lucene.apache.org/), - [Snowball stemmer](https://snowballstem.org/), or - [GitHub - Stopwords ISO](https://github.com/stopwords-iso/). ### `language_filters` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) - [Filter Configuration](/main/core/plugins/fts.html#filter-configuration) **Plugin:** [fts plugin](/main/core/plugins/fts.html) The list of filters to apply. See [Filter Configuration](/main/core/plugins/fts.html#filter-configuration) for configuration information. ### `language_tokenizer_address_token_maxlen` **Default:** `250` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizer_generic_algorithm` **Default:** `simple` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `simple``tr29` **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Defines the method for finding word boundaries. | Value | Description | | --- | --- | | `simple` | Faster algorithm that works for many texts, especially using the latin alphabets, but leaves corner cases. | | `tr29` | Implements a version of Unicode technical report 29 word boundary lookup. It might work better with texts containing e.g. Katakana or Hebrew characters, but it is not possible to use a single algorithm for all existing languages. | ### `language_tokenizer_generic_token_maxlen` **Default:** `30` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizer_generic_wb5a` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Unicode TR29 rule WB5a setting to the tr29 tokenizer. Splits prefixing contracted words from base word. E.g. `l'homme` -> `l` and `homme`. Together with a language specific stopword list unnecessary contractions can thus be filtered away. This is disabled by default and only works with the TR29 algorithm. Enable by declaring: - `[language_tokenizer_generic_algorithm = tr29](/main/core/summaries/settings.html#language_tokenizer_generic_algorithm)` and - `[language_tokenizer_generic_wb5a = yes](/main/core/summaries/settings.html#language_tokenizer_generic_wb5a)`. ### `language_tokenizer_kuromoji_icu_id` **Default:** `Any-NFKC` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Description of the normalizing/transliterating rules to use. See [Normalizer Format](https://unicode-org.github.io/icu/userguide/transforms/general/#transliterator-identifiers) for syntax. Defaults to `Any-NFKC` which is quite good for CJK text mixed with latin alphabet languages. It transforms CJK characters to full-width encoding and transforms latin ones to half-width. The NFKC transformation is described above. WARNING If this setting is changed, existing FTS indexes will produce unexpected results. The FTS indexes should be recreated in this case. ### `language_tokenizer_kuromoji_token_maxlen` **Default:** `1024` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) **Plugin:** [fts plugin](/main/core/plugins/fts.html) Maximum length of token, before an arbitrary cut off is made. ### `language_tokenizers` **Default:** `generic email-address` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) - [Tokenizer Configuration](/main/core/plugins/fts.html#tokenizer-configuration) **Plugin:** [fts plugin](/main/core/plugins/fts.html) The list of tokenizers to use. See [Tokenizer Configuration](/main/core/plugins/fts.html#tokenizer-configuration) for configuration information. ### `last_login` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **Plugin:** [last-login plugin](/main/core/plugins/last_login.html) Named filter for initializing dictionary used to store last login information. Example: ``` redis_host = 127.0.0.1 redis_port = 6379 last_login { dict redis { } } ``` ### `last_login_dict_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **Plugin:** [last-login plugin](/main/core/plugins/last_login.html) **Changes:** - Added: 2.4.5 List of additional key/value pairs to be updated in the dictionary. Both keys and values support [variable expansion](/main/core/settings/variables.html). ### `last_login_key` **Default:** `last-login/%{user}` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [last-login plugin](/main/core/plugins/last_login.html) The key that is updated in the dictionary with the last login timestamp. ### `last_login_precision` **Default:** `s` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `s``ms``us``ns` **Plugin:** [last-login plugin](/main/core/plugins/last_login.html) Precision for last login timestamp. ### `last_valid_gid` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[first_valid_gid](/main/core/summaries/settings.html#first_valid_gid)` This setting and `[first_valid_gid](/main/core/summaries/settings.html#first_valid_gid)` specify the valid GID range for users. A user whose primary GID is outside this range is not allowed to log in. If the user belongs to any supplementary groups, the corresponding IDs are not set. ### `last_valid_uid` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[first_valid_uid](/main/core/summaries/settings.html#first_valid_uid)` This setting and `[first_valid_uid](/main/core/summaries/settings.html#first_valid_uid)` specify the valid UID range for users. A user whose UID is outside this range is not allowed to log in. ### `lazy_expunge_mailbox` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [lazy-expunge storage locations](/main/core/plugins/lazy_expunge.html#storage-locations) **Plugin:** [lazy-expunge plugin](/main/core/plugins/lazy_expunge.html) The mailbox to move messages to when expunged. This setting MUST be defined or else lazy-expunge plugin will not be active. ``` lazy_expunge_mailbox = .EXPUNGED namespace inbox { mailbox Drafts { lazy_expunge_mailbox = } namespace "External accounts" { lazy_expunge_mailbox = } } ``` ### `lazy_expunge_only_last_instance` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [lazy-expunge plugin](/main/core/plugins/lazy_expunge.html) **Changes:** - Changed: 2.4.3 Changed from `no` to `yes`. If `yes`, only move to expunged storage if this is the last copy of the message in the user's account. This prevents the same mail from being duplicated in the lazy-expunge folder as the mail becomes expunged from all the folders it existed in. This setting prevents copying mail to the lazy-expunge folder when using the IMAP MOVE command. When using COPY/EXPUNGE, this setting prevents duplicates only with the following mailbox formats: - [Maildir](/main/core/config/mailbox_formats/maildir.html) (with `[maildir_copy_with_hardlinks = yes](/main/core/summaries/settings.html#maildir_copy_with_hardlinks)`, which is the default) - [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox)/[mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) ### `lda_mailbox_autocreate` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Should LDA create a nonexistent mailbox automatically when attempting to save a mail message? ### `lda_mailbox_autosubscribe` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Should automatically created mailboxes be subscribed to? ### `lda_original_recipient_header` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) The header from which the original recipient address (used in the SMTP RCPT TO: address) is obtained if that address is not available elsewhere. With [LDA](/main/core/config/delivery/lda.html) the default is based on the username in `-d` parameter. With [LMTP Server](/main/core/config/delivery/lmtp.html) the default is the same as RCPT TO. Example: ``` lda_original_recipient_header = X-Original-To ``` ### `ldap_auth_dn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specify the Distinguished Name (the username used to login to the LDAP server). Leave it commented out to bind anonymously (useful with `[passdb_ldap_bind = yes](/main/core/summaries/settings.html#passdb_ldap_bind)`). Example: `ldap_auth_dn = uid=dov-read,dc=example,dc=com,dc=.` ### `ldap_auth_dn_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for LDAP server. Used if `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is specified. ### `ldap_auth_sasl_authz_id` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL authorization ID, ie. the `[ldap_auth_dn_password](/main/core/summaries/settings.html#ldap_auth_dn_password)` is for this "master user", but the `[ldap_auth_dn](/main/core/summaries/settings.html#ldap_auth_dn)` is still the logged in user. Normally you want to keep this empty. ### `ldap_auth_sasl_mechanisms` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of SASL mechanism names to use. ### `ldap_auth_sasl_realm` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SASL realm to use. ### `ldap_base` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP base. `[Settings variables](/main/core/settings/variables.html)` can be used. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the variable. Examples: - `ldap_base = dc=mail, dc=example, dc=org` - `ldap_base = %{passdb:classOfServiceDN | safe}` ### `ldap_connection_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Only databases with the same connection group share the ldap connections. By default all the databases have the same `""`(empty string) default connection group, and as such share the connections. ### `ldap_debug_level` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP library debug level as specified by `LDAP_DEBUG_*` in `ldap_log.h`. Value `-1` means everything. You may need to recompile OpenLDAP with debugging enabled to get enough output. ### `ldap_deref` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``searching``finding``always` Specify dereference which is set as an LDAP option. ### `ldap_max_idle_time` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) Disconnect from LDAP server after connection has been idle for this many seconds. ### `ldap_scope` **Default:** `subtree` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `base``onelevel``subtree` This specifies the search scope. ### `ldap_starttls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set to `yes` to use TLS to connect to the LDAP server. ### `ldap_uris` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) LDAP URIs to use. Configure this setting to specify what LDAP server(s) to connect to. The URIs are in syntax `protocol://host:port`. Example: `ldap_uris = ldaps://secure.domain.org` ### `ldap_version` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) LDAP protocol version to use. Likely `2` or `3`. ### `listen` **Default:** `*, ::` **Value:** [IP addresses](/main/core/settings/types.html#ip-addresses) A comma-separated list of IP addresses or hostnames on which external network connections will be handled. `*` listens at all IPv4 interfaces, and `::` listens at all IPv6 interfaces. Example: ``` listen = 127.0.0.1, 192.168.0.1 ``` This setting can be used also inside an `[inet_listener](/main/core/summaries/settings.html#inet_listener)` to override the listener address, e.g.: ``` service imap-login { inet_listener imap { listen = 192.168.0.1 } } ``` ### `lmtp_add_received_header` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Controls if "Received:" header should be added to delivered mails. ### `lmtp_client_workarounds` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Configures the list of active workarounds for LMTP client bugs. Supported workaround identifiers are: `whitespace-before-path` Allow one or more spaces or tabs between 'MAIL FROM:' and path and between 'RCPT TO:' and path. `mailbox-for-path` Allow using bare Mailbox syntax (i.e., without <...>) instead of full path syntax. ### `lmtp_hdr_delivery_address` **Default:** `final` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `alternative``final``none` The recipient address to use for the "Delivered-To:" header and the relevant "Received:" header. Options: `alternative` Address from the RCPT TO OCRPT parameter `final` Address from the RCPT TO command `none` No address (always used for messages with multiple recipients) ### `lmtp_proxy` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [proxy passdb](/main/core/config/proxy/overview.html) If yes, LMTP sessions perform a passdb lookup to see if the user should be proxied. The user is proxied only if the `proxy` extra field is returned. The proxy destination is determined via returned passdb extra fields. ### `lmtp_proxy_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Directory location to store raw LMTP proxy protocol traffic logs. `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` can be used. However, because LMTP session starts without a user, all user-specific variables expand to empty. ### `lmtp_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Directory location to store raw LMTP protocol traffic logs. `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` can be used. However, because LMTP session starts without a user, all user-specific variables expand to empty. ### `lmtp_rcpt_check_quota` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Should quota be verified before a reply to RCPT TO is issued? If active, this creates a small amount of extra overhead so it is disabled by default. ### `lmtp_save_to_detail_mailbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If the recipient address includes a detail element / role (as in user+detail format), save the message to the detail mailbox. ### `lmtp_user_concurrency_limit` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Changes:** - Changed: 2.4.1 Changed from `unlimited` to `10`. Limit the number of concurrent deliveries to a single user to this maximum value. It is useful if one user is receiving numerous mail messages and thereby causing delays to other deliveries. To prevent limiting concurrent user deliveries set this to `unlimited`. ### `lock_method` **Default:** `fcntl` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `fcntl``flock``dotlock` Specify the locking method to use for index files. Options: `dotlock` `mailboxname.lock` file created by almost all software when writing to mboxes. This grants the writer an exclusive lock over the mbox, so it's usually not used while reading the mbox so that other processes can also read it at the same time. So while using a dotlock typically prevents actual mailbox corruption, it doesn't protect against read errors if mailbox is modified while a process is reading. `flock` flock() system call is quite commonly used for both read and write locking. The read lock allows multiple processes to obtain a read lock for the mbox, so it works well for reading as well. The one downside to it is that it doesn't work if mailboxes are stored in NFS. `fcntl` Very similar to flock, also commonly used by software. In some systems this fcntl() system call is compatible with flock(), but in other systems it's not, so you shouldn't rely on it. fcntl works with NFS if you're using lockd daemon in both NFS server and client. ### `log_core_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Crash after logging a matching event. The syntax of the filter is described in [Global Metric Filters](/main/core/config/events/filter.html#global-filter-syntax). For example: ``` log_core_filter = category=error ``` will crash any time an error is logged, which can be useful for debugging. ### `log_debug` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter to specify what debug logging to enable. The syntax of the filter is described in [Global Metric Filters](/main/core/config/events/filter.html#global-filter-syntax). INFO This will eventually replace `[mail_debug](/main/core/summaries/settings.html#mail_debug)` and `[auth_debug](/main/core/summaries/settings.html#auth_debug)` settings. ### `log_path` **Default:** `syslog` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[debug_log_path](/main/core/summaries/settings.html#debug_log_path)` - `[info_log_path](/main/core/summaries/settings.html#info_log_path)` Specify the log file to use for error messages. Options: - `syslog`: Log to syslog - `/dev/stderr`: Log to stderr If you don't want to use syslog, or if you just can't find the Dovecot's error logs, you can make Dovecot log elsewhere as well: ``` log_path = /var/log/dovecot.log ``` If you don't want errors, info, and debug logs all in one file, specify `[info_log_path](/main/core/summaries/settings.html#info_log_path)` or `[debug_log_path](/main/core/summaries/settings.html#debug_log_path)` as well: ``` log_path = /var/log/dovecot.log info_log_path = /var/log/dovecot-info.log ``` ### `log_timestamp` **Default:** `%b %d %H:%M:%S` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The prefix for each line written to the log file. `%` variables are in strftime(3) format. ### `login_log_format` **Default:** `%{message}: %{elements}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The formatting of login log messages. Variables allowed (in addition to `[Global variables](/main/core/settings/variables.html#global-variables)`): | Variable Name | Description | | --- | --- | | `%{elements}` | A `[login_log_format_elements](/main/core/summaries/settings.html#login_log_format_elements)` string | | `%{message}` | The log data | ### `login_log_format_elements` **Default:** `user=<%{user}> method=%{mechanism} rip=%{remote_ip} lip=%{local_ip} mpid=%{mail_pid} %{secured} session=<%{session}>` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) A space-separated list of elements of the login log formatting. Elements that have a non-empty value are joined together to form a comma-separated string. Example: doveconf ``` # If `secured` is empty, the output for `%{elements}` will look like: # `user=, method=PLAIN, session=` login_log_format_elements = user=<%{user}> method=%{mechanism} %{secured} session=<%{session}> ``` `[Login variables](/main/core/settings/variables.html#login-variables)` can be used. ### `login_plugin_dir` **Default:** `/usr/lib64/dovecot/login` **Value:** [string](/main/core/settings/types.html#string) Location of the login plugin directory. ### `login_plugins` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of plugins to load for IMAP and POP3 login processes. ### `login_proxy_max_disconnect_delay` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) Specify the delayed disconnection interval of clients when there is a server mass-disconnect. For prevention of load spikes when a backend server fails or is restarted, disconnection is spread over the amount of time indicated. `0` disables the delay. ### `login_proxy_max_reconnects` **Default:** `3` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) How many times login proxy will attempt to reconnect to destination server on connection failures (3 reconnects = total 4 connection attempts). Reconnecting is done for most types of failures, except for regular authentication failures. There is a 1 second delay between each reconnection attempt. If `[login_proxy_timeout](/main/core/summaries/settings.html#login_proxy_timeout)` is reached, further reconnects aren't attempted. ### `login_proxy_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Login processes write rawlogs for proxied connections to this directory for debugging purposes. Note that login processes are usually chrooted, so the directory is relative to `$base_dir/login/`. Changed: 2.4.2 You can use [Login Variables](/main/core/settings/variables.html#login-variables) with this setting. ### `login_proxy_timeout` **Default:** `30 secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for login proxy failures. The timeout covers everything from the time connection is started until a successful login reply is received. This can be overwritten by [proxy passdb](/main/core/config/proxy/overview.html) passdb extra field. This setting applies only to proxying via login processes, not to lmtp or doveadm processes. ### `login_source_ips` **Default:** _\[None\]_ **Value:** [IP addresses](/main/core/settings/types.html#ip-addresses) Value Format: List of trusted network ranges, see [Boolean List](/main/core/settings/types.html#boolean-list). A list of hosts / IP addresses that are used in a round-robin manner for the source IP address when the proxy creates TCP connections. Example: ``` login_source_ips { proxy-sources.example.com = yes } ``` ### `login_trusted_networks` **Default:** _\[None\]_ **Value:** [IP addresses](/main/core/settings/types.html#ip-addresses) Value Format: List of trusted network ranges, see [Boolean List](/main/core/settings/types.html#boolean-list). This setting is used for a few different purposes, but most importantly it allows the client connection to tell the server what the original client's IP address was. This original client IP address is then used for logging and authentication checks. Client connections from trusted networks are also treated as secured unless `[ssl = required](/main/core/summaries/settings.html#ssl)`. Plaintext authentication is always allowed for [secured connections](/main/core/config/ssl.html#secured-connections) (`[auth_allow_cleartext](/main/core/summaries/settings.html#auth_allow_cleartext)` is ignored). Localhost connections are secured by default, but they are not trusted by default. If you want localhost to be trusted, it needs to be included in this setting. The details of how this setting works depends on the used protocol: **IMAP** ID command can be used to override: - Session ID - Client IP and port (`%{remote_ip}`, `%{remote_port}`) - Server IP and port (`%{local_ip}`, `%{local_port}`) `forward_*` fields can be sent to auth process's passdb lookup The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. **POP3** XCLIENT command can be used to override: - Session ID - Client IP and port (`%{remote_ip}`, `%{remote_port}`) - Added: 2.4.3 Server IP and port (`%{local_ip}`, `%{local_port}`) `forward_*` fields can be sent to auth process's passdb lookup The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. **ManageSieve** XCLIENT command can be used to override: - Session ID - Client IP and port (`%{remote_ip}`, `%{remote_port}`) - Added: 2.4.3 Server IP and port (`%{local_ip}`, `%{local_port}`) The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. **Submission** XCLIENT command can be used to override: - Session ID - Client IP and port (`%{remote_ip}`, `%{remote_port}`) - HELO - Overrides what the client sent earlier in the EHLO command - LOGIN - Currently unused - PROTO - Currently unused - Added: 2.4.3 Server IP and port (`%{local_ip}`, `%{local_port}`) `forward_*` fields can be sent to auth process's passdb lookup The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. **LMTP** XCLIENT command can be used to override: - Session ID - Client IP and port (`%{remote_ip}`, `%{remote_port}`) - HELO - Overrides what the client sent earlier in the LHLO command - LOGIN - Currently unused - PROTO - Currently unused - TIMEOUT (overrides `[mail_max_lock_timeout](/main/core/summaries/settings.html#mail_max_lock_timeout)`) - Added: 2.4.3 Server IP and port (`%{local_ip}`, `%{local_port}`) The trust is always checked against the connecting IP address. Except if HAProxy is used, then the original client IP address is used. ### `lua_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - [mail-lua plugin](/main/core/plugins/mail_lua.html) - [push-notification-lua plugin](/main/core/plugins/push_notification_lua.html) - [Lua authentication database](/main/core/config/auth/databases/lua.html) Path to lua script to be used. This is used by - [mail-lua plugin](/main/core/plugins/mail_lua.html), - [push-notification-lua plugin](/main/core/plugins/push_notification_lua.html), and - [Lua authentication database](/main/core/config/auth/databases/lua.html). ### `lua_settings` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - `[lua_file](/main/core/summaries/settings.html#lua_file)` Key-value pairs that are passed as a table to lua `script_init()` functions. ### `mail_access_groups` **Default:** `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` - `[service_extra_groups](/main/core/summaries/settings.html#service_extra_groups)` **Changes:** - Changed: 2.4.3 Changed from empty to `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)`. Supplementary groups that are granted access for mail processes (imap, pop3, lmtp, etc.) The default is to include `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` to allow UNIX socket access to various internal Dovecot services. For proper security, there should be no regular files or directories writable for the `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)`. WARNING It may be dangerous to use access groups if (untrusted) users can create symlinks to any paths the mail processes access. For example if using `[mail_access_groups = mail](/main/core/summaries/settings.html#mail_access_groups)`: - `ln -s /var/mail ~/mail/var` could allow a user to delete others' mailboxes, or - `ln -s /secret/shared/box ~/mail/mybox` would allow reading others' mail. ### `mail_alt_check` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_alt_path](/main/core/summaries/settings.html#mail_alt_path)` Whether to perform a sanity check and warn if `[mail_alt_path](/main/core/summaries/settings.html#mail_alt_path)` changes from the last access. This can catch accidentally broken configurations before users start reporting missing mails. The downside to this check is some additional disk IO. ### `mail_alt_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [dbox alternate storage](/main/core/config/mailbox_formats/dbox.html#alternate-storage) Specifies the [dbox alternate storage](/main/core/config/mailbox_formats/dbox.html#alternate-storage) path. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_always_cache_fields` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)` - `[mail_never_cache_fields](/main/core/summaries/settings.html#mail_never_cache_fields)` The fields specified here are always added to cache when saving mails, even if the client never accesses these fields. See [mail cache configuration](/main/core/config/mail_cache.html) for details and for the list of fields. ### `mail_attachment_detection_options` **Default:** `- mbox, imapc, maildir: - others: add-flags content-type=!application/signature` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Changed: 2.4.2 The default was set. - Changed: 2.4.3 The Maildir default was reverted to empty. Settings to control adding `$HasAttachment` or `$HasNoAttachment` keywords. By default, all MIME parts with `Content-Disposition=attachment` or inlines with filename parameter are considered attachments. To enable this feature, this setting needs at least one option specified. Multiple options can be added in a space-separated list. Options: `add-flags` Attachments are detected and marked during save. Detection is done also during fetch if it can be done without extra disk IO and with minimal CPU cost. This means that either both `mime.parts` and `imap.bodystructure` has to be in cache already, or if mail body is opened in any case. `add-flags no-flags-on-fetch` Flags are added during save, but not during fetch. This option will likely be removed in a later release. `content-type=` Include or exclude given content type. Including will only negate an exclusion (e.g. `content-type=!foo/* content-type=foo/bar`). `exclude-inlined` Do not consider any attachment with disposition inlined. ### `mail_attribute` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[imap_metadata](/main/core/summaries/settings.html#imap_metadata)` Named filter for initializing [dict driver](/main/core/config/dict.html) for server and mailbox attributes (`key=value`). This is used by the URLAUTH and METADATA extensions, as well as various other features. Example: ``` mail_attribute { dict file { path = %{home}/dovecot-attributes } } ``` ### `mail_cache_fields` **Default:** `flags hdr.date hdr.subject hdr.from hdr.sender hdr.reply-to hdr.to hdr.cc hdr.bcc hdr.in-reply-to hdr.message-id date.received size.virtual imap.bodystructure mime.parts hdr.references hdr.importance hdr.x-priority hdr.x-open-xchange-share-url pop3.uidl pop3.order` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[mail_always_cache_fields](/main/core/summaries/settings.html#mail_always_cache_fields)` - `[mail_never_cache_fields](/main/core/summaries/settings.html#mail_never_cache_fields)` **Changes:** - Changed: 2.4.1 The default fields were changed. They used to be just "flags". The default list of fields that are added to cache if no other caching decisions exist yet. This setting is used only when creating the initial INBOX for the user. Other folders get their defaults from the INBOX. See [mail cache configuration](/main/core/config/mail_cache.html) for details and for the list of fields. ### `mail_cache_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) Place `dovecot.index.cache` files to this directory instead of among the other index files. This may be used as an optimization to split most index files to the fastest (smallest) storage while keeping cache files in a slightly slower (larger) storage. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_chroot` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The default chroot directory for mail processes. This chroots all users globally into the same directory. `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` can be used. ### `mail_compress_write_method` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [mail-compress plugin](/main/core/plugins/mail_compress.html) The [Compression Method](/main/core/plugins/mail_compress.html#compression-methods) to use for writing new mails. If empty, new mails are written without compression. Old mails can still be read. ### `mail_control_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Location for (mailbox-format specific) control files. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_debug` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) This setting adjusts log verbosity. It enables mail-process debugging. This can help you figure out the reason if Dovecot isn't finding certain mail messages. ### `mail_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats) - [Mailbox Root Autodetection](/main/core/config/mail_location.html#mailbox-root-autodetection) - `[mail_path](/main/core/summaries/settings.html#mail_path)` One of the mailbox formats described at [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats). For an empty value or `auto`, Dovecot attempts to find the mailboxes automatically. ### `mail_ext_attachment` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[fs_driver](/main/core/summaries/settings.html#fs_driver)` - `[mail_ext_attachment_path](/main/core/summaries/settings.html#mail_ext_attachment_path)` Named filter for initializing [FS Driver](/main/core/config/fs.html) for external attachments. Commonly used options: `posix` No single-instance storage (SIS) done (this option might simplify the filesystem's own de-duplication operations). `sis` SIS with immediate byte-by-byte comparison during saving. SIS is deprecated and writing of SIS files is disabled. Reading is supported for now, any missing SIS attachments are replaced with files filled with spaces. `sis-queue` SIS with delayed comparison and de-duplication. Changed: 2.4.0 SIS is deprecated and writing SIS files is disabled. Reading is supported for now. Any missing SIS attachments are replaced with files filled with spaces. ### `mail_ext_attachment_min_size` **Default:** `128k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[mail_ext_attachment_path](/main/core/summaries/settings.html#mail_ext_attachment_path)` Attachments below this size will not be saved externally. ### `mail_ext_attachment_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The directory in which to store mail attachments. With [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) and [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox), mail attachments can be saved to external files, which also allows single-instance storage of them. If no value is specified, attachment saving to external files is disabled. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_fsync` **Default:** `optimized` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `always``optimized``never` Specify when to use fsync() or fdatasync() calls. Using fsync waits until the data is written to disk before it continues, which is used to prevent corruption or data loss in case of server crashes. This setting applies to mail files and index files on the filesystem. This setting doesn't apply to object storage operations. Options: `always` Use fsync after all disk writes. Recommended for NFS to make sure there aren't any delayed write()s. `optimized` Use fsync after important disk writes. For example cache file writes aren't fsynced, because they can be regenerated if necessary. `never` Never fsync any disk writes. This provides the best performance, but risks losing recently saved emails in case of a crash with most mailbox formats. ### `mail_gid` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string)[unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[mail_uid](/main/core/summaries/settings.html#mail_uid)` The system group ID used for accessing mail messages. Can be either a numeric ID or a group name. This can be overridden via the `gid` [userdb field](/main/core/config/auth/userdb.html#fields). ### `mail_home` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_path](/main/core/summaries/settings.html#mail_path)` - [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) User's home directory. This is used as the root for some of the user-specific files and directories. The `%{home}` variable expands to this value. Also some path settings expand `~/` to this value. There are various possible ways of specifying this parameter and `[mail_path](/main/core/summaries/settings.html#mail_path)`. The following example is one option when home is in `/var/vmail/domain/user/` and mails are in `/var/vmail/domain/user/mail/`: ``` mail_home = /var/vmail/%{user | domain}/%{user | username} mail_path = ~/mail ``` `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` can be used. ### `mail_inbox_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_home](/main/core/summaries/settings.html#mail_home)` - `[mail_path](/main/core/summaries/settings.html#mail_path)` Path to the INBOX mailbox. The path doesn't have to be absolute - it is relative to the `[mail_path](/main/core/summaries/settings.html#mail_path)`. WARNING If your inbox _does not_ reside in some weird location, you should _not_ set this setting to any value. Setting it to `.` or `$MAIL_HOME/` will just cause problems. You almost certainly want to change `mail_home` or `mail_path` settings instead. This is often used with mbox format where INBOX is in `/var/mail/` while the rest of the folders are under the user's home directory. This can also be used to specify a different INBOX path with Maildir: ``` mail_driver = maildir mail_path = ~/Maildir mail_inbox_path = ~/Maildir/.INBOX ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_index_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mail Location: Index Files](/main/core/config/mail_location.html#index-files) Location of [index files](/main/core/config/mail_location.html#index-files). `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_index_private_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [public shared mailboxes](/main/core/config/shared_mailboxes.html#public-shared-mailboxes) The private index files are used with shared mailboxes to provide private (per-user) message flags. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_log_cached_only` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [mail-log plugin](/main/core/plugins/mail_log.html) If enabled, everything except `save` event will log only the fields that can be looked up from cache. This improves performance if some of the fields aren't cached and it's not a strict requirement to log them. ### `mail_log_events` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [mail-log plugin](/main/core/plugins/mail_log.html) List of events to log. Available events: - `delete` - `undelete` - `expunge` - `save` - `copy` - `mailbox_create` - `mailbox_delete` - `mailbox_rename` - `flag_change` ### `mail_log_fields` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [mail-log plugin](/main/core/plugins/mail_log.html) List of fields to log. | Field | Restrictions | | --- | --- | | `uid` | | | `box` | | | `msgid` | | | `size` | Only available for `expunge` and `copy` events. | | `vsize` | Only available for `expunge` and `copy` events. | | `vsize` | | | `flags` | | | `from` | | | `subject` | | ### `mail_log_prefix` **Default:** `%{service}(%{user})<%{process:pid}><%{session}>:` **Value:** [string](/main/core/settings/types.html#string) You can specify a log prefix for mail processes here. `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` can be used. ### `mail_lua` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[lua_file](/main/core/summaries/settings.html#lua_file)` - `[lua_settings](/main/core/summaries/settings.html#lua_settings)` **Plugin:** [mail-lua plugin](/main/core/plugins/mail_lua.html) **Changes:** - Added: 2.4.0 Named filter for initializing [mail\_lua plugin](/main/core/plugins/mail_lua.html) settings. Example: ``` mail_lua { lua_file = /etc/dovecot/user.lua lua_settings { extra_param = %{userdb:extra_param} } } ``` ### `mail_max_lock_timeout` **Default:** `0` **Value:** [time](/main/core/settings/types.html#time) This value is used as a timeout for tempfailing mail connections. It can be set globally, for application to all Dovecot services, but is normally better to set it in only certain protocol blocks. You may wish to set a value for this for LMTP and LDA while leaving it at the global default of `0` for IMAP and POP3 connections, which tolerate tempfailing less well. ### `mail_max_userip_connections` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of IMAP connections allowed for a user from each IP address. This setting is checked only by backends, not proxies. Note that for this to work, any username changes must be done already by passdb lookup (not by userdb lookup). Unique users are identified via case-sensitive comparison. ### `mail_never_cache_fields` **Default:** `imap.envelope` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[mail_always_cache_fields](/main/core/summaries/settings.html#mail_always_cache_fields)` - `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)` List of fields that should never be cached. This should generally never include anything other than `imap.envelope`, which isn't needed because it can be generated from the cached header fields. See [mail cache configuration](/main/core/config/mail_cache.html) for details and for the list of fields. ### `mail_nfs_index` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_nfs_storage](/main/core/summaries/settings.html#mail_nfs_storage)` - `[mail_fsync](/main/core/summaries/settings.html#mail_fsync)` This setting attempts to flush NFS caches when accessing index files. The intention was that it would allow accessing the same user's index files in multiple servers at the same time. However, this couldn't be made to work perfectly due to the way Linux and BSD kernels work internally. The setting exists for now, but it is not a supported way of running Dovecot. When using this setting, be sure also to use `[mmap_disable = yes](/main/core/summaries/settings.html#mmap_disable)` and `[mail_fsync = optimized](/main/core/summaries/settings.html#mail_fsync)`. ### `mail_nfs_storage` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_nfs_index](/main/core/summaries/settings.html#mail_nfs_index)` This setting attempts to flush NFS caches when accessing mail storage. The intention was that it would allow accessing the same user's mails in multiple servers at the same time. However, this couldn't be made to work perfectly due to the way Linux and BSD kernels work internally. The setting exists for now, but it is not a supported way of running Dovecot. ### `mail_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Mail User Variables](/main/core/settings/variables.html#mail-user-variables) - [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) - `[mail_driver](/main/core/summaries/settings.html#mail_driver)` Path to a directory where the mail is stored. [Mail User Variables](/main/core/settings/variables.html#mail-user-variables) are commonly used here. Usually the mails should be stored in a sub-directory under the home directory, but not the home directory itself (see [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories)). The path must be absolute, not a relative path. Even if relative paths appear to work, this usage is deprecated and will likely stop working at some point. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_plugin_dir` **Default:** `/usr/lib64/dovecot` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` The directory in which to search for Dovecot mail plugins. ### `mail_plugins` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - `[mail_plugin_dir](/main/core/summaries/settings.html#mail_plugin_dir)` A list of mail plugins to load. ### `mail_prefetch_count` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The number of messages to try to prefetch whenever possible. `0` means that no prefetching is done. Depending on the (remote) storage latency, this may significantly speed up performance when reading many mails. The exact behavior depends on the mailbox format: - [mbox](/main/core/config/mailbox_formats/mbox.html), [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox): No effect in behavior. - [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox), [Maildir](/main/core/config/mailbox_formats/maildir.html): Call `posix_fadvise(POSIX_FADV_WILLNEED)` on mail files to instruct kernel to read the whole files into memory. - [imapc](/main/core/config/mailbox_formats/imapc.html): Combine multiple mail reads into the same remote imapc FETCH command. For example with `[mail_prefetch_count = 0](/main/core/summaries/settings.html#mail_prefetch_count)` reading two mails would result in `FETCH 1 BODY.PEEK[]` and `FETCH 2 BODY.PEEK[]` commands, while with `[mail_prefetch_count = 1](/main/core/summaries/settings.html#mail_prefetch_count)` they would be combined into a single `FETCH 1:2 BODY.PEEK[]` command. The downside is that each mail uses a file descriptor and disk space in `[mail_temp_dir](/main/core/summaries/settings.html#mail_temp_dir)`. A good value is likely between `10..100`. ### `mail_privileged_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` This group is enabled temporarily for privileged operations. Currently, this is used only with the [mbox format](/main/core/config/mailbox_formats/mbox.html) with INBOX when either its initial creation or dotlocking fails. Typically, this is set to `mail` to give access to `/var/mail` when using the [mbox format](/main/core/config/mailbox_formats/mbox.html). You can give Dovecot access to mail group by setting `mail_privileged_group = mail`. ### `mail_server_admin` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imap_metadata](/main/core/summaries/settings.html#imap_metadata)` The method for contacting the server administrator. Per the METADATA standard ([RFC 5464](https://datatracker.ietf.org/doc/html/rfc5464)), this value MUST be a URI (e.g., a mailto: or tel: URL), but that requirement is not enforced by Dovecot. This value is accessible to authenticated users through the `/shared/admin` IMAP METADATA server entry. Example: ``` mail_server_admin = mailto:admin@example.com ``` ### `mail_server_comment` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[imap_metadata](/main/core/summaries/settings.html#imap_metadata)` A comment or note that is associated with the server. This value is accessible to authenticated users through the `/shared/comment` IMAP METADATA server entry. ### `mail_shared_explicit_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) This setting determines whether a shared INBOX should be visible as "shared/user" or as "shared/user/INBOX" instead. ### `mail_sort_max_read_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The number of slow mail accesses an IMAP SORT can perform before it returns failure to the client. On failure, the untagged SORT reply is retuned, but it is likely not correct. The IMAP reply returned to the client is: ``` NO [LIMIT] Requested sort would have taken too long. ``` ### `mail_temp_dir` **Default:** `/tmp` **Value:** [string](/main/core/settings/types.html#string) The directory in which LDA/LMTP will temporarily store incoming message data that is above 128kB in size. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `mail_temp_scan_interval` **Default:** `1week` **Value:** [time](/main/core/settings/types.html#time) How often Dovecot scans for and deletes stale temporary files. These files exist only if Dovecot crashes while saving a message. This is just to make sure such temporary files will eventually get deleted to avoid wasting disk space. This scan happens independently for each folder, and it's done at the time the folder is opened. In order to prevent load spikes, the actual value of the setting is spread increasing it by 0..30%, based on a hash of the username. The scanning is done only for these mailbox formats: - [Maildir](/main/core/config/mailbox_formats/maildir.html): Delete all files having ctime older than 36 hours from `tmp/`. The scan is done if tmp/ directory's atime older than this setting. - [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox), [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox): Delete `.temp.*` files having ctime older than 36 hours from `dbox-Mails/`. The scan is done if the `last_temp_file_scan` header field in dovecot.index is older than this setting. - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox): Delete `.temp.*` files having ctime older than 36 hours from `storage/`. The scan is done if storage/ directory's atime is older than this setting. A value of `0` means this scan never occurs. ### `mail_uid` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string)[unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[mail_gid](/main/core/summaries/settings.html#mail_gid)` This setting indicates the system userid used for accessing mail messages. Can be either a numeric ID or a username. This can be overridden via the `uid` [userdb field](/main/core/config/auth/userdb.html#fields). ### `mail_utf8_extensions` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If Dovecot has been compiled with --enable-experimental-mail-utf8 configure option, this setting can be used to enable the feature. Settings this to `yes` will enable SMTPUTF8 for LMTP and Submission sessions, and UTF8=ACCEPT for IMAP sessions. ### `mail_volatile_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Changed: 2.4.5 Fixed behavior to avoid collisions across multiple namespaces. Specifies the location of volatile files. This includes lock files and potentially other files that don't need to exist permanently, so it can point to an in-memory filesystem (`tmpfs`). This is especially useful to avoid creating lock files to NFS or other remote filesystems. This setting can be shared between multiple namespaces. Dovecot makes the filenames unique for each namespace to avoid collisions. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` and `~/` can be used. ### `mail_vsize_bg_after_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [quota plugin](/main/core/plugins/quota.html) Controls transitioning mail size determination to the background instead of synchronously during the delivery process. After this many messages have been opened, the system allows a background indexer-worker process to perform quota calculations in the background. This may happen when mail messages do not have their virtual sizes cached. When indexing is occurring in the background, explicit quota size queries return an internal error and mail deliveries are assumed to succeed. This setting must not be set to indexer-worker process, or the background calculation isn't finished. The configuration should be like: ``` protocol !indexer-worker { mail_vsize_bg_after_count = 10 } ``` ### `mailbox` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) Create a new mailbox to the list of mailboxes. The filter name refers to the `[mailbox_name](/main/core/summaries/settings.html#mailbox_name)` setting. The mailbox name can contain `?` and `*` wildcards. Settings are applied for all matching mailbox filters. TIP If the mailbox name has spaces, you can put it into quotes: ``` mailbox "Test Mailbox" { # ... } ``` TIP It's possible to rename the mailbox in userdb. For example: ``` mailbox junk { name = Junk special_use = \Junk } ``` The userdb can then return `mailbox/junk/name=Spam` to rename the mailbox for a specific user. ### `mailbox_auto` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `create``no``subscribe` Autocreate and/or subscribe to the mailbox? | Value | Description | | --- | --- | | `create` | Autocreate but don't autosubscribe | | `no` | Don't autocreate or autosubscribe | | `subscribe` | Autocreate and autosubscribe | Autocreated mailboxes are created lazily to disk only when accessed for the first time. The autosubscribed mailboxes aren't written to subscriptions file, unless SUBSCRIBE command is explicitly used for them. ### `mailbox_autoexpunge` **Default:** _\[None\]_ **Value:** [time](/main/core/settings/types.html#time) **See Also:** - `[mailbox_autoexpunge_max_mails](/main/core/summaries/settings.html#mailbox_autoexpunge_max_mails)` Expunge all mails in this mailbox whose saved-timestamp is older than this value. `0` means autoexpunging is disabled. For IMAP and POP3 this happens after the client is already disconnected. For LMTP this happens when the user's mail delivery is finished. Note that in case there are multiple recipients, autoexpunging is done only for some of the recipients to prevent delays with the mail delivery: The last recipient user is autoexpunged first. Next, the first recipient user is autoexpunged (because the first user's mail was kept open in case it could be directly copied to the other users). None of the middle recipient users are autoexpunged. `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)` is highly recommended when using this setting, as it avoids actually opening the mailbox to see if anything needs to be expunged. `[mail_always_cache_fields = date.save](/main/core/summaries/settings.html#mail_always_cache_fields)` is also recommended when using this setting with [sdbox](/main/core/config/mailbox_formats/dbox.html#single-dbox-sdbox) or [Maildir](/main/core/config/mailbox_formats/maildir.html), as it avoids using `stat()` to find out the mail's saved-timestamp. With [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) format this isn't necessary, since the saved-timestamp is always available. ### `mailbox_autoexpunge_max_mails` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Mails are autoexpunged until mail count is at or below this number of messages. Once this threshold has been reached, `[mailbox_autoexpunge](/main/core/summaries/settings.html#mailbox_autoexpunge)` processing is done. ### `mailbox_directory_name` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_driver](/main/core/summaries/settings.html#mail_driver)` - `[mailbox_directory_name_legacy](/main/core/summaries/settings.html#mailbox_directory_name_legacy)` Specifies the directory name used for mailbox, index, and control directory paths. See the individual mailbox format pages for further information. For example `dbox-Mails` with [dbox](/main/core/config/mailbox_formats/dbox.html). With mbox format this works differently. The user-provided mailbox name is the directory name, while `mailbox_directory_name` is the mbox file. For example with `mailbox_directory_name=mbox`, creating `foo/bar` mailbox name ends up creating `.../foo/bar/mbox` file. ### `mailbox_directory_name_legacy` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` **Changes:** - Changed: 2.4.2 The default value changed from `yes` to `no`. - Deprecated: 2.4.2 This legacy setting is obsolete and destined to be removed. Changing this setting requires migration, see [dbox: Migrating away from \`mailbox\_directory\_name\_legacy\`](/main/core/config/mailbox_formats/dbox.html#migrating-away-from-mailbox-directory-name-legacy) If `no`, `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` applies also to index and control directories. The only reason to set this to `yes` is if you already have an existing Dovecot installation with the legacy `DIRNAME` (rather than `FULLDIRNAME`) parameter and don't want to migrate the data. ### `mailbox_list_drop_noselect` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Specifies whether to automatically delete `NoSelect` mailboxes that have no children. These mailboxes are sometimes confusing to users. Also if a `NoSelect` mailbox is attempted to be created with `CREATE box/`, it's created as selectable mailbox instead. INFO [Maildir](/main/core/config/mailbox_formats/maildir.html) layout does not support `NoSelect` mailboxes, so this setting has no effect with it. ### `mailbox_list_index` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Dovecot indexes live at the root of user's mailbox storage, and allows quick lookup of mailbox status instead of needing to open all mailbox indexes separately. Enabling this optimizes the server reply to IMAP STATUS commands, which are commonly issued by clients. This also needs to be enabled if you wish to enable the IMAP NOTIFY extension. ### `mailbox_list_index_include_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mailbox_list_index](/main/core/summaries/settings.html#mailbox_list_index)` Should INBOX be kept up-to-date in the mailbox list index? Disabled by default as most mailbox accesses will open INBOX anyway. ### `mailbox_list_index_prefix` **Default:** `dovecot.list.index` **Value:** [string](/main/core/settings/types.html#string) Prefix for the mailbox list index filename. It may also optionally include a path (relative to `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)`) to place it in a different directory. ### `mailbox_list_index_very_dirty_syncs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, assume that the mailbox list index is fully updated so that stat() will not be run for mailbox files/directories. ### `mailbox_list_iter_from_index_dir` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Perform mailbox listing using the `[mail_index_path](/main/core/summaries/settings.html#mail_index_path)` directories instead of the `[mail_path](/main/core/summaries/settings.html#mail_path)` directories. Mainly useful when the index file storage is on a faster storage. ### `mailbox_list_layout` **Default:** `fs` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `fs``index``Maildir++` **See Also:** - [dbox](/main/core/config/mailbox_formats/dbox.html) - [Maildir](/main/core/config/mailbox_formats/maildir.html) - [mbox](/main/core/config/mailbox_formats/mbox.html) Directory layout to use. | Value | Description | | --- | --- | | `Maildir++` | The default used by [Maildir](/main/core/config/mailbox_formats/maildir.html). | | `fs` | The default used by [mbox](/main/core/config/mailbox_formats/mbox.html) and [dbox](/main/core/config/mailbox_formats/dbox.html). | | `index` | Uses mailbox GUIDs as the directory names. The mapping between mailbox names and GUIDs exists in `dovecot.list.index*` files. | INFO The `[mail_driver](/main/core/summaries/settings.html#mail_driver)` setting provides the default value, that the `mailbox_list_layout` setting can override. ### `mailbox_list_storage_escape_char` **Default:** The default for [imapc](/main/core/config/mailbox_formats/imapc.html) storage is `%`. **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mailbox_list_visible_escape_char](/main/core/summaries/settings.html#mailbox_list_visible_escape_char)` Specifies an escape character that it used for encoding special characters in the mailbox names in storage. This allows users to use characters in mailboxes names that would otherwise be illegal. For example: - [Maildir](/main/core/config/mailbox_formats/maildir.html) layout disallows using the `.` character, since it's used internally as the folder hierarchy separator. - The `~` character at the beginning of the mailbox name is disallowed, because of the possibility that it gets expanded to user's home directory. - The `/` character can't be used on POSIX filesystems, since it's the directory separator. The characters are escaped to the mailbox name as ``. INFO It's possible to use the same character here as for `[mailbox_list_visible_escape_char](/main/core/summaries/settings.html#mailbox_list_visible_escape_char)`. ### `mailbox_list_utf8` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Store mailbox names on disk using UTF-8 instead of modified UTF-7 (mUTF-7). This applies also to the mailbox list index with `[mailbox_list_layout = index](/main/core/summaries/settings.html#mailbox_list_layout)`. WARNING Changing this breaks already existing non-ASCII mailbox names. ### `mailbox_list_validate_fs_names` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mail_full_filesystem_access](/main/core/summaries/settings.html#mail_full_filesystem_access)` Specifies whether to disallow mailbox names that might be unsafe to use in filesystems or potentially allow bypassing ACL checks: - `/` character anywhere in the name (except as a hierarchy separator), unless mailbox list isn't on a filesystem (e.g. index, imapc). - `/` as the first character. - `~` as the first character (so it's not confused as home directory). - No adjacent `/` characters. - No `.` or `..` names between `/` characters. - No `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` between `/` characters. - No mailbox format-specific internal directories between `/` characters, - unless `[mailbox_directory_name](/main/core/summaries/settings.html#mailbox_directory_name)` is non-empty. This mainly means the Maildir `new`, `cur` and `tmp` directories in some configurations. Enabling `[mail_full_filesystem_access](/main/core/summaries/settings.html#mail_full_filesystem_access)` enables also this setting. ### `mailbox_list_visible_escape_char` **Default:** The default for [imapc](/main/core/config/mailbox_formats/imapc.html) storage is `~`. **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mailbox_list_storage_escape_char](/main/core/summaries/settings.html#mailbox_list_storage_escape_char)` Specifies an escape character that is used for broken or otherwise inaccessible mailbox names. If mailbox name can't be changed reversibly to UTF-8 and back, encode the problematic parts using ``in the user-visible UTF-8 name. The `mailbox_list_visible_escape_char` itself also has to be encoded the same way. This can be useful with [imapc](/main/core/config/mailbox_formats/imapc.html) to access mailbox names that aren't valid mUTF-7 charset from remote servers, or if the remote server uses a different hierarchy separator and has folder names containing the local separator. INFO It's possible to use the same character here as for `[mailbox_list_storage_escape_char](/main/core/summaries/settings.html#mailbox_list_storage_escape_char)`. ### `mailbox_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the mailbox being configured. The `[mailbox](/main/core/summaries/settings.html#mailbox)` filter name refers to this setting. ### `mailbox_notify_status` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [notify-status plugin](/main/core/plugins/notify_status.html) Whether notifications for a single mailbox or mailbox wildcards are enabled. Example: ``` mailbox INBOX { notify_status = yes } mailbox Spam { notify_status = yes } mailbox *BOX { notify_status = yes } ``` ### `mailbox_root_directory_name` **Default:** `specific to the mail_driver setting` **Value:** [string](/main/core/settings/types.html#string) Specifies directory name under which all mailbox directories are stored. For example `mailboxes` with [dbox](/main/core/config/mailbox_formats/dbox.html). ### `mailbox_special_use` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Namespaces](/main/core/config/namespaces.html) **Changes:** - Changed: 2.4.0 Using non-standard special-use flags will result in a warning message at startup. List of SPECIAL-USE ([RFC 6154](https://datatracker.ietf.org/doc/html/rfc6154)) flags to broadcast for the mailbox. There are no validity checks, so you could specify anything you want here, but it's not a good idea to use other than the standard ones specified in the RFC. ### `maildir_broken_filename_sizes` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, do not obtain a mail message's physical size from the `S=` data in the Maildir filename except when recalculating the Maildir++ quota. ### `maildir_copy_with_hardlinks` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, copying of a message is done with hard links whenever possible. This makes the performance much better, and it's unlikely to have any side effects. The only reason to disable this is if you're using a filesystem where hard links are slow (e.g. HFS+). ### `maildir_empty_new` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) Should mail messages always be moved from the `new/` directory to `cur/`, even when the `Recent` flags aren't being reset? ### `maildir_stat_dirs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled, don't include non-directory files in a LIST response that begin with a dot. Thus, if disabled, Dovecot assumes that all the files beginning with a dot in the Maildir are Maildirs. You shouldn't have any non-directory files beginning with a dot in the Maildirs, but if you do you may need to set this to `yes`, in which case Dovecot needs to `stat()` each directory entry, which degrades the performance. Some filesystems (e.g. ext4) provide the directory/non-directory status for free without having to `stat()`. In those filesystems this setting is ignored. ### `maildir_very_dirty_syncs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Maildir](/main/core/config/mailbox_formats/maildir.html) If enabled (`yes`), Dovecot is assumed to be the only MUA that accesses Maildir directly, so the `cur/` directory is scanned only when its mtime changes unexpectedly or when the mail cannot otherwise be found. If enabled and another process (or a Dovecot process which doesn't update index files) does changes to `cur/` while the mailbox is simultaneously being modified by Dovecot, Dovecot may not notice those external changes. It is still safe to deliver new mails to `new/` using non-Dovecot software (except with `[mailbox_list_index = yes](/main/core/summaries/settings.html#mailbox_list_index)`, changes aren't noticed outside INBOX). ### `managesieve_logout_format` **Default:** `bytes=%{input}/%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) Specifies the string pattern used to compose the logout message of an authenticated session. The following substitutions are available: | Variable Name | Description | | --- | --- | | `%{input}` | Total number of bytes read from client | | `%{output}` | Total number of bytes sent to client | | `%{put_count}` | Number of scripts uploaded by client using PUTSCRIPT command | | `%{put_bytes}` | Number of bytes with script data sent by client using PUTSCRIPT command | | `%{get_count}` | Number of scripts downloaded by client using GETSCRIPT command | | `%{get_bytes}` | Number of bytes with script data sent to client using GETSCRIPT command | | `%{check_count}` | Number of scripts checked by client using CHECKSCRIPT command | | `%{check_bytes}` | Number of bytes with script data sent by client using CHECKSCRIPT command | | `%{deleted_count}` | Number of scripts deleted by client using DELETESCRIPT command | | `%{renamed_count}` | Number of scripts renamed by client using RENAMESCRIPT command | | `%{session}` | The client session ID | ### `mbox_dirty_syncs` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mbox_very_dirty_syncs](/main/core/summaries/settings.html#mbox_very_dirty_syncs)` - [mbox](/main/core/config/mailbox_formats/mbox.html) Enable optimized mbox syncing? For larger mbox files, it can take a long time to determine what has changed when the file is altered unexpectedly. Since the change in most cases consists solely of newly appended mail, Dovecot can operate more quickly if it starts off by simply reading the new messages, then falls back to reading the entire mbox file if something elsewhere in it isn't as expected. Dovecot assumes that external mbox file changes only mean that new messages were appended to it. Without this setting Dovecot re-reads the whole mbox file whenever it changes. There are various safeguards in place to make this setting safe even when other changes than appends were done to the mbox. The downside to this setting is that external message flag modifications may not be visible immediately. When this setting is enabled, Dovecot tries to avoid re-reading the mbox every time something changes. Whenever the mbox changes (i.e. timestamp or size), Dovecot first checks if the mailbox's size changed. If it didn't, it most likely meant that only message flags were changed so it does a full mbox read to find it. If the mailbox shrunk, it means that mails were expunged and again Dovecot does a full sync. Usually however the only thing besides Dovecot that modifies the mbox is the LDA which appends new mails to the mbox. So if the mbox size was grown, Dovecot first checks if the last known message is still where it was last time. If it is, Dovecot reads only the newly added messages and goes into "dirty mode". As long as Dovecot is in dirty mode, it can't be certain that mails are where it expects them to be, so whenever accessing some mail, it first verifies that it really is the correct mail by finding its X-UID header. If the X-UID header is different, it fallbacks to a full sync to find the mail's correct position. The dirty mode goes away after a full sync. If `[mbox_lazy_writes](/main/core/summaries/settings.html#mbox_lazy_writes)` was enabled and the mail didn't yet have an X-UID header, Dovecot uses the MD5 sum of a couple of headers to compare the mails. ### `mbox_dotlock_change_timeout` **Default:** `2 mins` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Override a lockfile after this amount of time if a dot-lock exists but the mailbox hasn't been modified in any way. ### `mbox_lazy_writes` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) If enabled, mbox headers (e.g., metadata updates, such as writing X-UID headers or flag changes) are not written until a full write sync is performed (triggered via IMAP EXPUNGE or CHECK commands and/or when the mailbox is closed). mbox rewrites can be costly, so this may avoid a lot of disk writes. Enabling this setting is especially useful with POP3, in which clients often delete all mail messages. One negative consequence of enabling this setting is that the changes aren't immediately visible to other MUAs. C-Client works the same way. The upside of this is that it reduces writes because multiple flag updates to same message can be grouped, and sometimes the writes don't have to be done at all if the whole message is expunged. The downside is that other processes don't notice the changes immediately (but other Dovecot processes do notice because the changes are in index files). ### `mbox_lock_timeout` **Default:** `5 mins` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) The maximum time to wait for all locks to be released before aborting. ### `mbox_read_locks` **Default:** `fcntl` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Allowed Values:** `dotlock``dotlock_try``fcntl``flock``lockf` **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Specify which locking method(s) to use for locking the mbox files during reading. Descriptions of the locking methods can be found at [mbox locking](/main/core/config/mailbox_formats/mbox.html#locking). ### `mbox_very_dirty_syncs` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)` - [mbox](/main/core/config/mailbox_formats/mbox.html) If enabled, Dovecot performs the optimizations from `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)` also for the IMAP SELECT, EXAMINE, EXPUNGE, and CHECK commands. If set, this option overrides `[mbox_dirty_syncs](/main/core/summaries/settings.html#mbox_dirty_syncs)`. ### `mbox_write_locks` **Default:** `dotlock fcntl` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Allowed Values:** `dotlock``dotlock_try``fcntl``flock``lockf` **See Also:** - [mbox](/main/core/config/mailbox_formats/mbox.html) Specify which locking method(s) to use for locking the mbox files during writing. Descriptions of the locking methods can be found at [mbox locking](/main/core/config/mailbox_formats/mbox.html#locking). ### `mdbox_preallocate_space` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)` - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: If enabled, preallocate space for newly created files. In creation of new mdbox files, their size is immediately preallocated as `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)`. This setting currently works only in Linux with certain filesystems (ext4 and xfs). ### `mdbox_rotate_interval` **Default:** _\[None\]_ **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: The maximum age the dbox file may reach before it's rotated. `0` means there is no age-based rotation. ### `mdbox_rotate_size` **Default:** `10M` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) mdbox only: The maximum size the dbox file may reach before it is rotated. ### `metric` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[metric_name](/main/core/summaries/settings.html#metric_name)` Creates a new metric. The filter name refers to the `[metric_name](/main/core/summaries/settings.html#metric_name)` setting. ### `metric_description` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Human-readable description of the metric. This is included in the HELP text sent to OpenMetrics. ### `metric_exporter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Event Export](/main/core/config/events/export.html) - `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` Export events matching the filter with this [event exporter](/main/core/config/events/export.html). Refers to the `[event_exporter_name](/main/core/summaries/settings.html#event_exporter_name)` setting. If empty, the events are used only for statistics, and no exporting is done. ### `metric_exporter_include` **Default:** `name hostname timestamps categories fields` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Specifies which parts of the event are exported to the serialized event: | Values | Description | | --- | --- | | `name` | The name of the event. | | `hostname` | The name of the host generating this event. | | `timestamps` | The event start and end timestamps. | | `categories` | A set of categories associated with this event. | | `fields` | The fields associated with this event. The fields that will be exported are defined by the `[metric_fields](/main/core/summaries/settings.html#metric_fields)` setting. | For example: ``` metric example { exporter_include = name hostname timestamps } ``` includes just the 3 specified parts, while ``` metric another_example { exporter_include = } ``` includes nothing and the exported event will be empty (i.e. `{}` in JSON). ### `metric_fields` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Changed: 2.4.0 All listed fields are exported to OpenMetrics as well. A list of fields included in the metric. All events have a default `duration` field that does not need to be listed explicitly. ### `metric_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Metric Filters](/main/core/config/events/filter.html#metric-filter-syntax) [Event filter](/main/core/config/events/filter.html#metric-filter-syntax) that matches the events belonging to this metric. ### `metric_group_by` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Creates a new [group\_by](/main/core/config/statistics.html#group-by) for dynamically generating sub-metrics based on the specified field's values. The filter name refers to the `[metric_group_by_field](/main/core/summaries/settings.html#metric_group_by_field)`. ### `metric_group_by_field` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Generate sub-metrics based on this event field name. The `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)` filter name refers to this setting. WARNING Avoid high-cardinality fields. Each distinct value creates a sub-metric kept in memory with no upper limit, so fields like usernames, email addresses, remote IPs, message IDs, or mailbox GUIDs will cause unbounded memory growth. Use low-cardinality fields, or reduce cardinality with the `exponential`/`linear` aggregation methods or `[metric_group_by_method_discrete_modifier](/main/core/summaries/settings.html#metric_group_by_method_discrete_modifier)`. ### `metric_group_by_method` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures `[metric_group_by_method_method = the aggregation method](/main/core/summaries/settings.html#metric_group_by_method_method)` for the `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)`. Only a single method can be specified for a group\_by. The filter name refers to the `[metric_group_by_method_method](/main/core/summaries/settings.html#metric_group_by_method_method)`. ### `metric_group_by_method_discrete_modifier` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures a modifier string for values grouped by the [discrete method](/main/core/config/statistics.html#discrete). [%variables](/main/core/settings/variables.html) and their functions can be used: `%{value}` The original value. `%{user | domain}` If the value is in `user@domain` format, this contains the `domain` text. Otherwise empty. ### `metric_group_by_method_exponential_base` **Default:** `10` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_min_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)` - `[metric_group_by_method_exponential_max_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` Configures the base for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). Only `2` and `10` are supported. ### `metric_group_by_method_exponential_max_magnitude` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_min_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_min_magnitude)` - `[metric_group_by_method_exponential_base](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)` Configures the maximum magnitude for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). ### `metric_group_by_method_exponential_min_magnitude` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_exponential_max_magnitude](/main/core/summaries/settings.html#metric_group_by_method_exponential_max_magnitude)` - `[metric_group_by_method_exponential_base](/main/core/summaries/settings.html#metric_group_by_method_exponential_base)` Configures the minimum magnitude for values grouped by the [exponential method](/main/core/config/statistics.html#exponential). ### `metric_group_by_method_linear_max` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_min](/main/core/summaries/settings.html#metric_group_by_method_linear_min)` - `[metric_group_by_method_linear_step](/main/core/summaries/settings.html#metric_group_by_method_linear_step)` Configures the maximum for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_linear_min` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_max](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` - `[metric_group_by_method_linear_step](/main/core/summaries/settings.html#metric_group_by_method_linear_step)` Configures the minimum for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_linear_step` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) - `[metric_group_by_method_linear_min](/main/core/summaries/settings.html#metric_group_by_method_linear_min)` - `[metric_group_by_method_linear_max](/main/core/summaries/settings.html#metric_group_by_method_linear_max)` Configures the step for values grouped by the [linear method](/main/core/config/statistics.html#linear). ### `metric_group_by_method_method` **Default:** `discrete` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `discrete``exponential``linear` **See Also:** - [Statistics: Group By](/main/core/config/statistics.html#group-by) Configures the aggregation method for the `[metric_group_by](/main/core/summaries/settings.html#metric_group_by)`. The `[metric_group_by_method](/main/core/summaries/settings.html#metric_group_by_method)` filter name refers to this setting. ### `metric_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the metric. It is visible in statistics outputs. The `[metric](/main/core/summaries/settings.html#metric)` filter name refers to this setting. ### `mmap_disable` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Disable mmap() usage? This must be set to `yes` if you store indexes to shared filesystems (i.e., if you use NFS or a clustered filesystem). ### `mysql` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[mysql_host](/main/core/summaries/settings.html#mysql_host)` Creates a new MySQL/MariaDB connection. If more than one is specified, the connections are automatically used for load balancing and for failover. The filter name refers to the `[mysql_host](/main/core/summaries/settings.html#mysql_host)` setting. ### `mysql_client_flags` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Flags to use when connecting to the database, provided as base-10 number. See [https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html](https://dev.mysql.com/doc/c-api/8.0/en/mysql-real-connect.html) ### `mysql_connect_timeout` **Default:** `5s` **Value:** [time](/main/core/settings/types.html#time) How long to wait for connection. ### `mysql_connection_limit` **Default:** `5` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of parallel connections. Currently MySQL queries are blocking, so only a single connection can be used in parallel. ### `mysql_dbname` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Database name to connect to. ### `mysql_host` **Default:** `localhost` **Value:** [string](/main/core/settings/types.html#string) Host or UNIX socket path to connect to. The `[mysql](/main/core/summaries/settings.html#mysql)` setting defaults to this. INFO MySQL drivers can default to using UNIX socket connection when host is `localhost` and port is `0` (default). To force it to use TCP connection, set `[mysql_host = 127.0.0.1](/main/core/summaries/settings.html#mysql_host)` or set `[mysql_port](/main/core/summaries/settings.html#mysql_port)` explicitly. ### `mysql_option_file` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) File to read for client library specific configuration. ### `mysql_option_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Section name to read from `[mysql_option_file](/main/core/summaries/settings.html#mysql_option_file)`. ### `mysql_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication. ### `mysql_port` **Default:** `0 (defaults to 3306 for TCP connections)` **Value:** [Port Number](/main/core/settings/types.html#port-number) Port to connect to. ### `mysql_read_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) Timeout when reading data from server. ### `mysql_ssl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [SSL](/main/core/config/ssl.html) Whether to use SSL when connecting to MySQL. Configure it using the `ssl_client_*` settings. See [SSL](/main/core/config/ssl.html). ### `mysql_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Username for authentication. ### `mysql_write_timeout` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) Timeout in seconds when writing data to server. ### `namespace` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) Creates a new namespace to the list of namespaces. The filter name refers to the `[namespace_name](/main/core/summaries/settings.html#namespace_name)` setting. Example: ``` namespace foo { [...] } ``` ### `namespace_alias_for` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Refers to an alias namespace's `[namespace_name](/main/core/summaries/settings.html#namespace_name)`. If multiple namespaces point to the same location, they should be marked as aliases against one primary namespace. This avoids duplicating work for some commands (listing the same mailbox multiple times). `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. Note: Alias namespaces often have `hidden=yes` and `list=no` so they are not visible unless clients have specifically configured them, and they're typically used when migrating to a different namespace prefix for existing users. Example: ``` namespace inbox { prefix = # ... } namespace alias { prefix = INBOX/ alias_for = inbox } ``` ### `namespace_disabled` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, namespace is disabled and cannot be accessed by user in any way. Useful when returned by a userdb lookup to easily configure per-user namespaces. ### `namespace_hidden` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, namespace will be hidden from IMAP NAMESPACE ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) command. ### `namespace_ignore_on_failure` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If namespace's storage initialization fails, by default the entire session will fail to start. If this is set, this namespace will be ignored instead. ### `namespace_inbox` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, this namespace will be considered the one holding the INBOX folder. There can be only one namespace defined like this. ### `namespace_list` **Default:** `yes` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `yes``no``children` **See Also:** - `[namespace_hidden](/main/core/summaries/settings.html#namespace_hidden)` Include this namespace in LIST output when listing its parent's folders. Options: | Value | Description | | --- | --- | | `children` | Namespace prefix list listed only if it has child mailboxes. | | `no` | Namespace and mailboxes not listed unless listing requests explicitly mailboxes under the namespace prefix. | | `yes` | Namespace and mailboxes are always listed. | It is still possible to list the namespace's folders by explicitly asking for them. For example, if this setting is `no`, using `LIST "" *` with namespace prefix "lazy-expunge/" won't list it, but using `LIST "" lazy-expunge/*` lists all folders under it. ### `namespace_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Name of the namespace. This is used only in configurations - it's not visible to user. The `[namespace](/main/core/summaries/settings.html#namespace)` filter refers to this setting. ### `namespace_order` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Sets display order in IMAP NAMESPACE ([RFC 2342](https://datatracker.ietf.org/doc/html/rfc2342)) command. Namespaces are automatically numbered if this setting does not exist. ### `namespace_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Specifies prefix for namespace. INFO Must end with `[namespace_separator](/main/core/summaries/settings.html#namespace_separator)`. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. Example: ``` namespace shared { prefix = Shared/ separator = / } ``` ### `namespace_separator` **Default:** `"." for Maildir; "/" for other mbox formats` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Namespaces: Hierarchy Separators](/main/core/config/namespaces.html#hierarchy-separators) Specifies the hierarchy separator for the namespace. The separator is a single character, which can't then otherwise be used in folder names. The commonly used separators are `.` and `/`, but other separators can be used as well. For example `^` is less likely to be found in normal folder names. Recommended value is to leave it empty and accept the default value. ### `namespace_subscriptions` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether subscriptions are stored in this namespace. This is usually `no` for shared namespaces so that the shared folders' subscriptions are stored in the user's primary subscriptions file. If `no`, the subscriptions are stored in the first parent namespace (based on the prefix) that has this setting enabled. Example: If this setting is `no` for a namespace with `prefix=foo/bar/`, Dovecot first sees if there's a `prefix=foo/` namespace with `subscriptions=yes` and then a namespace with an empty prefix. If neither is found, an error is given. ### `namespace_type` **Default:** `private` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `private``shared``public` The namespace type. One of: | Type | Description | | --- | --- | | `public` | Contains [public shared mailboxes](/main/core/config/shared_mailboxes.html#public-shared-mailboxes). | | `private` | Typically contains only user's own private mailboxes. | | `shared` | Contains other users' [user shared mailboxes](/main/core/config/shared_mailboxes.html#user-shared-mailboxes). | ### `notify_status` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Dependencies:** - [dictionary](/main/core/config/dict.html) **Plugin:** [notify-status plugin](/main/core/plugins/notify_status.html) The URI of the dictionary to use. This MUST be set for the plugin to be active. See [dictionary](/main/core/config/dict.html) for how to configure dictionaries. ``` notify_status { dict proxy { name = notify_status socket_path = dict-async } } ``` ### `notify_status_value` **Default:** `{"messages":%{messages},"unseen":%{unseen}}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **Plugin:** [notify-status plugin](/main/core/plugins/notify_status.html) A template of the string that will be written to the dictionary. The template supports variable substitution of the form `%{variable_name}`. Supported variable substitutions: | Field | Value | | --- | --- | | `first_recent_uid` | First recent UID | | `highest_modseq` | Highest modification sequence number | | `highest_pvt_modseq` | Highest private modification sequence number | | `mailbox` | Mailbox name | | `messages` | Number of messages | | `recent` | Number of recent messages (deprecated) | | `uidnext` | Predicted next UID value | | `uidvalidity` | Current UID validity | | `unseen` | Number of unseen messages | | `username` | Username (user@domain) | ### `oauth2` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [OAuth2 Authentication Database](/main/core/config/auth/databases/oauth2.html) Filter for oauth2 specific settings. ### `oauth2_active_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Attribute name for (optional) checking whether account is disabled. ### `oauth2_active_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Expected value in `active_attribute`. (empty = require present, but anything goes) ### `oauth2_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) Key-value fields to include in successful authentication. ### `oauth2_force_introspection` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Force introspection even if tokeninfo contains wanted fields. Set this to `yes` if you are using `[oauth2_active_attribute](/main/core/summaries/settings.html#oauth2_active_attribute)`. ### `oauth2_introspection_mode` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** ```auth``get``post``local` **See Also:** - `[oauth2_introspection_url](/main/core/summaries/settings.html#oauth2_introspection_url)` - `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` - `[oauth2_force_introspection](/main/core/summaries/settings.html#oauth2_force_introspection)` To enable oauth2 you must choose how to do token introspection. `[oauth2_introspection_url](/main/core/summaries/settings.html#oauth2_introspection_url)` is not required if `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` already provides all the necessary fields, or if you are using `local` validation. You can force introspection with `[oauth2_force_introspection](/main/core/summaries/settings.html#oauth2_force_introspection)`, if you need to it every time. With `local` validation, `[oauth2_tokeninfo_url](/main/core/summaries/settings.html#oauth2_tokeninfo_url)` is also ignored. | Value | Description | | --- | --- | | `auth` | GET request with Bearer authentication. | | `get` | GET request with token appended to URL. | | `post` | POST request with token=bearer\_token as content. | | `local` | Attempt to locally validate and decode JWT token. | ### `oauth2_introspection_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL for getting more information about token. ### `oauth2_issuers` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Valid issuer(s) for the token. ### `oauth2_local_validation` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[dict](/main/core/summaries/settings.html#dict)` A dictionary for fetching validation keys. Example: ``` oauth2_local_validation { dict fs { fs posix { prefix = /tmp/keys/ } } } ``` ### `oauth2_openid_configuration_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL to [RFC 7628](https://datatracker.ietf.org/doc/html/rfc7628) OpenID Provider Configuration Information schema. ### `oauth2_scope` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) A list of valid scopes. ### `oauth2_send_auth_headers` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Whether to send special headers about authentication to remote server. If you enable this, the following headers will be sent: `X-Dovecot-Auth-Protocol` Requested protocol, such as imap or pop3. `X-Dovecot-Auth-Local` Local IP address where client connected to. `X-Dovecot-Auth-Remote` Remote IP address of the client connection. ### `oauth2_token_expire_grace` **Default:** `1min` **Value:** [time](/main/core/settings/types.html#time) Specifies how long after token expiration it is still accepted. It shouldn't be 0 because clocks aren't exact, and network can add some delays. ### `oauth2_tokeninfo_url` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) URL for verifying token validity. Token is appended to the URL. Example: ``` oauth2_tokeninfo_url = http://endpoint/oauth/tokeninfo?access_token= ``` ### `oauth2_use_worker_with_mech` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[passdb_use_worker](/main/core/summaries/settings.html#passdb_use_worker)` Use worker process to verify token. This setting only applies to mechanism. If you want to use worker with `passdb oauth2`, use `[passdb_use_worker](/main/core/summaries/settings.html#passdb_use_worker)` instead. Worker processes are mostly useful for distributing local token validation to multiple CPUs. ### `oauth2_username_attribute` **Default:** `email` **Value:** [string](/main/core/settings/types.html#string) Username attribute in response. ### `oauth2_username_validation_format` **Default:** `%{user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[oauth2_username_attribute](/main/core/summaries/settings.html#oauth2_username_attribute)` Normalization for oauth2 provided username, this setting is normally not needed. You only need this if the username that comes from authentication will not otherwise match with `[oauth2_username_attribute](/main/core/summaries/settings.html#oauth2_username_attribute)` value. ### `passdb` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` **See Also:** - [passdb](/main/core/config/auth/passdb.html) - `[passdb_name](/main/core/summaries/settings.html#passdb_name)` - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` Creates a new [passdb](/main/core/config/auth/passdb.html). The filter name refers to the `[passdb_name](/main/core/summaries/settings.html#passdb_name)` setting. ### `passdb_default_password_scheme` **Default:** `CRYPT` specific [passdb](/main/core/config/auth/passdb.html) have different defaults `passdb_static`: `PLAIN` **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Changed: 2.4.3 Changed from `PLAIN` to `CRYPT`. The scheme that passwords are in the passdb, unless overridden by the passdb entry (typically by prefixing with `{SCHEME}`). ### `passdb_deny` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes` and the user is found from the `denied user database` the authentication will fail. ### `passdb_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[passdb_name](/main/core/summaries/settings.html#passdb_name)` - [passdb](/main/core/config/auth/passdb.html) - [Authentication Databases](/main/core/config/auth/databases/overview.html) The driver used for this password database. If empty, defaults to `[passdb_name](/main/core/summaries/settings.html#passdb_name)`. ### `passdb_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields) Passdb fields (and [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields)). The values can contain [%variables](/main/core/settings/variables.html). All `%variables` used here reflect the state **after** the current passdb lookup, and can refer to fields returned by previous passdb lookups. Depending on the passdb driver, it can also refer to variable fields returned by it (e.g. `%{ldap:fieldName}`). INFO The LDAP driver provides additional specific variables, see [LDAP authentication](/main/core/config/auth/databases/ldap.html) for more details. For example: ``` passdb ldap { fields { user = %{ldap:userId} proxy = yes host = %{ldap:proxyHost} } } ``` ### `passdb_fields_import_all` **Default:** `yes` For `passdb ldap` the default is `no`. **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` If yes, import all fields returned by the passdb lookup. If no, require `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` to explicitly add wanted fields. ### `passdb_ldap_bind` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Set `yes` to use authentication binding for verifying password's validity. This works by logging into LDAP server using the username and password given by client. The `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` is used to find the DN for the user. Note that the `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` are still used, only the password field is ignored in it. Before doing any search, the binding is switched back to the default DN. If you use this setting, it's a good idea to use a different `[ldap_connection_group](/main/core/summaries/settings.html#ldap_connection_group)` for userdb. That way one connection is used only for LDAP binds and another connection is used for user lookups. Otherwise the binding is changed to the default DN before each user lookup. ### `passdb_ldap_bind_userdn` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) If authentication binding is used, you can save one LDAP request per login if users' DN can be specified with a common template. The template can use the standard `[Settings variables](/main/core/settings/variables.html)`. Changed: 2.4.3 To avoid unwanted escaping of the output, add `| safe` filter to the safe variables (i.e. NOT `%{user}`). Note that you can't use any `%{ldap:*}` variables in `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)` if you use this setting, because no LDAP fields are looked up. Examples: - `passdb_ldap_bind_userdn = cn=%{user},ou=people,o=org` - `passdb_ldap_bind_userdn = cn=%{user},%{passdb:domain_dn | safe}` ### `passdb_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter for passdb lookup. Variables that can be used (see `[Settings variables](/main/core/settings/variables.html)` for full list). Example: ``` passdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) #... } ``` ### `passdb_master` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes` and the user is found from the [Master Users](/main/core/config/auth/master_users.html) the user is allowed to login as other users. ### `passdb_mechanisms_filter` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Changes:** - Added: 2.4.0 Skip the passdb if non-empty and the current auth mechanism is not listed here. If the value contains `lookup`, it matches for non-authenticating passdb lookups (e.g. lmtp/doveadm lookups). Example: ``` passdb passwd-file { driver = passwd-file mechanisms_filter = PLAIN LOGIN # ... } ``` ### `passdb_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[passdb](/main/core/summaries/settings.html#passdb)` - `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` Name of the passdb. The `[passdb](/main/core/summaries/settings.html#passdb)` filter name refers to this setting. If the `[passdb_driver](/main/core/summaries/settings.html#passdb_driver)` setting is empty, the `passdb_name` is used as the driver. This allows doing e.g.: ``` passdb passwd-file { passwd_file_path = /etc/dovecot/passwd ``` ### `passdb_pam_failure_show_msg` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Replace the default `Authentication failed` reply with PAM's failure. ### `passdb_pam_max_requests` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` Number of requests a auth-worker process handles for passdb pam before it dies. This configures similar behaviour as the `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` setting but it limits only the number of pam passdb requests, not all requests to be handled by an auth-worker. ### `passdb_pam_service_name` **Default:** `dovecot` **Value:** [string](/main/core/settings/types.html#string) The PAM service name to be used with the pam passdb. ### `passdb_pam_session` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot open a PAM session and close it immediately. ### `passdb_pam_setcred` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Make Dovecot create PAM credentials. The credentials are never deleted, which may cause problems with some PAM plugins. ### `passdb_result_failure` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication failed. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). ### `passdb_result_internalfail` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication failed due to an internal error. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). If any of the passdbs had an internal failure and the final passdb also returns `continue` the authentication will fail with `internal error`. ### `passdb_result_success` **Default:** `return-ok` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [passdb: Result Values](/main/core/config/auth/passdb.html#result-values) What to do after the passdb authentication succeeded. Possible values and their meaning are described fully at [passdb: Result Values](/main/core/config/auth/passdb.html#result-values). This is commonly used together with master passdb to specify that even after a successful master user authentication, the authentication should continue to the actual non-master passdb to lookup the user. ### `passdb_skip` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``authenticated``unauthenticated` Configures when passdbs should be skipped: | Value | Description | | --- | --- | | `never` | Never skip over this passdb. | | `authenticated` | Skip if an earlier passdb already authenticated the user successfully. | | `unauthenticated` | Skip if user hasn't yet been successfully authenticated by the previous passdbs. | ### `passdb_sql_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to lookup the passdb fields (`password` and other extra fields). ### `passdb_sql_update_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Removed: 2.4.5 Removed together with the `OTP` [authentication mechanisms](/main/core/config/auth/mechanisms/overview.html), which was its only user. SQL query to update the password. Currently used only by the `OTP` auth mechanism. ### `passdb_static_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 The static password to be used for all users authenticating using this passdb. ### `passdb_use_worker` **Default:** `no` specific [passdb](/main/core/config/auth/passdb.html) have different defaults **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, run the passdb lookup in auth-worker process instead of the main auth process. This setting is only used by some of the passdb drivers. ### `passdb_username_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Skip the passdb if non-empty and the username doesn't match the filter. This is mainly used to assign specific passdbs to specific domains. Space or comma-separated list of username filters that can have `*` or `?` wildcards. If any of the filters matches, the filter succeeds. Define negative matches by preceding `!`. If any of the negative filter matches, the filter won't succeed. **Example**: - Filter: `*@example.com *@example2.com !user@example.com` - Matches: - `any@example.com` - `user@example2.com` - Won't match: - `user@example.com` ### `passwd_file_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to the passwd-file. The path can consists from per-user variables such as s`%{user | domain}`. If the path starts with static path, then Dovecot ensures that the expanded path does not point outside of this static path. If the path starts with variable, this protection is disabled. For example if this is set to `/etc/dovecot/%{user | domain}/passwd`, then using login username such as `root@..` won't be allowed to expand into `/etc/dovecot../passwd`, as that would escape `/etc/dovecot`. If you use something like `%{env:PREFIX}}/%{user | domain}/passwd` as path, it is recommended that PREFIX points to deep enough path, such as `/etc/dovecot/domains/`, and you do not modify `[auth_username_chars](/main/core/summaries/settings.html#auth_username_chars)` to avoid including `/` as allowed character. ### `pgsql` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[pgsql_host](/main/core/summaries/settings.html#pgsql_host)` Creates a new PostgreSQL connection. If more than one is specified, the connections are automatically used for load balancing and for failover. The filter name refers to the `[pgsql_host](/main/core/summaries/settings.html#pgsql_host)` setting. ### `pgsql_connection_limit` **Default:** `5` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of parallel connections. ### `pgsql_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pgsql](/main/core/summaries/settings.html#pgsql)` Host to connect to. The `[pgsql](/main/core/summaries/settings.html#pgsql)` setting defaults to this. ### `pgsql_parameters` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) List of key/value settings passed to PostgreSQL. See [https://www.postgresql.org/docs/current/libpq-connect.html](https://www.postgresql.org/docs/current/libpq-connect.html) for available parameters. ### `pop3_client_workarounds` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) Workarounds for various POP3 client bugs can be enabled here. The following values are currently supported: `oe-ns-eoh` Because Outlook Express and Netscape Mail expect an end-of-headers line, this option sends one explicitly if none has been sent. `outlook-no-nuls` Because Outlook and Outlook Express hang if messages contain NUL characters, this setting replaces each of them with a `0x80` character. ### `pop3_delete_type` **Default:** `default` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `default``flag``expunge` Action to perform in POP3 when mails are deleted and `[pop3_deleted_flag](/main/core/summaries/settings.html#pop3_deleted_flag)` is enabled. ### `pop3_deleted_flag` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3_delete_type](/main/core/summaries/settings.html#pop3_delete_type)` Change POP3 behavior so a user cannot permanently delete messages via POP3. Instead, the messages are hidden from POP3 sessions by setting an IMAP flag, which Dovecot will filter out in future listings. To enable this behavior, enter the name of the IMAP keyword to use. INFO This keyword will visible on IMAP clients for the message. Example: ``` pop3_deleted_flag = $POP3Deleted ``` ### `pop3_enable_last` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Enable support for the POP3 LAST command. While this command has been removed from newer POP3 specs, some clients still attempt to use it. Enabling this causes the RSET command to clear all \\Seen flags that messages may have. ### `pop3_fast_size_lookups` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, use the virtual message size of the message for POP3 replies if available. POP3 requires message sizes to be listed as if they contain CR+LF line breaks; however, many POP3 servers instead return the sizes with pure line feeds (LFs), for the sake of speed. If enabled, use the virtual message size if available, before falling back to the incorrect, physical size (used by many POP3 servers) if judging the correct size would have required opening the message to determine. ### `pop3_lock_session` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, only one POP3 session may exist for any single user. ### `pop3_logout_format` **Default:** `top=%{top_count}/%{top_bytes}, retr=%{retr_count}/%{retr_bytes}, del=%{deleted_count}/%{deleted_bytes}, size=%{message_bytes}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The string to display to the client on POP3 logout (informational only). Variables available (in addition to `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)`): | Variable Name | Description | | --- | --- | | `%{input}` | Bytes read from the client | | `%{output}` | Bytes sent to the client | | `%{top_count}` | Number of TOP commands run | | `%{top_bytes}` | Bytes sent to the client because of TOP commands | | `%{retr_count}` | Number of RETR commands run | | `%{retr_bytes}` | Bytes sent to the client because of RETR commands | | `%{deleted_count}` | Number of deleted messages | | `%{deleted_bytes}` | Number of bytes in deleted messages | | `%{message_count}` | Number of messages before deletion | | `%{message_bytes}` | Mailbox size, in bytes, before deletion | | `%{uidl_change}` | The old and the new UIDL hash (which can be useful for identifying unexpected changes in UIDLs) | ### `pop3_migration_all_mailboxes` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) By default it's assumed that POP3 contains the same messages as IMAP INBOX. If there are any unexpected mails, the migration fails. If the POP3 server includes other folders' contents in POP3 as well, this setting needs to be enabled. It causes Dovecot to try to match POP3 messages in all the migrated folders, not just INBOX. There is no warning logged if any POP3 UIDLs are missing or if POP3 has messages that aren't found from IMAP. ### `pop3_migration_ignore_extra_uidls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) If IMAP INBOX has all messages that exist in POP3, but POP3 still has some additional messages, the migration fails. Enable this setting to log it as a warning and continue anyway. This could happen if there's a race condition where a new mail is just delivered and it shows up in POP3 but not in IMAP. ### `pop3_migration_ignore_missing_uidls` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) If POP3 has messages that aren't found from IMAP INBOX, and IMAP INBOX also has messages not found from POP3, the migration fails. Enable this setting to log it as a warning and continue anyway. ### `pop3_migration_mailbox` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) This setting points to the POP3 INBOX in the configured pop3c namespace. This setting is required for the plugin to be active. ``` pop3_migration_mailbox = POP3-MIGRATION-NS/INBOX ``` ### `pop3_migration_skip_size_check` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) IMAP and POP3 messages are attempted to be matched by the message sizes by default. This is the most efficient way of matching the messages, since both IMAP and POP3 listings can usually be looked up from indexes/caches. If the IMAP INBOX and POP3 listings don't match exactly, or if two adjacent messages have the same size, the rest of the messages are matched by reading their headers. If this setting is enabled, the message size check is skipped entirely and only headers are matched. This may be necessary for reliability if it's known that the IMAP and POP3 messages cannot be matched by size anyway. ### `pop3_migration_skip_uidl_cache` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [pop3-migration plugin](/main/core/plugins/pop3_migration.html) If imapc is configured with persistent indexes, the POP3 UIDLs are stored into the imapc mailbox's dovecot.index.cache files. Any following incremental migrations use these cached UIDLs if possible. This setting can be used to disable this in case there are any problems with the cache. This setting is unlikely to be ever needed. ### `pop3_no_flag_updates` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, do not attempt to mark mail messages as seen or non-recent when a POP3 session is involved. ### `pop3_reuse_xuidl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, and the mail message has an X-UIDL header, use this as the mail's UIDL. ### `pop3_save_uidl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) [Maildir](/main/core/config/mailbox_formats/maildir.html) only: If enabled, allow permanent saving of UIDLs sent to POP3 clients so that changes to `[pop3_uidl_format](/main/core/summaries/settings.html#pop3_uidl_format)` don't cause future changes to the corresponding UIDLs. ### `pop3_uidl_duplicates` **Default:** `allow` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `allow``rename` How to handle any duplicate POP3 UIDLs that may exist. Options: `allow` Show duplicates to clients. `rename` Append a temporary counter (such as -2 or -3) after the UIDL ### `pop3_uidl_format` **Default:** `%{uid | hex(8)}%{uidvalidity | hex(8)}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The POP3 unique mail identifier (UIDL) format to use. The following variables can be used in combination with the standard variable filters (e.g., `%{filename | upper}` supplies the filename in uppercase) and with `[Global variables](/main/core/settings/variables.html#global-variables)`: | Variable Name | Description | | --- | --- | | `%{uidvalidity}` | Mailbox's IMAP UIDVALIDITY value | | `%{uid}` | IMAP UID associated with the message | | `%{md5}` | MD5 sum of the mailbox headers in hex ([mbox](/main/core/config/mailbox_formats/mbox.html) only) | | `%{filename}` | Filename ([Maildir](/main/core/config/mailbox_formats/maildir.html) only) | | `%{guid}` | Dovecot GUID for the message | ### `pop3c_features` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of features, optimizations, and workarounds that can be enabled. Workarounds: `no-pipelining` Prevents use of the PIPELINING extension even when it is advertised. ### `pop3c_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The remote POP3 host to connect to. ### `pop3c_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_password](/main/core/summaries/settings.html#pop3c_password)` - `[pop3c_user](/main/core/summaries/settings.html#pop3c_user)` The master username to authenticate as on the remote POP3 host. To authenticate as a master user but use a separate login user, the following configuration should be employed, where the credentials are represented by masteruser and masteruser-secret: ``` pop3c_user = %{user} pop3c_master_user = masteruser pop3c_password = masteruser-secret ``` `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `pop3c_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_master_user](/main/core/summaries/settings.html#pop3c_master_user)` - `[pop3c_user](/main/core/summaries/settings.html#pop3c_user)` The authentication password for the remote POP3 server. If using master users, this setting will be the password of the master user. ### `pop3c_port` **Default:** `110` **Value:** [Port Number](/main/core/settings/types.html#port-number) The port on the remote POP3 host to connect to. ### `pop3c_quick_received_date` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, pop3c doesn't require calling TOP for each message in order to get the metadata. ### `pop3c_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Log all POP3 traffic input/output to this directory. ### `pop3c_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``pop3s``starttls` Use TLS to connect to the remote POP3 server. | Value | Description | | --- | --- | | `no` | No TLS | | `pop3s` | Explicitly connect to remote POP3 port using TLS | | `starttls` | Use POP3 STARTTLS command to switch to TLS connection | ### `pop3c_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[pop3c_ssl](/main/core/summaries/settings.html#pop3c_ssl)` Verify remote POP3 TLS certificate? Verification may be disabled during testing, but should be enabled during production use. Only used if `[pop3c_ssl](/main/core/summaries/settings.html#pop3c_ssl)` is enabled. ### `pop3c_user` **Default:** `%{user}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[pop3c_master_user](/main/core/summaries/settings.html#pop3c_master_user)` - `[pop3c_password](/main/core/summaries/settings.html#pop3c_password)` The user identity to be used for performing authentication to the source POP3 server. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `postmaster_address` **Default:** `postmaster@%{user|domain|default(hostname)}` **Value:** [string](/main/core/settings/types.html#string) The From address from which email rejection messages (bounces) are sent. As used here, `%{user | domain}` expands to the domain of the local user. Other `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used as well. ### `process_shutdown_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter to specify which events shutdown the process after finishing the current connections. This is mainly intended to save memory by preventing long-running imap processes that use a lot of memory (due to libc not freeing all of it to the OS). The syntax of the filter is described in [Global Metric Filters](/main/core/config/events/filter.html#global-filter-syntax). For example: ``` process_shutdown_filter = "event=mail_user_session_finished AND rss > 20MB" ``` ### `protocols` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) The list of protocols to enable. By default all protocols are disabled. For example: ``` # Only IMAP protocol enabled: protocols = imap # Enable also LMTP protocol (on top of IMAP): protocols { lmtp = yes } ``` ### `push_notification` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [push-notification plugin](/main/core/plugins/push_notification.html) The configuration value is a named filter for a specified driver, see [Push Notifications](/main/core/plugins/push_notification.html) for their names and their supported options. ### `push_notification_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Dependencies:** - `[push_notification](/main/core/summaries/settings.html#push_notification)` **Plugin:** [push-notification plugin](/main/core/plugins/push_notification.html) The name of the driver. This value determines the available options and the behavior. See [Push Notifications](/main/core/plugins/push_notification.html) for the list of supported drivers and options. ### `quota` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Create a new [quota root](/main/core/plugins/quota.html#quota-root). The filter name refers to `[quota_name](/main/core/summaries/settings.html#quota_name)` setting. Globally configured quota roots are used only for private namespaces. To use quota for public namespaces, configure it inside the public namespace. Example: ``` quota "User quota" { storage_size = 1G } ``` ### `quota_clone` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [dictionary](/main/core/config/dict.html) **Plugin:** [quota-clone plugin](/main/core/plugins/quota_clone.html) **Changes:** - Added: 2.4.0 Named filter for initializing dictionary used to update with quota clone information. ``` redis_host = 127.0.0.1 redis_port = 6379 quota_clone { dict redis { } } ``` ### `quota_driver` **Default:** `count` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Drivers](/main/core/plugins/quota.html#quota-drivers) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Quota driver to use. See [Quota Drivers](/main/core/plugins/quota.html#quota-drivers). ### `quota_enforce` **Default:** `yes` `quota_imapc { no }` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If disabled, the quota limit isn't actually enforced. The quota is still tracked and the current quota usage is visible to IMAP GETQUOTA commands. ### `quota_exceeded_message` **Default:** `Quota exceeded (mailbox for user is full)` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [quota plugin](/main/core/plugins/quota.html) The message specified here is passed on to a user who goes over quota. There are also other messages, which are currently hard coded: - When exceeding `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`: `Mail size is larger than the maximum size allowed by server configuration` - When exceeding `[quota_mailbox_message_count](/main/core/summaries/settings.html#quota_mailbox_message_count)`: `Too many messages in the mailbox` ### `quota_fs_message_limit` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If yes, use filesystem quota's inode limit as the message count limit. This can be useful with Maildir or sdbox. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_fs_mount_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If specified, enable FS quota for the specified mount path. Only mailboxes existing in this mount path have the quota enabled. Empty value looks up the mountpoint automatically. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_fs_type` **Default:** `any` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `any``user``group` **Plugin:** [quota plugin](/main/core/plugins/quota.html) Using `any` attempts to use the user quota first, with a fallback to group quota. Using `user` or `group` only attempts to use the user or the group quota, with a fallback to unlimited quota limit. Used only with [Quota Driver: Filesystem](/main/core/plugins/quota.html#driver-fs). ### `quota_full_tempfail` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [quota plugin](/main/core/plugins/quota.html) If enabled, return a temporary failure to the sending server if quota is exceeded. This allows the message to potentially be delivered later if the account moves under the quota limit at the time of redelivery. If disabled, the message is bounced with a permanent error returned to the sending server. ### `quota_hidden` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If yes, hide the quota root from IMAP GETQUOTA commands. ### `quota_ignore` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If yes, don't include this mailbox or namespace in quota calculations. Example: ``` namespace inbox { mailbox Trash { quota_ignore = yes } } namespace secondary { quota_ignore = yes } ``` ### `quota_ignore_unlimited` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If yes, ignore the quota root entirely if it has no quota limits. This means no tracking of the quota, and not making it visible to IMAP GETQUOTA commands. ### `quota_imapc_mailbox_name` **Default:** `INBOX` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_root_name](/main/core/summaries/settings.html#quota_imapc_root_name)` **Plugin:** [quota plugin](/main/core/plugins/quota.html) If non-empty, use `GETQUOTAROOT ` to get the imapc quota root. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### `quota_imapc_root_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` **Plugin:** [quota plugin](/main/core/plugins/quota.html) If `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` is empty, use `GETQUOTA ` to get the imapc quota. Some servers may have an empty quota root name. This is why if this setting and `[quota_imapc_mailbox_name](/main/core/summaries/settings.html#quota_imapc_mailbox_name)` both have empty values, this setting is used. Used only with [Quota Driver: Imapc](/main/core/plugins/quota.html#driver-imapc). ### `quota_mail_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Quota: Maximum Saved Mail Size](/main/core/plugins/quota.html#maximum-saved-mail-size) **Plugin:** [quota plugin](/main/core/plugins/quota.html) The maximum message size that is allowed to be saved (e.g. by LMTP, IMAP APPEND or `[doveadm save](/main/core/summaries/doveadm.html#save)`). If configured, will also be advertized with `APPENDLIMIT` extensions for IMAP clients, and `SIZE` extension for LMTP clients. ### `quota_mailbox_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Quota: Maximum Mailbox Count](/main/core/plugins/quota.html#maximum-mailbox-count) **Plugin:** [quota plugin](/main/core/plugins/quota.html) **Changes:** - Added: 2.4.0 Maximum number of mailboxes that can be created. Each namespace is tracked separately, so e.g. shared mailboxes aren't counted towards the user's own limit. ### `quota_mailbox_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [quota plugin](/main/core/plugins/quota.html) **Changes:** - Added: 2.4.0 Maximum number of messages that can be created in a single mailbox. ### `quota_message_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Maximum number of messages for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_message_percentage](/main/core/summaries/settings.html#quota_message_percentage)` to get the final value (in this mailbox or namespace). This is reported as the MESSAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ### `quota_message_percentage` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` **Plugin:** [quota plugin](/main/core/plugins/quota.html) Multiplier for the `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` setting (in this mailbox/namespace). This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_message_count = 10000 namespace inbox { mailbox Trash { # 110% * 10000 = 11000 limit quota_message_percentage = 110 } } ``` ### `quota_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[quota](/main/core/summaries/settings.html#quota)` - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Name of the [quota root](/main/core/plugins/quota.html#quota-root). The `[quota](/main/core/summaries/settings.html#quota)` filter name refers to this setting. The quota root name is just an arbitrary string that is sent to IMAP clients, which in turn may show it to the user. The name has no meaning. ### `quota_over_status` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Named filter for executing the overquota-flag script. The `[execute](/main/core/summaries/settings.html#execute)` setting is required to be specified inside the filter. ### `quota_over_status_current` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) **Plugin:** [quota plugin](/main/core/plugins/quota.html) An identifier that indicates whether the overquota-flag is active for a user. This identifier is compared against `[quota_over_status_mask](/main/core/summaries/settings.html#quota_over_status_mask)` to determine if the overquota-flag should be set for the user. Usually, this value will be loaded via [userdb](/main/core/config/auth/userdb.html). ### `quota_over_status_lazy_check` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If enabled, overquota-flag is checked only when current quota usage is going to already be checked anyway. This prevents any additional storage I/O that would be caused by the overquota-flag check. ### `quota_over_status_mask` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota: Overquota Flag](/main/core/plugins/quota.html#overquota-flag) **Plugin:** [quota plugin](/main/core/plugins/quota.html) The search string to match against `[quota_over_status_current](/main/core/summaries/settings.html#quota_over_status_current)` to determine if the overquota-flag is set for the user. Wildcards can be used in a generic way, e.g. `*yes` or `*TRUE*`. ### `quota_status_nouser` **Default:** `REJECT Unknown user` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) **Plugin:** [quota plugin](/main/core/plugins/quota.html) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a nonexistent users. ### `quota_status_overquota` **Default:** `554 5.2.2 %{error}` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) **Plugin:** [quota plugin](/main/core/plugins/quota.html) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is over the quota. The `%{error}` variable expands to the more detailed reason, which is generally `[quota_exceeded_message](/main/core/summaries/settings.html#quota_exceeded_message)`. ### `quota_status_success` **Default:** `OK` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) **Plugin:** [quota plugin](/main/core/plugins/quota.html) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a user that is under the quota. ### `quota_status_toolarge` **Default:** `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Status Service](/main/core/plugins/quota.html#quota-status-service) **Plugin:** [quota plugin](/main/core/plugins/quota.html) [Quota Status Service](/main/core/plugins/quota.html#quota-status-service): Response when asking quota for a mail that is larger than user's `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` or `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`. Empty value defaults to `[quota_status_overquota](/main/core/summaries/settings.html#quota_status_overquota)`. ### `quota_storage_extra` **Default:** `0` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If set, increase the `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` (for the mailbox/namespace) by this amount. This is an alternative to using `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)`, although both can also be used. This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_storage_size = 1G namespace inbox { mailbox Trash { # 1G + 100M = 1100M quota_storage_extra = 100M } } ``` ### `quota_storage_grace` **Default:** `10 M` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) If set, allows message deliveries (LDA, LMTP) to exceed quota once by this amount. After the quota is already over the limit, the grace no longer applies. This prevents a situation where some smaller mails may still become delivered, but larger mail deliveries fail, and the user may not have received any warning about reaching the quota limit. ### `quota_storage_percentage` **Default:** `100` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` - `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Multiplier for the `[quota_storage_size](/main/core/summaries/settings.html#quota_storage_size)` setting (in this mailbox/namespace). This is an alternative to using `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)`, although both can also be used. This may be useful to exceed the regular quota limit in some mailboxes, such as allowing clients that move messages with IMAP COPY+EXPUNGE to Trash folder to temporarily exceed the quota. Example: ``` quota_storage_size = 1G namespace inbox { mailbox Trash { # 110% * 1G = 1100M quota_storage_percentage = 110 } } ``` ### `quota_storage_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` - `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` - `[quota_message_count](/main/core/summaries/settings.html#quota_message_count)` - [Quota Root](/main/core/plugins/quota.html#quota-root) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Quota storage size limit for the [Quota Root](/main/core/plugins/quota.html#quota-root). This value is still multiplied by `[quota_storage_percentage](/main/core/summaries/settings.html#quota_storage_percentage)` and then increased by `[quota_storage_extra](/main/core/summaries/settings.html#quota_storage_extra)` to get the final value (in this mailbox or namespace). This is reported as the STORAGE limit in IMAP GETQUOTA commands. Using `0` as the value means the same as `unlimited`. ### `quota_warning` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Create a new [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The filter name refers to `[quota_warning_name](/main/core/summaries/settings.html#quota_warning_name)` setting. The `[execute](/main/core/summaries/settings.html#execute)` setting is required to be specified inside the filter. The order of `quota_warning` filters in the configuration is important: Only the first warning that matches the rules is executed. This means you must configure the highest limits first. ### `quota_warning_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Name of the [quota warning](/main/core/plugins/quota.html#quota-warning-scripts). The `[quota_warning](/main/core/summaries/settings.html#quota_warning)` filter name refers to this setting. This name is only used within the configuration to identify the quota warning - it has no meaning otherwise. ### `quota_warning_resource` **Default:** `storage` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `storage``message` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Which [quota resource](/main/core/plugins/quota.html#quota-limits) the quota warning is tracking. ### `quota_warning_threshold` **Default:** `over` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `over``under` **See Also:** - [Quota Warning Scripts](/main/core/plugins/quota.html#quota-warning-scripts) **Plugin:** [quota plugin](/main/core/plugins/quota.html) Should the quota warning be executed when quota grows over the limit, or when it drops under the limit. ### `rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Directory where to create `*.in` and `*.out` rawlog files, one per TCP connection. The directory must already exist and be writable by the process. No error is logged if the directory doesn't exist. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. Example: ``` protocol imap { rawlog_dir = /tmp/rawlog/%{user} # if you want to put files into user's homedir, use this, do not use ~ #rawlog_dir = %{home}/rawlog } ``` ### `recipient_delimiter` **Default:** `+` **Value:** [string](/main/core/settings/types.html#string) The separator between the :user and :detail address parts. ### `redis_db_id` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Database number. ### `redis_expire` **Default:** `infinite` **Value:** [time](/main/core/settings/types.html#time) Expiration value for all keys. ### `redis_host` **Default:** `127.0.0.1` **Value:** [string](/main/core/settings/types.html#string) Redis server host. ### `redis_key_prefix` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Prefix to add to all keys. ### `redis_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Redis server password. ### `redis_port` **Default:** `6379` **Value:** [Port Number](/main/core/settings/types.html#port-number) Redis server port. ### `redis_request_timeout` **Default:** `30s` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) How long to wait for answer before aborting request. ### `redis_socket_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) UNIX socket path to the Redis server. This is preferred over `[redis_host](/main/core/summaries/settings.html#redis_host)` if both are set. ### `rejection_reason` **Default:** `Your message to <%{to}> was automatically rejected:%{literal('\r\n')}%{reason}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) A human-readable message for the recipients of bounce messages. The following variables are allowed, including `[Global variables](/main/core/settings/variables.html#global-variables)`: | Variable Name | Description | | --- | --- | | `%{reason}` | Reason for rejection | | `%{subject}` | Original subject line | | `%{to}` | Recipient address | The variable values are obtained from the mail being delivered or the delivery protocol. ### `rejection_subject` **Default:** `Rejected: %{subject}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[rejection_reason](/main/core/summaries/settings.html#rejection_reason)` The Subject: header to use for bounce messages. See `[rejection_reason](/main/core/summaries/settings.html#rejection_reason)` for the list of variables that can be used. ### `sendmail_path` **Default:** `/usr/sbin/sendmail` **Value:** [string](/main/core/settings/types.html#string) The binary to use for sending email. Used only if `[submission_host](/main/core/summaries/settings.html#submission_host)` is not set. ### `service` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service_name](/main/core/summaries/settings.html#service_name)` Creates a new service to the list of services. The filter name refers to the `[service_name](/main/core/summaries/settings.html#service_name)` setting. ### `service_chroot` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` The processes are chrooted to this directory at startup. Relative to `[base_dir](/main/core/summaries/settings.html#base_dir)`. ### `service_client_limit` **Default:** `[default_client_limit](/main/core/summaries/settings.html#default_client_limit)` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of simultaneous client connections per process. Once this number of connections is received, the next incoming connection will prompt Dovecot to spawn another process. ### `service_drop_priv_before_exec` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Drop all privileges after forking, but before executing the binary. This is mainly useful for dumping core files on non-Linux OSes, since the processes are no longer in "setuid" mode. This setting can't be used with non-empty chroot. ### `service_executable` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` The binary path to execute and its parameters. If the path doesn't begin with `/`, it's relative to `[base_dir](/main/core/summaries/settings.html#base_dir)`. ### `service_extra_groups` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` Secondary UNIX groups that this process belongs to. WARNING This may be dangerous to use for the same reason as `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)`. ### `service_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) The primary UNIX group (GID) which runs this process. ### `service_idle_kill_interval` **Default:** `[default_idle_kill_interval](/main/core/summaries/settings.html#default_idle_kill_interval)` **Value:** [time](/main/core/settings/types.html#time) Time interval between killing extra idling processes. During the interval the master process tracks the lowest number of idling processes for the service. Afterwards it sends `SIGINT` notification to that many idling processes. If the processes are still idling when receiving the signal, they shut down themselves. Using `infinite` disables the idle-killing. ### `service_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[service](/main/core/summaries/settings.html#service)` Name of the service. The `[service](/main/core/summaries/settings.html#service)` filter name refers to this setting. ### `service_process_limit` **Default:** `[default_process_limit](/main/core/summaries/settings.html#default_process_limit)` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) The maximum number of processes that may exist for this service. Changed: 2.4.5 However, if `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` > 1, when the process reaches `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)`, the process is no longer counted towards its process limit. Otherwise long-lived connections in the old process could prevent creation of new processes. ### `service_process_min_avail` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` - `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` Minimum number of processes that always should be available to accept more client connections. Note that if `[service_client_limit = 1](/main/core/summaries/settings.html#service_client_limit)`, this means there are always that many processes that are not doing anything. When a new process launches, one of the idling processes will accept the connection and a new idling process is launched. - For `[service_restart_request_count = 1](/main/core/summaries/settings.html#service_restart_request_count)` processes this decreases the latency for handling new connections, because there's no need to wait for processes to fork. This is usually not necessary to to be set. Large `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` values might be useful in some special cases, like if there are a lot of POP3 users logging in exactly at the same time to check mails. - For `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` to a value `!=1` and `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)` to a value `>1` processes it could be set to the number of CPU cores (you can use `%{system:cpu_count}`) on the system to balance the load among them. This is commonly used with `*-login` processes. - For `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` with a value of `!=1` and `[service_client_limit = 1](/main/core/summaries/settings.html#service_client_limit)` processes it is likely not useful to use this, because generally there are already some idling processes waiting to accept new connections. However, it's not harmful either, since `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` includes the existing idling processes when counting how many new idling processes are needed. ### `service_protocol` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[protocols](/main/core/summaries/settings.html#protocols)` If non-empty, this service is enabled only when the protocol name is listed in `[protocols](/main/core/summaries/settings.html#protocols)` setting. ### `service_restart_request_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Number of client connections to handle until the process kills itself. Use `unlimited` to keep the process alive. `1` means only a single connection is handled until the process is stopped - this is the most secure choice since there's no way for one connection's state to leak to the next one. For better performance this can be set higher, but ideally not `unlimited` since more complex services can have small memory leaks and/or memory fragmentation and the process should get restarted eventually. For example `100` or `1000` can be good values. ### `service_reuse_port` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.4 Distribute TCP connections to processes more evenly using the [SO\_REUSEPORT](https://lwn.net/Articles/542629/) option on Linux. When enabled, `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` must be the same as `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`. This setting is mainly intended to be enabled for login processes. When `no`, all processes listening on the socket try to accept it at the same time. Whichever process is fastest (gets scheduled first by the kernel) gets the connection. This can lead to rather uneven distribution of connections. On the positive side, this behaves more gracefully once the process reaches `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`: it simply doesn't accept new connections, allowing other processes to handle them instead. If all processes are full, the master process rejects new connections. When `yes`, a separate listener socker is created for each process at startup. This is why `[service_process_min_avail](/main/core/summaries/settings.html#service_process_min_avail)` must be the same as `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`. Each process gets one of these listener sockets and uses it to accept connections. The kernel assigns incoming connections to these listener sockets based on the connection hash, so the connections should be rather evenly distributed across processes. This also means that if any process has reached `[service_client_limit](/main/core/summaries/settings.html#service_client_limit)`, it must start rejecting new connections, even if other processes aren't full. However, because of the more even distribution of connections, it's expected that when one process is full, other processes are nearly full as well. ### `service_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)` Type of this service: | Value | Description | | --- | --- | | `` | The default. | | `login` | Used by login services. The login processes have "all processes full" notification fd. It's used by the processes to figure out when no more client connections can be accepted because client and process limits have been reached. The login processes can then kill some of their oldest connections that haven't logged in yet. | | `worker` | Used by various worker services. It's normal for worker processes to fill up to `[service_process_limit](/main/core/summaries/settings.html#service_process_limit)`, and there shouldn't be a warning logged about it. | | `startup` | Creates one process at startup. | | `log` `config` `anvil` | Treated specially by these specific services. | ### `service_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[default_login_user](/main/core/summaries/settings.html#default_login_user)` - `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` UNIX user (UID) which runs this process. `[default_login_user](/main/core/summaries/settings.html#default_login_user)` should be used for `type=login` processes and `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` should be used for other processes that don't require root privileges. ### `service_vsz_limit` **Default:** `[default_vsz_limit](/main/core/summaries/settings.html#default_vsz_limit)` **Value:** [size](/main/core/settings/types.html#size) Limit the process's address space (both `RLIMIT_DATA` and `RLIMIT_AS` if available). When the space is reached, some memory allocations may start failing with "Out of memory", or the kernel may kill the process with signal 9. This setting is mainly intended to prevent memory leaks from eating up all of the memory, but there can be also legitimate reasons why the process reaches this limit. For example a huge mailbox may not be accessed if this limit is too low. Use `unlimited` to disable this entirely. ### `shutdown_clients` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, all processes are killed when the master process is shutdown. Otherwise, existing processes will continue to run. This may be useful to not interrupt earlier sessions, but may not be desirable if restarting Dovecot to apply a security update, for example. ### `sieve_editheader_header` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` - `[sieve_editheader_header_forbid_add](/main/core/summaries/settings.html#sieve_editheader_header_forbid_add)` - `[sieve_editheader_header_forbid_delete](/main/core/summaries/settings.html#sieve_editheader_header_forbid_delete)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Configures a new message header, which can be forbidden to be added or deleted. The filter name refers to the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` setting. ### `sieve_editheader_header_forbid_add` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Forbid adding the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` to the message header. Addition of the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, adding this header to this setting has no effect. ### `sieve_editheader_header_forbid_delete` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Forbid deleting the `[sieve_editheader_header_name](/main/core/summaries/settings.html#sieve_editheader_header_name)` from the message header. Deleting the `Received:` and `Auto-Submitted:` fields is always forbidden, while removing the `Subject:` header cannot be prohibited, as required by the RFC specification. Therefore, using this setting with one of these headers has no effect. ### `sieve_editheader_header_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) - `[sieve_editheader_header](/main/core/summaries/settings.html#sieve_editheader_header)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The name of the message header that is forbidden to be added or deleted. ### `sieve_editheader_max_header_size` **Default:** `2k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve editheader extension](/main/core/config/sieve/extensions/editheader.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum size in bytes of a header field value passed to the addheader command. The minimum value for this setting is `1024` bytes. ### `sieve_execute_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory where the plugin looks for programs (shell scripts) to execute directly for the `vnd.dovecot.execute` extension. ### `sieve_execute_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Configures the maximum execution time after which the program run by the `vnd.dovecot.execute` extension is forcibly terminated. ### `sieve_execute_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.execute` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_execute_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.execute` extension. ### `sieve_extensions` **Default:** `` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The Sieve language extensions available to users. By default, all supported extensions are available, except for deprecated extensions, extensions that add the ability to change messages, extensions that require explicit configuration, or extensions that are still under development. Some system administrators may want to disable certain Sieve extensions or enable those that are not available by default. Supported extensions are listed at [Sieve extensions](/main/core/config/sieve/overview.html#extensions). Example: ``` # Enable the vacation-seconds extension in addition to all # extensions enabled by default. sieve_extensions { vacation-seconds = yes } ``` ### `sieve_extlists_list` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This named list filter creates a new list definition for the ":list" match type in Sieve (extlists extension). The filter name refers to the `[sieve_extlists_list_name](/main/core/summaries/settings.html#sieve_extlists_list_name)` setting. That is the name of the list as it will be accessible from the Sieve. A dict definition that is placed inside the scope of this list filter will serve as the lookup for this extlists list. So, values from Sieve are looked up in the defined dict. This is often used to define an address book or whitelist external to the Sieve script. If no dict is present, the list will function as an empty list. Example: ``` sieve_extlists_list :addrbook:default { dict proxy { name = addressbook } } ``` ### `sieve_extlists_list_max_lookup_size` **Default:** `1k` **Value:** [size](/main/core/settings/types.html#size) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum size of a value looked up from this Sieve extlists list. If the value is too large, the lookup will be skipped and will yield no result. Note that individual lists may have different limits, meaning that lookups in other lists may still succeed. ### `sieve_extlists_list_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve extlists extension](/main/core/config/sieve/extensions/extlists.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The name of the list as it will be accessible through the Sieve ":list" match type (extlists extension). According to the standard, this name must be a valid URI. This implementation further limits that to a valid URN or TAG URI. Note that the standard URN and TAG equality rules dictate that at least parts of these URIs are case-sensitive, so it is best to consider the configured name case-sensitive entirely. The `[sieve_extlists_list](/main/core/summaries/settings.html#sieve_extlists_list)` filter refers to this setting. ### `sieve_filter_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory where the plugin looks for programs (shell scripts) to execute directly and filter messages through for the `vnd.dovecot.filter` extension. ### `sieve_filter_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Configures the maximum execution time after which the program run by the `vnd.dovecot.filter` extension is forcibly terminated. ### `sieve_filter_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.filter` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_filter_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.filter` extension. ### `sieve_global_extensions` **Default:** `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Which Sieve language extensions are **only** available in global scripts. This can be used to restrict the use of certain Sieve extensions to administrator control, for instance when these extensions can cause security concerns. This setting has higher precedence than `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`, meaning that the extensions enabled with this setting are never available to the user's personal script no matter what is specified for the `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting. The syntax of this setting is identical to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`, with the difference that extensions are enabled or disabled for exclusive use in global scripts. Currently, no extensions are marked as such by default. ### `sieve_implicit_extensions` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) WARNING Do not use this setting unless you really need to! The Sieve language extensions implicitly available to users. The extensions listed in this setting do not need to be enabled explicitly using the Sieve "require" command. This behavior directly violates the Sieve standard, but can be necessary for compatibility with some existing implementations of Sieve (notably jSieve). The syntax and semantics of this setting are otherwise identical to `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)`. ### `sieve_max_actions` **Default:** `32` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` - `[sieve_notify_max_notifications](/main/core/summaries/settings.html#sieve_notify_max_notifications)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum number of actions that can be performed during a single script execution. If set to `0`, no limit on the total number of actions is enforced. This is the total across all action classes. Per-class limits such as `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` and `[sieve_notify_max_notifications](/main/core/summaries/settings.html#sieve_notify_max_notifications)` apply on top: the effective per-class limit is `min(per_class_setting, sieve_max_actions)`. When the total cap is hit first, execution aborts with "total number of actions exceeds policy limit". ### `sieve_max_cpu_time` **Default:** `30s` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) **Changes:** - Changed: 2.4.5 Changed from `0` (unlimited) to `30s`. Previously, `30s` was the default only for the `sieve_env_location_ms` (IMAP) filter; it now applies globally including MDA execution. The maximum amount of CPU time that a Sieve script is allowed to use while executing. If the execution exceeds this resource limit, the script ends with an error, causing the implicit "keep" action to be executed. This limit is not only enforced for a single script execution, but also cumulatively for the last executions within a configurable timeout (see `[sieve_resource_usage_timeout](/main/core/summaries/settings.html#sieve_resource_usage_timeout)`). ### `sieve_max_redirects` **Default:** `4` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum number of redirect actions that can be performed during a single script execution. `0` means redirect is prohibited. The effective per-script limit on `redirect` actions is `min(sieve_max_redirects, sieve_max_actions)`: when the total number of actions in a script reaches `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` (default `32`), execution aborts with "total number of actions exceeds policy limit" before this per-class limit is reached. To allow more than `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` redirect actions, raise that setting as well. ### `sieve_notify_mailto_envelope_from` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[sieve_redirect_envelope_from](/main/core/summaries/settings.html#sieve_redirect_envelope_from)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Defines the source of the notification sender address for e-mail notifications. ### `sieve_pipe_bin_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory where the plugin looks for programs (shell scripts) to execute directly and pipe messages to for the `vnd.dovecot.pipe` extension. ### `sieve_pipe_exec_timeout` **Default:** `10s` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Configures the maximum execution time after which the program run by the `vnd.dovecot.pipe` extension is forcibly terminated. ### `sieve_pipe_input_eol` **Default:** `crlf` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `crlf``lf` **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Determines the end-of-line character sequence used for the data piped to external programs run by the `vnd.dovecot.pipe` extension. The default is currently "crlf", which represents a sequence of the carriage return (CR) and line feed (LF) characters. This matches the Internet Message Format ([RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322)) and what Sieve itself uses as a line ending. Set this setting to "lf" to use a single LF character instead. ### `sieve_pipe_socket_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve-extprograms plugin](/main/core/plugins/sieve_extprograms.html) Points to a directory relative to the `[base_dir](/main/core/summaries/settings.html#base_dir)` where the plugin looks for script service sockets for the `vnd.dovecot.pipe` extension. ### `sieve_plugins` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The Pigeonhole Sieve interpreter can have plugins of its own. Using this setting, the used plugins can be specified. Check [Sieve plugins](/main/core/config/sieve/overview.html#extensions) for available plugins. ### `sieve_quota_script_count` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum number of personal Sieve scripts a single user can have. ### `sieve_quota_storage_size` **Default:** `unlimited` **Value:** [size](/main/core/settings/types.html#size) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The maximum amount of disk storage a single user's scripts may occupy. ### `sieve_redirect_envelope_from` **Default:** `sender` **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Specifies what envelope sender address is used for redirected messages. Normally, the Sieve `redirect` command copies the sender address for the redirected message from the processed message So, the redirected message appears to originate from the original sender. The following options are supported for this setting: | Option | Description | | --- | --- | | `sender` | The sender address is used | | `recipient` | The final recipient address is used | | `orig_recipient` | The original recipient is used | | `user_email` | The user's primary address is used. This is configured with the `[sieve_user_email](/main/core/summaries/settings.html#sieve_user_email)` setting. If that setting is not configured, `user_email` is equal to `sender`. | | `postmaster` | The `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)` configured for LDA/LMTP. | | `` | Redirected messages are always sent from `user@domain`. The angle brackets are mandatory. The null `<>` address is also supported. | When the envelope sender of the processed message is the null address `<>`, the envelope sender of the redirected message is also always `<>`, irrespective of what is configured for this setting. ### `sieve_resource_usage_timeout` **Default:** `1h` **Value:** [time](/main/core/settings/types.html#time) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) To prevent abuse, the Sieve interpreter can record resource usage of a Sieve script execution in the compiled binary if it is significant. Currently, this happens when CPU system + user time exceeds 1.5 seconds for one execution. Such high resource usage is summed over time in the binary and once that cumulative resource usage exceeds the limits (`[sieve_max_cpu_time](/main/core/summaries/settings.html#sieve_max_cpu_time)`), the Sieve script is disabled in the binary for future execution, even if an individual execution exceeded no limits. If the last time high resource usage was recorded is older than `[sieve_resource_usage_timeout](/main/core/summaries/settings.html#sieve_resource_usage_timeout)`, the resource usage in the binary is reset. This means that the Sieve script is only disabled when the limits are cumulatively exceeded within this timeout. With the default configuration this means that the Sieve script is only disabled when the total CPU time of Sieve executions that lasted more than 1.5 seconds exceeds 30 seconds in the last hour. A disabled Sieve script can be reactivated by the user by uploading a new version of the Sieve script after the excessive resource usage times out. An administrator can force reactivation by forcing a script compile (e.g. using the sievec command line tool). ### `sieve_script` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Creates a new Sieve script storage to the list of script storages. The filter name refers to the `[sieve_script_storage](/main/core/summaries/settings.html#sieve_script_storage)` setting. Example: ``` sieve_script personal { [...] } ``` ### `sieve_script_active_path` **Default:** `~/.dovecot.sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) When [ManageSieve server](/main/core/config/sieve/managesieve.html) is used, one script in the storage can be active; i.e., evaluated at delivery. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)`. For that storage driver, the active script in the storage directory is pointed to by a symbolic link. This setting configures where this symbolic link is located. If the `[sieve_script_path](/main/core/summaries/settings.html#sieve_script_path)` setting points to a regular file, this setting has no effect (and ManageSieve cannot be used). ### `sieve_script_bin_path` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Points to the directory where the compiled binaries for this script location are stored. This directory is created automatically if possible. If this setting is not configured, the behavior depends on the storage driver. For the [file storage driver](/main/core/plugins/sieve.html#file-storage-driver), the binaries are for example stored in the same directory as the corresponding sieve scripts. Don't specify the same directory for multiple script storages (e.g. with different types), as this will result in undefined behavior. For one, the same script name could point to different scripts for different storages, leading to a conflict, because the storages will try to use the same binary file. Multiple mail users can share a single script directory if the associated script storage configuration is identical between users and all users share the same system credentials (uid, gid). All users will then use the same scripts for that storage type. ### `sieve_script_cause` **Default:** `delivery` **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The causes for executing Sieve scripts from this storage. This is currently only relevant for the IMAPSieve plugin. For standard Sieve execution at message delivery the cause is "delivery". Other causes can be `append`, `copy` and `flag`. ### `sieve_script_driver` **Default:** `file` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The Sieve script storage driver to use. See [Script storage drivers](/main/core/plugins/sieve.html#script-storage-drivers). ### `sieve_script_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The LDAP search filter that is used to find the entry containing the Sieve script. These variables can be used: | Variable | Description | | --- | --- | | `%{user}` | username | | `%{user | username}` | user part in user@domain, same as `%{user}` if there's no domain | | `%{user | domain}` | domain part in user@domain, empty if user there's no domain | | `%{home}` | user's home directory | | `%{name}` | name of the Sieve script | ### `sieve_script_ldap_modified_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The name of the attribute used to detect modifications to the LDAP entry. ### `sieve_script_ldap_script_attribute` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [LDAP storage driver](/main/core/plugins/sieve.html#ldap-storage-driver) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The name of the attribute containing the Sieve script. ### `sieve_script_name` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The (default) name of a Sieve script retrieved from this storage. If the name of the Sieve script cannot be derived somehow from the storage (e.g. from a file name) and the storage for a single script is specified, this option is required (e.g. for dict locations that must point to a particular script). If the name of the script is derived from the storage, the value of the `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)` setting overrides that name. If the Sieve interpreter explicitly queries for a specific name (e.g. to let the Sieve [Sieve include extension](/main/core/config/sieve/extensions/include.html) retrieve a script from the [global script storage](/main/core/plugins/sieve.html#script-storage-type-global)), this setting has no effect. For the Sieve script storage with type [default](/main/core/plugins/sieve.html#script-storage-type-default), the name is required to make the default script visible in ManageSieve. See '[Sieve visible default script](/main/core/plugins/sieve.html#visible-default-script)' ### `sieve_script_path` **Default:** `~/sieve` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [File storage driver](/main/core/plugins/sieve.html#file-storage-driver) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) A file system path pointing to a Sieve script file or a directory containing one or more Sieve script files with names structured as `.sieve`. This setting only applies when `[sieve_script_driver = file](/main/core/summaries/settings.html#sieve_script_driver)` ### `sieve_script_precedence` **Default:** `infinite` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The precedence of this Sieve storage in the configuration. Normally, script storages with matching type and cause are accessed in the order these are specified in the configuration. This setting can be used to configure an explicit order. Storages will be accessed with lower precedence first. ### `sieve_script_storage` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage](/main/core/plugins/sieve.html#script-storage) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The identifier of the Sieve script storage. This is used only in configurations and by command line tools - it's not visible to the user. The `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter refers to this setting. ### `sieve_script_type` **Default:** `personal` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Script storage types](/main/core/plugins/sieve.html#script-storage-types) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The type of the configured Sieve script storage. See [Script storage types](/main/core/plugins/sieve.html#script-storage-types). ### `sieve_spamtest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Value format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. The syntax is identical to the `[sieve_spamtest_status_header](/main/core/summaries/settings.html#sieve_spamtest_status_header)` setting. This setting cannot be used together with `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. ### `sieve_spamtest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_spamtest_score_max_header](/main/core/summaries/settings.html#sieve_spamtest_score_max_header)`. This setting can specify a fractional score with a decimal point. ### `sieve_spamtest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Value format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. ### `sieve_spamtest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_spamtest_text_value](/main/core/summaries/settings.html#sieve_spamtest_text_value)`). ### `sieve_spamtest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) When the `[sieve_spamtest_status_type](/main/core/summaries/settings.html#sieve_spamtest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_spamtest_status_header = X-Spam-Verdict sieve_spamtest_status_type = text sieve_spamtest_text_value { 1 = Not Spam 10 = Spam } ``` ### `sieve_trace_addresses` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Enables showing byte code addresses in the trace output, rather than only the source line numbers. ### `sieve_trace_debug` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Enables highly verbose debugging messages that are usually only useful for developers. ### `sieve_trace_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The directory where trace files are written. Trace debugging is disabled if this setting is not configured or if the directory does not exist. If the path is relative or it starts with `~/` it is interpreted relative to the current user's home directory. ### `sieve_trace_level` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve trace debugging](/main/core/plugins/sieve.html#trace-debugging) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The verbosity level of the trace messages. Trace debugging is disabled if this setting is not configured. Options are: | Option | Description | | --- | --- | | `actions` | Only print executed action commands, like keep, fileinto, reject, and redirect. | | `commands` | Print any executed command, excluding test commands. | | `tests` | Print all executed commands and performed tests. | | `matching` | Print all executed commands, performed tests and the values matched in those tests. | ### `sieve_user_email` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The primary e-mail address for the user. This is used as a default when no other appropriate address is available for sending messages. If this setting is not configured, either the postmaster or null `<>` address is used as a sender, depending on the action involved. This setting is important when there is no message envelope to extract addresses from, such as when the script is executed in IMAP. ### `sieve_user_log_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) The path to the file where the user log file is written. A default location is used if this setting is not explicitly configured: - If the main user's personal Sieve script storage (as configured with `[sieve_script](/main/core/summaries/settings.html#sieve_script)` uses the [File storage driver](/main/core/plugins/sieve.html#file-storage-driver), the logfile is set to `.log` by default. - If the script is not stored as a file, the default user log file is `~/.dovecot.sieve.log`. ### `sieve_vacation_check_recipient` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This setting determines whether the checks for implicit delivery are performed. If this is skipped, this means that the vacation command does not verify that the message is explicitly addressed at the recipient. Use this option with caution. Specifying `no` will violate the Sieve standards and can cause vacation replies to be sent for messages not directly addressed at the recipient. ### `sieve_vacation_default_period` **Default:** `7d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Specifies the default period that is used when no `:days` or `:seconds` tag is specified. The configured value must lie between `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)` and `[sieve_vacation_max_period](/main/core/summaries/settings.html#sieve_vacation_max_period)`. ### `sieve_vacation_max_period` **Default:** `60d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Specifies the maximum period that can be specified for the `:days` tag of the vacation command. The configured value must be larger than `[sieve_vacation_min_period](/main/core/summaries/settings.html#sieve_vacation_min_period)`. A value of `0` has a special meaning: it indicates that there is no upper limit. ### `sieve_vacation_min_period` **Default:** `1d` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Specifies the minimum period that can be specified for the `:days` and `:seconds` tags of the vacation command. A minimum of `0` indicates that users are allowed to make the Sieve interpreter send a vacation response message for every incoming message that meets the other reply criteria (refer to [RFC 5230](https://datatracker.ietf.org/doc/html/rfc5230)). A value of zero is not recommended. ### `sieve_vacation_send_from_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This setting determines whether vacation messages are sent with the SMTP `MAIL FROM` envelope address set to the recipient address of the Sieve script owner. Normally this is set to `<>`, which is the default as recommended in the specification. This is meant to prevent mail loops. However, there are situations for which a valid sender address is required and this setting can be used to accommodate for those. ### `sieve_vacation_use_original_recipient` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This specifies whether the original envelope recipient should be used in the check for implicit delivery. The vacation command checks headers of the incoming message, such as `To:` and `Cc:` for the address of the recipient, to verify that the message is explicitly addressed at the recipient. If the recipient address is not found, the vacation action will not trigger a response to prevent sending a reply when it is not appropriate. Normally only the final recipient address is used in this check. This setting allows including the original recipient specified in the SMTP session if available. This is useful to handle mail accounts with aliases. Use this option with caution: if you are using aliases that point to more than a single account, as senders can get multiple vacation responses for a single message. Use the [LDA](/main/core/config/delivery/lda.html) `-a` option or the LMTP/LDA `[lda_original_recipient_header](/main/core/summaries/settings.html#lda_original_recipient_header)` setting to make the original SMTP recipient available to Sieve. ### `sieve_virustest_score_max_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Value Format: ` [ ":" ]` Some spam scanners include the maximum score value in one of their status headers. Using this setting, this maximum can be extracted from the message itself instead of specifying the maximum manually using the setting `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. The syntax is identical to `[sieve_virustest_status_header](/main/core/summaries/settings.html#sieve_virustest_status_header)`. This setting cannot be used together with `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. ### `sieve_virustest_score_max_value` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This statically specifies the maximum value a numeric spam score can have. This setting cannot be used together with `[sieve_virustest_score_max_header](/main/core/summaries/settings.html#sieve_virustest_score_max_header)`. This setting can specify a fractional score with a decimal point. ### `sieve_virustest_status_header` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) Value Format: ` [ ":" ]` This specifies the header field that contains the result information of the spam scanner and it may express the syntax of the content of the header. If no matching header is found in the message, the spamtest command will match against `0`. This is a structured setting. The first part specifies the header field name. Optionally, a POSIX regular expression follows the header field name, separated by a colon. Any white space directly following the colon is not part of the regular expression. If the regular expression is omitted, any header content is accepted and the full header value is used. When a regular expression is used, it must specify one match value (inside brackets) that yields the desired spam scanner result. If the header does not match the regular expression or if no value match is found, the spamtest test will match against `0` during Sieve script execution. ### `sieve_virustest_status_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `score``strlen``text` **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) This specifies the type of status result that the spam/virus scanner produces. This can either be a numeric score (`score`), a string of identical characters (`strlen`), e.g. `'*******'`, or a textual description (`text`), e.g. `'Spam'` or `'Not Spam'` (see `[sieve_virustest_text_value](/main/core/summaries/settings.html#sieve_virustest_text_value)`). ### `sieve_virustest_text_value` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) **Plugin:** [sieve plugin](/main/core/plugins/sieve.html) When the `[sieve_virustest_status_type](/main/core/summaries/settings.html#sieve_virustest_status_type)` setting is set to `text`, this setting specifies what values the spamtest test will match against to obtain the score value. For each recognized numeric score value this string list setting yields the text to match against. Score values between 0 and 10 are recognized. Example: ``` sieve_virustest_status_header = X-VirusCheck sieve_virustest_status_type = text sieve_virustest_text_value { 1 = Clean 2 = Presumed Clean 3 = Not sure 4 = Almost Certain 5 = Definitely } ``` ### `sql_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL driver to use for any SQL database accesses. ### `sqlite_journal_mode` **Default:** `wal` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `delete``wal` Allows using write-ahead logging mode for database. ### `sqlite_path` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Path to the sqlite database. ### `sqlite_readonly` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) Specifies that this database is read-only and should not be attempted to be created or written to. ### `sqlite_synchronous` **Default:** `default` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `default``off``normal``full``extra` **Changes:** - Added: 2.4.3 Sets the synchronization mode for SQlite database. See [https://sqlite.org/pragma.html#pragma\_synchronous](https://sqlite.org/pragma.html#pragma_synchronous) for full explanation of values. This has no effect if database is opened in read-only mode. Setting special value `default` elides pragma call. ### `ssl` **Default:** `yes` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `yes``no``required` **See Also:** - [SSL configuration](/main/core/config/ssl.html#configuration-overview) - `[inet_listener_ssl](/main/core/summaries/settings.html#inet_listener_ssl)` The level of SSL support. This setting affects both the implicit SSL ports and the STARTTLS commands. Options: `no` SSL/TLS is completely disabled. `yes` SSL/TLS is enabled, but not necessarily required for clients. `required` SSL/TLS (or otherwise secure connection) is required for all imap, pop3, managesieve and submission protocol client connections. This differs from `[auth_allow_cleartext](/main/core/summaries/settings.html#auth_allow_cleartext)` in that even non-cleartext authentication mechanisms aren't allowed without SSL/TLS. See [secured connections](/main/core/config/ssl.html#secured-connections) for details which connections are considered `secured`. The value of this setting affects the `secured` state of connections. See [secured connections](/main/core/config/ssl.html#secured-connections). Note: Do not confuse this with the `[inet_listener_ssl](/main/core/summaries/settings.html#inet_listener_ssl)` setting, which gets used inside `inet_listener { ssl }`. ### `ssl_cipher_list` **Default:** `ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH (for ssl_server, empty for ssl_client)` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` - `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The list of SSL ciphers to use for TLSv1.2 and below connections, in order of preference. Use `[ssl_cipher_suites](/main/core/summaries/settings.html#ssl_cipher_suites)` for TLSv1.3 connections. You do not need to edit this setting in order to disable specific SSL protocols; that is best done with `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` instead. This setting is used for both incoming and outgoing SSL connections. ### `ssl_cipher_suites` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The list of SSL cipher suites to use for TLSv1.3 connections, in order of preference. Use `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` for TLSv1.2 and below connections. This setting is used for both incoming and outgoing SSL connections. See: [https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites](https://wiki.openssl.org/index.php/TLS1.3#Ciphersuites) ### `ssl_client` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_server](/main/core/summaries/settings.html#ssl_server)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Named filter, which can be used for specifying SSL client settings. ### `ssl_client_ca_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The directory where trusted SSL CA certificates can be found. For example `/etc/ssl/certs`. These certificates are used only for outgoing SSL connections (e.g. with the imapc driver). For extra security you might want to point to a directory containing certificates only for the CAs that are actually needed for the server operation instead of all the root CAs. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) File containing the trusted SSL CA certificates. For example `/etc/ssl/certs/ca-bundle.crt`. These certificates are used only for outgoing SSL connections (e.g. with the [imapc](/main/core/config/mailbox_formats/imapc.html) driver). Note that this setting isn't recommended to be used with large CA bundles, because all the certificates are read into memory. This leads to excessive memory usage, because it gets multiplied by the number of imap processes. It's better to either use `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` setting or use a CA bundle that only contains the CAs that are actually necessary for the server operation. If both `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)` are empty, the system CA certificates are used. ### `ssl_client_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Public SSL certificate used for outgoing SSL connections. This is generally needed only when the server authenticates the client using the certificate. `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` is also needed for the private certificate. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. If it is password protected, set the password with `[ssl_client_key_password](/main/core/summaries/settings.html#ssl_client_key_password)`. Example: ``` ssl_client_cert_file = /etc/dovecot/dovecot-client.crt ssl_client_key_file = /etc/dovecot/dovecot-client.key ``` ### `ssl_client_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Password for the `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)`. ### `ssl_client_require_valid_cert` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Require a valid certificate when connecting to external SSL services? ### `ssl_crypto_device` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Available Values: Which SSL crypto device to use. ### `ssl_curve_list` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Colon separated list of elliptic curves to use, in order of preference. An empty value uses the defaults from the SSL library. This setting is used for both incoming and outgoing SSL connections. Example: ``` ssl_curve_list = P-521:P-384:P-256 ``` ### `ssl_min_protocol` **Default:** `TLSv1.2` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The minimum SSL protocol version Dovecot accepts. It cannot be empty. This setting is used for both incoming and outgoing SSL connections. Supported values are: `ANY` WARNING This value is meant for tests only. It should not be used in any deployment of any value/relevance. `TLSv1` Support TLSv1+. (TLSv1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.1` Support TLSv1.1+. (TLSv1.1 deprecated: [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)) `TLSv1.2` Support TLSv1.2+. `TLSv1.3` Support TLSv1.3+. `LATEST` Support only the latest version available. ### `ssl_options` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `compression``no_ticket` **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Additional options for SSL. This setting is used for both incoming and outgoing SSL connections. Currently supported options are: `compression` Enable compression. `no_ticket` Disable SSL session tickets. ### `ssl_peer_certificate_fingerprint_hash` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) - [passdb: check\_client\_fp Extra field](/main/core/config/auth/passdb.html#check-client-fp) An OpenSSL digest algorithm name to use to hash peer certificate names. Setting this value is required for `ssl_client_cert_fp` and `ssl_client_cert_pubkey_fp` availability in `[login_log_format_elements](/main/core/summaries/settings.html#login_log_format_elements)` and also in authentication variables. Weak algorithms are explicitly disallowed, such as MD5. ### `ssl_server` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client](/main/core/summaries/settings.html#ssl_client)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Named filter, which can be used for specifying SSL server settings. ### `ssl_server_alt_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Alternative SSL certificate that will be used if the algorithm differs from the primary certificate. This is useful when migrating to e.g. an ECDSA certificate. Example: ``` ssl_server_alt_cert_file = /path/to/alternative/cert.pem ``` ### `ssl_server_alt_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_server_alt_cert_file](/main/core/summaries/settings.html#ssl_server_alt_cert_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Private key for `[ssl_server_alt_cert_file](/main/core/summaries/settings.html#ssl_server_alt_cert_file)`. Example: ``` ssl_server_alt_key_file = /path/to/alternative/key.pem ssl_server_alt_cert_file = /path/to/alternative/cert.pem ``` ### `ssl_server_ca_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_client_require_valid_cert](/main/core/summaries/settings.html#ssl_client_require_valid_cert)` - `[ssl_server_request_client_cert](/main/core/summaries/settings.html#ssl_server_request_client_cert)` List of SSL CA certificates that are used to validate whether SSL certificates presented by incoming imap/pop3/etc. client connections are valid. Example: ``` ssl_server_ca_file = /etc/dovecot/ca.crt ssl_server_request_client_cert = yes auth_ssl_require_client_cert = yes ``` ### `ssl_server_cert_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Path to the PEM-encoded X.509 SSL/TLS certificate presented for incoming imap/pop3/etc. client connections. The `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` is also needed for the private certificate. Example: ``` ssl_server_cert_file = /etc/ssl/private/dovecot.crt ssl_server_key_file = /etc/ssl/private/dovecot.key ``` ### `ssl_server_cert_username_field` **Default:** `commonName` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Field name in the SSL client certificate that is used for `[auth_ssl_username_from_cert](/main/core/summaries/settings.html#auth_ssl_username_from_cert)`. The most common choices are `commonName` and `x500UniqueIdentifier`. Note: `[auth_ssl_username_from_cert](/main/core/summaries/settings.html#auth_ssl_username_from_cert)` MUST be enabled. ### `ssl_server_dh_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Path to the Diffie-Hellman parameters file. This setting isn't needed if using only ECDSA certificates. You can generate a new parameters file by, for example, running `openssl dhparam -out dh.pem 4096` on a machine with sufficient entropy (this may take some time). Example: ``` ssl_server_dh_file = /path/to/dh.pem ``` ### `ssl_server_key_file` **Default:** _\[None\]_ **Value:** [File](/main/core/settings/types.html#file) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)` - `[ssl_server_key_password](/main/core/summaries/settings.html#ssl_server_key_password)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) Path to the PEM-encoded X.509 SSL/TLS private key for `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)`. Example: ``` ssl_server_cert_file = /etc/ssl/private/dovecot.crt ssl_server_key_file = /etc/ssl/private/dovecot.key ``` ### `ssl_server_key_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[ssl](/main/core/summaries/settings.html#ssl)` - `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` - [SSL configuration](/main/core/config/ssl.html#configuration-overview) The password to use if `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)` is password-protected. Since this file is often world-readable, you may wish to specify the path to a file containing the password, rather than the password itself, by using the format `ssl_server_key_password = ) instead of full path syntax. `whitespace-before-path` Allow one or more spaces or tabs between 'MAIL FROM:' and path and between 'RCPT TO:' and path. ### `submission_host` **Default:** _\[None\]_ **Value:** [URL](/main/core/settings/types.html#url) Use this SMTP submission host to send messages. Overrides `[sendmail_path](/main/core/summaries/settings.html#sendmail_path)` value, if set. ### `submission_logout_format` **Default:** `in=%{input} out=%{output}` **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) The SMTP Submission logout format string. Variables supported, including `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)`: | Variable Name | Description | | --- | --- | | `%{input}` | Bytes read from client | | `%{output}` | Bytes sent to client | | `%{command_count}` | Number of commands received from client | | `%{reply_count}` | Number of replies sent to client | | `%{transaction_id}` | ID of the current transaction, if any | ### `submission_max_mail_size` **Default:** `40M` **Value:** [size](/main/core/settings/types.html#size) The maximum message size accepted for relay. This value is announced in the SMTP SIZE capability. If empty, this value is either determined from the relay server or left unlimited if no limit is known; the relay MTA will reply with error if some unknown limit exists there, which will be passed back to the client. ### `submission_max_recipients` **Default:** `unlimited` **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) Maximum number of recipients accepted per connection. ### `submission_relay_command_timeout` **Default:** `5mins` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for SMTP commands issued to the submission service's relay server. The timeout is reset every time more data is being sent or received. ### `submission_relay_connect_timeout` **Default:** `30secs` **Value:** [time (milliseconds)](/main/core/settings/types.html#millisecond-time) Timeout for connecting to and logging into the submission service's relay server. ### `submission_relay_host` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Host of the relay server (REQUIRED to provide the submission service). ### `submission_relay_master_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Master user name for authentication to the relay MTA if authentication is required. ### `submission_relay_max_idle_time` **Default:** `29mins` **Value:** [time](/main/core/settings/types.html#time) Submission relay max idle time for connection to relay MTA. ### `submission_relay_password` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Password for authentication to the relay MTA if authentication is required. ### `submission_relay_port` **Default:** `25` **Value:** [Port Number](/main/core/settings/types.html#port-number) Port for the submission relay server. ### `submission_relay_rawlog_dir` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - [rawlog](/main/core/admin/rawlog.html) Write protocol logs for relay connection to this directory for debugging. `[Mail user variables](/main/core/settings/variables.html#mail-user-variables)` can be used. ### `submission_relay_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``smtps``starttls` If enabled, SSL/TLS is used for the connection to the relay server. Available values: `no` No SSL connection is used. `smtps` An SMTPS connection (immediate SSL) is used. `starttls` The STARTTLS command is used to establish the TLS layer. ### `submission_relay_ssl_verify` **Default:** `yes` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, TLS certificate of the relay server must be verified. ### `submission_relay_trusted` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, the relay server is trusted. Determines whether we try to send (Postfix-specific) XCLIENT data to the relay server (only if enabled). ### `submission_relay_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) User name for authentication to the relay MTA if authentication is required. ### `submission_ssl` **Default:** `no` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `no``smtps``starttls` **See Also:** - `[submission_host](/main/core/summaries/settings.html#submission_host)` If enabled, use SSL/TLS to connect to `[submission_host](/main/core/summaries/settings.html#submission_host)`. Available values: `no` No SSL connection is used. `smtps` An SMTPS connection (immediate SSL) is used. `starttls` The STARTTLS command is used to establish the TLS layer. ### `submission_timeout` **Default:** `30secs` **Value:** [time](/main/core/settings/types.html#time) **See Also:** - `[submission_host](/main/core/summaries/settings.html#submission_host)` Timeout for submitting outgoing messages. ### `syslog_facility` **Default:** `mail` **Value:** [string](/main/core/settings/types.html#string) The syslog facility used if you're logging to syslog. ### `textcat_config_path` **Default:** `` **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[language](/main/core/summaries/settings.html#language)` **Plugin:** [fts plugin](/main/core/plugins/fts.html) Path to the textcat/exttextcat configuration file, which lists the supported languages. This is recommended to be changed to point to a minimal version of a configuration that supports only the languages listed in `[language](/main/core/summaries/settings.html#language)`. Doing this improves language detection performance during indexing and also makes the detection more accurate. Example: ``` textcat_config_path = /usr/share/libexttextcat/fpdb.conf ``` ### `trash_priority` **Default:** _\[None\]_ **Value:** [unsigned integer](/main/core/settings/types.html#unsigned-integer) **Plugin:** [trash plugin](/main/core/plugins/trash.html) If non-zero, enables the trash plugin for the mailbox with the specified priority. Mailboxes with smaller priority number are emptied before mailboxes with a larger priority number. If there are multiple mailboxes with the same priority, expunge the oldest mail from them first. Example where Trash is emptied before Spam: ``` namespace inbox { mailbox Trash { trash_priority = 1 } mailbox Spam { trash_priority = 2 } } ``` ### `unix_listener` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **See Also:** - `[service](/main/core/summaries/settings.html#service)` - `[unix_listener_path](/main/core/summaries/settings.html#unix_listener_path)` Creates a new UNIX listener for a `[service](/main/core/summaries/settings.html#service)`. The filter name refers to the `[unix_listener_path](/main/core/summaries/settings.html#unix_listener_path)` setting. ### `unix_listener_group` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Group of the listener file. Empty (default) means GID 0 (root/wheel). ### `unix_listener_mode` **Default:** `0600` **Value:** [octal unsigned integer](/main/core/settings/types.html#octal-unsigned-integer) Mode of the file. Note that `0600` is an octal value, while `600` is a different decimal value. Setting mode to `0` disables the listener. ### `unix_listener_path` **Default:** _\[None\]_ **Value:** [String without variables](/main/core/settings/types.html#string-without-variables) **See Also:** - `[base_dir](/main/core/summaries/settings.html#base_dir)` - `[unix_listener](/main/core/summaries/settings.html#unix_listener)` Path to the UNIX socket file, relative to `[base_dir](/main/core/summaries/settings.html#base_dir)` setting. The `[unix_listener](/main/core/summaries/settings.html#unix_listener)` filter name refers to this setting. ### `unix_listener_type` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **Changes:** - Added: 2.4.0 Listener type. This string value has service-specific meaning and is used to distinguish different listener types that one service may employ. ### `unix_listener_user` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Owner of the listener file. Empty (default) means UID 0 (root). ### `userdb` **Default:** _\[None\]_ **Value:** [Named List Filter](/main/core/settings/types.html#named-list-filter) **Dependencies:** - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` **See Also:** - [userdb](/main/core/config/auth/userdb.html) - `[userdb_name](/main/core/summaries/settings.html#userdb_name)` - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` Creates a new [userdb](/main/core/config/auth/userdb.html). The filter name refers to the `[userdb_name](/main/core/summaries/settings.html#userdb_name)` setting. ### `userdb_driver` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[userdb_name](/main/core/summaries/settings.html#userdb_name)` - [userdb](/main/core/config/auth/userdb.html) - [Authentication Databases](/main/core/config/auth/databases/overview.html) The driver used for this user database. If empty, defaults to `[userdb_name](/main/core/summaries/settings.html#userdb_name)`. ### `userdb_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) **See Also:** - [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) Userdb fields (and [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields)). The values can contain [%variables](/main/core/settings/variables.html). All `%variables` used here reflect the state **after** the current userdb lookup, and can refer to fields returned by previous userdb lookups. Depending on the userdb driver, it can also refer to variable fields returned by it (e.g. `%{ldap:fieldName}`). INFO The LDAP driver provides additional specific variables, see [LDAP authentication](/main/core/config/auth/databases/ldap.html) for more details. For example: ``` userdb ldap { fields { user = %{ldap:userId} home = /home/%{ldap:mailboxPath} uid = vmail gid = vmail } } ``` ### `userdb_fields_import_all` **Default:** `yes` For `userdb ldap` the default is `no`. **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` If yes, import all fields returned by the userdb lookup. If no, require `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)` to explicitly add wanted fields. ### `userdb_ldap_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter for userdb lookup. Variables that can be used (see `[Settings variables](/main/core/settings/variables.html)` for full list). Example: ``` userdb ldap { filter = (&(objectClass=posixAccount)(uid=%{user})) #... } ``` ### `userdb_ldap_iterate_fields` **Default:** _\[None\]_ **Value:** [String List](/main/core/settings/types.html#string-list) Attributes to get a list of all users. Currently only the attribute `user` is supported. Example: ``` userdb ldap { iterate_filter = (objectClass=smiMessageRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} } } ``` ### `userdb_ldap_iterate_filter` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) Filter to get a list of all users. ``` userdb ldap { iterate_filter = (objectClass=smiMessageRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} } } ``` ### `userdb_name` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) **See Also:** - `[userdb](/main/core/summaries/settings.html#userdb)` - `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` Name of the userdb. The `[userdb](/main/core/summaries/settings.html#userdb)` filter name refers to this setting. If the `[userdb_driver](/main/core/summaries/settings.html#userdb_driver)` setting is empty, the `userdb_name` is used as the driver. This allows doing e.g.: ``` userdb passwd-file { passwd_file_path = /etc/dovecot/passwd ``` ### `userdb_result_failure` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do if the user was not found from the userdb. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). ### `userdb_result_internalfail` **Default:** `continue` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do after the userdb failed due to an internal error. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). If any of the userdbs had an internal failure and the final userdb also returns `continue` the authentication will fail with `internal error`. ### `userdb_result_success` **Default:** `return-ok` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `return-ok``return``return-fail``continue``continue-ok``continue-fail` **See Also:** - [userdb: Result Values](/main/core/config/auth/userdb.html#result-values) What to do if the user was successfully found from the userdb. Possible values and their meaning are described fully at [userdb: Result Values](/main/core/config/auth/userdb.html#result-values). ### `userdb_skip` **Default:** `never` **Value:** [string](/main/core/settings/types.html#string) **Allowed Values:** `never``found``notfound` Configures when userdbs should be skipped: | Value | Description | | --- | --- | | `never` | Never skip over this userdb. | | `found` | Skip if an earlier userdbs already found the user. | | `notfound` | Skip if previous userdbs haven't yet found the user. | ### `userdb_sql_iterate_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to list all available usernames. ### `userdb_sql_query` **Default:** _\[None\]_ **Value:** [string](/main/core/settings/types.html#string) SQL query to lookup the userdb fields. ### `userdb_static_allow_all_users` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Added: 2.4.0 Skip user existence verification via passdb lookup. ### `userdb_use_worker` **Default:** `no` specific [userdb](/main/core/config/auth/userdb.html) have different defaults **Value:** [boolean](/main/core/settings/types.html#boolean) If `yes`, run the userdb lookup in auth-worker process instead of the main auth process. This setting is only used by some of the userdb drivers. ### `valid_chroot_dirs` **Default:** _\[None\]_ **Value:** [Boolean List](/main/core/settings/types.html#boolean-list) List of directories under which chrooting is allowed for mail processes. Addresses the risk of root exploits enabled by incorrect use of chrooting. Interpretation is recursive, so including `/var/mail` allows chrooting to subdirectories such as `/var/mail/foo/bar`. ### `verbose_proctitle` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, the `ps` command shows more verbose process details, including the username and IP address of the connected client. This aids in seeing who is actually using the server, as well as helps debugging in case there are any problems. See [process titles](/main/core/admin/process_titles.html). ### `verbose_ssl` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **Changes:** - Removed: 2.4.0 If enabled, protocol-level SSL errors are logged. Same as `[log_debug = category=ssl](/main/core/summaries/settings.html#log_debug)`. ### `version_ignore` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) If enabled, ignore version mismatches between different Dovecot versions. ### `welcome_script` **Value:** [Named Filter](/main/core/settings/types.html#named-filter) **See Also:** - [welcome](/main/core/plugins/welcome.html) **Plugin:** [welcome plugin](/main/core/plugins/welcome.html) Named filter for configuring the welcome `[execute](/main/core/summaries/settings.html#execute)` script. ``` welcome { execute welcome { args = %{user} } ``` ### `welcome_wait` **Default:** `no` **Value:** [boolean](/main/core/settings/types.html#boolean) **See Also:** - [welcome](/main/core/plugins/welcome.html) **Plugin:** [welcome plugin](/main/core/plugins/welcome.html) If enabled, wait for the script to finish. By default, the welcome script is run asynchronously. ## Advanced Settings DANGER These settings should not normally be changed. ### `auth_master_socket_path` | Key 1 | | --- | | Default | `auth-master` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The UNIX socket path to the master authentication server for finishing user logins. It is usually neither necessary nor advisable to change the default. ### `auth_policy_hash_truncate` | Key 1 | | --- | | Default | `12` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[auth_policy_server_url](/main/core/summaries/settings.html#auth_policy_server_url)` | | Advanced Setting; this should not normally be changed. | How many bits to use from password hash when reporting to policy server. ### `auth_proxy_self` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | If the destination for proxying matches any of the IP addresses listed here, proxying is not performed when `proxy_maybe=yes` is returned. This parameter isn't normally needed; its main use is if the destination IP address belongs to, for instance, a load-balancer rather than the server itself. ### `auth_socket_path` | Key 1 | | --- | | Default | `auth-userdb` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The UNIX socket path to the master authentication server for finding users. It is usually neither necessary nor advisable to change the default. ### `auth_username_translation` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | If set, performs username character translations before querying the auth database. The value is a string formed of sets of `from` and `to` characters alternating. A value of `#@/@` means that `#` and `/` will both be translated to the `@` character. ### `base_dir` | Key 1 | | --- | | Default | `/var/run/dovecot/` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The base directory in which Dovecot should store runtime data. This can be used to override the base directory determined at compile time. ### `dns_client_socket_path` | Key 1 | | --- | | Default | `dns-client` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | UNIX socket path to the dns-client service. ### `dsync_hashed_headers` | Key 1 | | --- | | Default | `Date Message-ID` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Which email headers are used in incremental syncing for checking whether the local email matches the remote email? Format: a space-separated list of headers. This list should only include headers that can be efficiently downloaded from the remote server. ### `fs_posix_accurate_mtime` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | Configure whether `utimes()` is called after writes to guarantee microsecond precision timestamps for files. By default Linux updates the `mtime` only on timer interrupts, which is not remotely close to microsecond precision. ### `fs_posix_autodelete_empty_directories` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | If the last file in a directory is deleted, should the parent directory be automatically deleted? INFO Using this setting makes the POSIX filesystem behave more like an object storage would. WARNING This setting can cause the POSIX filesystem to also delete the parent directory hierarchy farther up than anticipated. ### `fs_posix_lock_method` | Key 1 | | --- | | Default | `flock` | | Value | [string](/main/core/settings/types.html#string) | | Allowed Values | `flock``dotlock` | | Advanced Setting; this should not normally be changed. | Lock method to use for locking files. Currently nothing uses `lib-fs` locking. ### `fts_flatcurve_commit_limit` | Key 1 | | --- | | Default | `500` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | Commit database changes after this many documents are updated. Higher commit limits will result in faster indexing for large transactions (i.e. indexing a large mailbox) at the expense of high memory usage. The default value should be sufficient to allow indexing in a 256 MB maximum size process. Set to `0` to use the Xapian default. ### `fts_flatcurve_min_term_size` | Key 1 | | --- | | Default | `2` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | The minimum number of characters in a term to index. ### `fts_flatcurve_optimize_density_percentage` | Key 1 | | --- | | Default | `25` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)` | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | When an index shard's message count falls below this percentage of `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)`, the mailbox is scheduled for deferred optimization. This helps prevent accumulation of sparse shards that degrade mailbox open performance. Set to `0` to disable density-based optimization. This optimization requires `[fts_flatcurve_rotate_count](/main/core/summaries/settings.html#fts_flatcurve_rotate_count)` to be enabled. ### `fts_flatcurve_optimize_limit` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | Once the database reaches this number of shards, automatically optimize the DB at shutdown. Set to `0` to disable auto-optimization. ### `fts_flatcurve_rotate_count` | Key 1 | | --- | | Default | `5000` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | When the "current" fts database reaches this number of messages, it is rotated to a read-only database and replaced by a new write DB. Most people should not change this setting. Set to `0` to disable rotation. ### `fts_flatcurve_rotate_time` | Key 1 | | --- | | Default | `5s` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Plugin | [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) | | Advanced Setting; this should not normally be changed. | When the "current" fts database exceeds this length of time (in msecs) to commit changes, it is rotated to a read-only database and replaced by a new write DB. Most people should not change this setting. Set to `0` to disable rotation. ### `http_client_auto_redirect` | Key 1 | | --- | | Default | `yes` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - `[http_client_request_max_redirects](/main/core/summaries/settings.html#http_client_request_max_redirects)` | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | If this setting is `yes` redirects are handled as long as `[http_client_request_max_redirects](/main/core/summaries/settings.html#http_client_request_max_redirects)` isn't reached. If `no` the redirect responses are handled as regular failure responses. WARNING This setting should likely be changed only in the code, never in configuration. ### `http_client_auto_retry` | Key 1 | | --- | | Default | `yes` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | If this setting is `no` requests are not automatically retried by the generic HTTP client code. It's still possible to retry the requests with explicit `http_client_request_try_retry()` calls as long as `[http_client_request_max_attempts](/main/core/summaries/settings.html#http_client_request_max_attempts)` isn't reached. WARNING This setting should likely be changed only in the code, never in configuration. ### `http_client_connect_backoff_max_time` | Key 1 | | --- | | Default | `1 min` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Maximum backoff time for retries. ### `http_client_connect_backoff_time` | Key 1 | | --- | | Default | `100 ms` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Initial backoff time for retries. It's doubled at each connection failure. ### `http_client_dns_ttl` | Key 1 | | --- | | Default | `30 mins` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | How long to cache DNS entries. ### `http_client_max_auto_retry_delay` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [time](/main/core/settings/types.html#time) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Maximum acceptable delay for automatically retrying/redirecting requests. If a server sends a response with a `Retry-After` header that causes a delay longer than this, the request is not automatically retried and the response is returned. ### `http_client_response_hdr_max_field_size` | Key 1 | | --- | | Default | `8k` | | Value | [size](/main/core/settings/types.html#size) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Response header limit: Max size for an individual field. ### `http_client_response_hdr_max_fields` | Key 1 | | --- | | Default | `50` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Response header limit: Max number of fields. ### `http_client_response_hdr_max_size` | Key 1 | | --- | | Default | `200k` | | Value | [size](/main/core/settings/types.html#size) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Response header limit: Max size for the entire response header. ### `http_client_socket_recv_buffer_size` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [size](/main/core/settings/types.html#size) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | The kernel receive buffer size for the connection sockets. `0` = kernel defaults. ### `http_client_socket_send_buffer_size` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [size](/main/core/settings/types.html#size) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | The kernel send buffer size for the connection sockets. `0` = kernel defaults. ### `http_client_soft_connect_timeout` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Time to wait for TCP connect and SSL handshake to finish for the first connection before trying the next IP in parallel. `0` = wait until current connection attempt finishes. ### `http_client_user_agent` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | `User-Agent:` header to send. ### `http_server_default_host` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Overwrite the local hostname with http\_server\_default\_host. ### `http_server_max_payload_size` | Key 1 | | --- | | Default | `10G` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Request payload limit: Max size for the request payload. ### `http_server_max_target_length` | Key 1 | | --- | | Default | `8k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Request target limit: Maximum length of the request target. ### `http_server_request_hdr_max_field_size` | Key 1 | | --- | | Default | `8k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Request header limit: Max size for an individual field. ### `http_server_request_hdr_max_fields` | Key 1 | | --- | | Default | `50` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Request header limit: Max number of fields. ### `http_server_request_hdr_max_size` | Key 1 | | --- | | Default | `200k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Request header limit: Max size for the entire request header. ### `http_server_socket_recv_buffer_size` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The kernel receive buffer size for the connection sockets. `0` = kernel defaults. ### `http_server_socket_send_buffer_size` | Key 1 | | --- | | Default | `0` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The kernel send buffer size for the connection sockets. `0` = kernel defaults. ### `imap_compress_on_proxy` | Key 1 | | --- | | Default | `yes` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` | | Changes | - Added: 2.4.2 - Changed: 2.4.5 Changed from `no` to `yes`. | | Advanced Setting; this should not normally be changed. | If enabled, the IMAP COMPRESS ([RFC 4978](https://datatracker.ietf.org/doc/html/rfc4978)) extension is handled by the Dovecot proxy instead of the backend's imap process when the client connection comes through a Dovecot proxy. This moves the compression work from the backend to the proxy. This setting must be set on the backend, not on the proxy. For this to work, `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` on the backend must be set to point to the proxy. ### `imap_idle_notify_interval` | Key 1 | | --- | | Default | `2mins` | | Value | [time](/main/core/settings/types.html#time) | | Advanced Setting; this should not normally be changed. | The amount of time to wait between "OK Still here" untagged IMAP responses when the client is in IDLE operation. ### `imap_max_line_length` | Key 1 | | --- | | Default | `64k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Maximum IMAP command line length. Some clients generate very long command lines with huge mailboxes, so you may need to raise this if you get Too long argument or IMAP command line too large errors often. ### `language_tokenizer_kuromoji_split_compounds` | Key 1 | | --- | | Default | `yes` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - [FTS Tokenization](/main/core/plugins/fts.html#tokenization) | | Plugin | [fts plugin](/main/core/plugins/fts.html) | | Advanced Setting; this should not normally be changed. | This setting enables `search mode` in the Atilika Kuromoji library. The setting defaults to enabled and should not be changed unless there is a compelling reason. WARNING If this setting is changed, existing FTS indexes will produce unexpected results. The FTS indexes should be recreated in this case. ### `libexec_dir` | Key 1 | | --- | | Default | `/usr/libexec/dovecot` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The directory from which you execute commands via doveadm-exec. ### `lmtp_verbose_replies` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | This setting makes the replies returned to the client much more verbose. Currently, this only applies when the LMTP proxy is involved, for which e.g. backend connection errors are returned in full detail. Normally, these errors are replaced by a more generic error message to prevent leaking system details to the clients (e.g. IP addresses and ports). It is therefore not recommended to enable this setting beyond troubleshooting efforts. ### `login_greeting` | Key 1 | | --- | | Default | `Dovecot ready.` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The greeting message displayed to clients. Variables allowed: - LMTP: `[Mail service user variables](/main/core/settings/variables.html#mail-service-user-variables)` - Other Protocols: `[Login variables](/main/core/settings/variables.html#login-variables)` ### `login_proxy_notify_path` | Key 1 | | --- | | Default | `proxy-notify` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Path to proxy-notify pipe. [Login Variables](/main/core/settings/variables.html#login-variables) can be used. ### `login_socket_path` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Default socket path for all services' login processes. Can be overridden by passing a parameter to the login executable. ### `mail_cache_max_header_name_length` | Key 1 | | --- | | Default | `100` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Maximum header name length stored in the cache, where 0 stands for unlimited (which is also the former behavior). When enabled, the cache truncates the names to this length in memory and on file. While the header name remains unchanged in the storage, all the headers sharing the first `[mail_cache_max_header_name_length](/main/core/summaries/settings.html#mail_cache_max_header_name_length)` prefix characters are de facto aliased and will be considered as the same header on cache fetch. Also, attempting to fetch a specific aliased header will succeed even if the header does not actually exist (this does NOT happen when the feature is disabled with explicitly with `[mail_cache_max_header_name_length = unlimited](/main/core/summaries/settings.html#mail_cache_max_header_name_length)`) Example: (`[mail_cache_max_header_name_length = 5](/main/core/summaries/settings.html#mail_cache_max_header_name_length)`) If the mail contains the header `X-name: value`, attempting to fetch `X-nam` or `X-names` will also produce `X-name: value` as a result (with the original header name, not the requested one). Trying to fetch the mail text or the mail headers will properly return only `X-name: value`. ### `mail_cache_max_headers_count` | Key 1 | | --- | | Default | `100` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Maximum number of headers in `yes`/`temp` cache decision before the cache refuses to promote more header decisions from `no` to `temp`, where `0` stands for unlimited (which is also the former behavior). When entries are rejected, the event `[mail_cache_decision_rejected](/main/core/summaries/events.html#mail_cache_decision_rejected)` is emitted. Also, while the cache's headers count is saturated, the effective value of `[mail_cache_unaccessed_field_drop](/main/core/summaries/settings.html#mail_cache_unaccessed_field_drop)` is reduced to 1/4 of of the specified one, in order to aid the cache to return within the limits. ### `mail_cache_max_size` | Key 1 | | --- | | Default | `1G` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | If `dovecot.index.cache` becomes larger than this, it's truncated to empty size. WARNING The maximum value is 1 GB because the cache file format can't currently support larger sizes. ### `mail_cache_min_mail_count` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Only update cache file when the mailbox contains at least this many messages. With a setting other than `0`, you can optimize behavior for fewer disk writes at the cost of more disk reads. ### `mail_cache_purge_continued_percentage` | Key 1 | | --- | | Default | `200` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Compress the cache file when n% of records are deleted (by count, not by size). For example `200` means that the record has 2 continued rows, i.e. it exists in 3 separate segments in the cache file. ### `mail_cache_purge_delete_percentage` | Key 1 | | --- | | Default | `20` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Compress the cache file when n% of records are deleted (by count, not by size). ### `mail_cache_purge_header_continue_count` | Key 1 | | --- | | Default | `4` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | Compress the cache file when we need to follow more than n next\_offsets to find the latest cache header. ### `mail_cache_purge_min_size` | Key 1 | | --- | | Default | `32k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Only compress cache file if it is larger than this size. ### `mail_cache_record_max_size` | Key 1 | | --- | | Default | `64k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | If a cache record becomes larger than this, don't add it to the cache file. ### `mail_cache_unaccessed_field_drop` | Key 1 | | --- | | Default | `30days` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [mail cache configuration](/main/core/config/mail_cache.html) | | Advanced Setting; this should not normally be changed. | Specifies when cache decisions are downgraded. Change caching decision from YES to TEMP after this much time has passed. Drop the field entirely after twice this much time has passed, regardless of whether the cache decision was YES or TEMP previously. If the cache header count is capped to `[mail_cache_max_headers_count](/main/core/summaries/settings.html#mail_cache_max_headers_count)` then the effective value is reduced to 1/4 of the configured value until enough headers expire for the cache to fall back inside the limits. ### `mail_ext_attachment_hash` | Key 1 | | --- | | Default | `%{sha1}` | | Value | [string](/main/core/settings/types.html#string) | | Allowed Values | `%{md4}``%{md5}``%{sha1}``%{sha256}``%{sha512}``%{size}` | | See Also | - `[mail_ext_attachment_path](/main/core/summaries/settings.html#mail_ext_attachment_path)` | | Advanced Setting; this should not normally be changed. | The hash format to use in attachment filenames when saving attachments externally. Variables and additional text can be included in this string. The syntax allows truncation of any variable. For example `%{sha256:80}` will return only the first 80 bits of the SHA256 output. ### `mail_full_filesystem_access` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | See Also | - `[mailbox_list_validate_fs_names](/main/core/summaries/settings.html#mailbox_list_validate_fs_names)` | | Advanced Setting; this should not normally be changed. | Allow full filesystem access to clients? If enabled, no access checks are performed other than what the operating system does for the active UID/GID. This also disables the `[mailbox_list_validate_fs_names](/main/core/summaries/settings.html#mailbox_list_validate_fs_names)` setting. This setting works with both [Maildir](/main/core/config/mailbox_formats/maildir.html) and [mbox](/main/core/config/mailbox_formats/mbox.html), allowing you to prefix mailbox names with `/path/` or `~user/` indicators. ### `mail_index_log2_max_age` | Key 1 | | --- | | Default | `2days` | | Value | [time](/main/core/settings/types.html#time) | | Advanced Setting; this should not normally be changed. | Delete `.log.2` index file when older than this value. Older `.log.2` files are useful for QRESYNC and dsync, so this value should not be too low. ### `mail_index_log_rotate_max_size` | Key 1 | | --- | | Default | `1M` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - `[mail_index_log_rotate_min_age](/main/core/summaries/settings.html#mail_index_log_rotate_min_age)` - `[mail_index_log_rotate_min_size](/main/core/summaries/settings.html#mail_index_log_rotate_min_size)` | | Advanced Setting; this should not normally be changed. | Always rotate transaction log after it exceeds this size. ### `mail_index_log_rotate_min_age` | Key 1 | | --- | | Default | `5mins` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - `[mail_index_log_rotate_max_size](/main/core/summaries/settings.html#mail_index_log_rotate_max_size)` | | Advanced Setting; this should not normally be changed. | Rotate transaction log if it is older than this value and is larger than `[mail_index_log_rotate_min_size](/main/core/summaries/settings.html#mail_index_log_rotate_min_size)`. ### `mail_index_log_rotate_min_size` | Key 1 | | --- | | Default | `32k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | Rotate transaction log if it is larger than this size and is older than `[mail_index_log_rotate_min_age](/main/core/summaries/settings.html#mail_index_log_rotate_min_age)`. ### `mail_index_rewrite_max_log_bytes` | Key 1 | | --- | | Default | `128k` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - `[mail_index_rewrite_min_log_bytes](/main/core/summaries/settings.html#mail_index_rewrite_min_log_bytes)` | | Advanced Setting; this should not normally be changed. | Rewrite the index when the number of bytes that needs to be read from the .log index file on refresh is between these min/max values. ### `mail_index_rewrite_min_log_bytes` | Key 1 | | --- | | Default | `8k` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - `[mail_index_rewrite_max_log_bytes](/main/core/summaries/settings.html#mail_index_rewrite_max_log_bytes)` | | Advanced Setting; this should not normally be changed. | Rewrite the index when the number of bytes that needs to be read from the .log index file on refresh is between these min/max values. ### `mail_max_keyword_length` | Key 1 | | --- | | Default | `50` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | The maximum length allowed for a mail keyword name. Compliance is enforced only during attempts to create new keywords. ### `mail_save_crlf` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Advanced Setting; this should not normally be changed. | Save message with CR+LF line endings? Messages are normally saved with LF line endings. Enabling this makes saving messages less CPU-intensive, especially with the sendfile() system call used in Linux and FreeBSD. However, enabling comes at the cost of slightly increased disk I/O, which could decrease the speed in some deployments. ### `mailbox_idle_check_interval` | Key 1 | | --- | | Default | `30secs` | | Value | [time](/main/core/settings/types.html#time) | | Advanced Setting; this should not normally be changed. | The minimum time between checks for new mail/other changes when a mailbox is in the IMAP IDLE state. ### `mailbox_subscriptions_filename` | Key 1 | | --- | | Default | `subscriptions` specific [Mailbox Formats](/main/core/config/mailbox_formats/overview.html#mailbox-formats) have different defaults | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Specifies the filename used for storing mailbox subscriptions. ### `managesieve_client_workarounds` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | Enables various workarounds for ManageSieve clients. Currently there are none. ### `managesieve_implementation_string` | Key 1 | | --- | | Default | `Dovecot Pigeonhole` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | Sets the ManageSieve implementation string returned by the `IMPLEMENTATION` capability. ### `managesieve_max_compile_errors` | Key 1 | | --- | | Default | `5` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Advanced Setting; this should not normally be changed. | The maximum number of compile errors that are returned to the client upon script upload or script verification. ### `managesieve_max_line_length` | Key 1 | | --- | | Default | `64k` | | Value | [size](/main/core/settings/types.html#size) | | Advanced Setting; this should not normally be changed. | The maximum ManageSieve command line length in bytes. Since long command lines are very unlikely with ManageSieve, changing this will generally not be useful. ### `managesieve_notify_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `NOTIFY` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). ### `managesieve_sieve_capability` | Key 1 | | --- | | Default | `` | | Value | [Boolean List](/main/core/settings/types.html#boolean-list) | | Advanced Setting; this should not normally be changed. | `SIEVE` capabilities reported by the ManageSieve service before authentication. This does normally not need to be configured. If left unassigned, these will be assigned dynamically according to what the Sieve interpreter is configured to support using the global `[sieve_extensions](/main/core/summaries/settings.html#sieve_extensions)` setting (after login this may differ depending on the settings applicable to the authenticated user). ### `mbox_md5` | Key 1 | | --- | | Default | `apop3d` | | Value | [string](/main/core/settings/types.html#string) | | Allowed Values | `apop3d``all` | | See Also | - `[pop3_uidl_format](/main/core/summaries/settings.html#pop3_uidl_format)` - [mbox](/main/core/config/mailbox_formats/mbox.html) | | Advanced Setting; this should not normally be changed. | The mail-header selection algorithm to use for MD5 POP3 UIDLs when the setting `[pop3_uidl_format = %{md5}](/main/core/summaries/settings.html#pop3_uidl_format)` is applied. ### `mbox_min_index_size` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [size](/main/core/settings/types.html#size) | | See Also | - [mbox](/main/core/config/mailbox_formats/mbox.html) | | Advanced Setting; this should not normally be changed. | For mboxes smaller than this size, index files are not written. If an index file already exists, it gets read but not updated. The default should not be changed for most installations. ### `quota_clone_unset` | Key 1 | | --- | | Default | `no` | | Value | [boolean](/main/core/settings/types.html#boolean) | | Plugin | [quota-clone plugin](/main/core/plugins/quota_clone.html) | | Changes | - Added: 2.4.0 | | Advanced Setting; this should not normally be changed. | Unset quota information before updating. This is needed with some dict drivers that do not support upserting, such as SQL with older SQLite. ### `service_privileged_group` | Key 1 | | --- | | Default | _\[None\]_ | | Value | [string](/main/core/settings/types.html#string) | | See Also | - `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` | | Advanced Setting; this should not normally be changed. | Secondary UNIX group - which is disabled by default - but can be enabled by the process. `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)` setting is a more user friendly way to use this setting for mail processes. ### `sieve_duplicate_default_period` | Key 1 | | --- | | Default | `14d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | Default period after which tracked values are purged from the duplicate tracking database. ### `sieve_duplicate_max_period` | Key 1 | | --- | | Default | `7d` | | Value | [time](/main/core/settings/types.html#time) | | See Also | - [Sieve duplicate extension](/main/core/config/sieve/extensions/duplicate.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | Maximum period after which tracked values are purged from the duplicate tracking database. ### `sieve_include_max_includes` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | The maximum number of scripts that may be included. This is the total number of scripts involved in the include tree. ### `sieve_include_max_nesting_depth` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve include extension](/main/core/config/sieve/extensions/include.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | The maximum nesting depth for the include tree. ### `sieve_max_script_size` | Key 1 | | --- | | Default | `1M` | | Value | [size](/main/core/settings/types.html#size) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | The maximum size of a Sieve script. The compiler will refuse to compile any script larger than this limit. If set to `0`, no limit on the script size is enforced. ### `sieve_notify_mailto_max_headers` | Key 1 | | --- | | Default | `16` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_recipients](/main/core/summaries/settings.html#sieve_notify_mailto_max_recipients)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Changes | - Added: 2.4.5 Setting to limit the number of additional headers per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of additional headers a single `mailto:` notify action may specify in its URI. `0` means no limit on the number of headers is enforced. ### `sieve_notify_mailto_max_recipients` | Key 1 | | --- | | Default | `8` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_notify_mailto_max_headers](/main/core/summaries/settings.html#sieve_notify_mailto_max_headers)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Changes | - Added: 2.4.5 Setting to limit the number of recipients per mailto notification is added. | | Advanced Setting; this should not normally be changed. | The maximum number of recipients a single `mailto:` notify action may specify, counted across the URI and any `to`/`cc`/`bcc` headers. `0` means no limit on the number of recipients is enforced. ### `sieve_notify_max_notifications` | Key 1 | | --- | | Default | `10` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` - `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)` - [Sieve notifications extensions](/main/core/config/sieve/extensions/enotify.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Changes | - Added: 2.4.5 Setting to limit maximum number of notifications per script is added. | | Advanced Setting; this should not normally be changed. | The maximum number of `notify` actions ([RFC 5435](https://datatracker.ietf.org/doc/html/rfc5435)) that can be performed during a single script execution. `0` means `notify` is prohibited; scripts using the `notify` command will fail to compile with "local policy prohibits the use of a notify action". Note that this is independent of `[sieve_max_redirects](/main/core/summaries/settings.html#sieve_max_redirects)`; the `redirect` action and the `notify` mailto method are governed by separate limits. The effective per-script limit on `notify` actions is `min(sieve_notify_max_notifications, sieve_max_actions)`: when the total number of actions in a script reaches `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` (default `32`), execution aborts with "total number of actions exceeds policy limit" before this per-class limit is reached. To allow more than `[sieve_max_actions](/main/core/summaries/settings.html#sieve_max_actions)` notify actions, raise that setting as well. ### `sieve_variables_max_scope_count` | Key 1 | | --- | | Default | `255` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | The maximum number of variables that can be declared in a scope. There are currently two variable scopes: the normal script scope and the global scope created by the [Sieve include extension](/main/core/config/sieve/extensions/include.html). The minimum value for this setting is `128`. ### `sieve_variables_max_value_size` | Key 1 | | --- | | Default | `4k` | | Value | [size](/main/core/settings/types.html#size) | | See Also | - [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) | | Plugin | [sieve plugin](/main/core/plugins/sieve.html) | | Advanced Setting; this should not normally be changed. | The maximum allowed size for the value of a variable. If exceeded at runtime, the value is always truncated to the configured maximum. The minimum value for this setting is `4000 bytes`. ### `sqlite_busy_timeout` | Key 1 | | --- | | Default | `1s` | | Value | [time (milliseconds)](/main/core/settings/types.html#millisecond-time) | | Changes | - Added: 2.4.5 | | Advanced Setting; this should not normally be changed. | Time to wait for a locked sqlite database to become available before giving up with an `SQLITE_BUSY` error. Even in write-ahead logging mode (see `[sqlite_journal_mode](/main/core/summaries/settings.html#sqlite_journal_mode)`) only one writer runs at a time, so concurrent writers may need to wait. Increase this on slow storage where writes contend. ### `state_dir` | Key 1 | | --- | | Default | `/var/lib/dovecot` | | Value | [string](/main/core/settings/types.html#string) | | Advanced Setting; this should not normally be changed. | The compile-time directory PKG\_STATEDIR (typically /var/lib/dovecot) is hard-coded as the location of state files. The PKG\_STATEDIR value is taken as the default state\_dir setting but can be overridden - for instance, if you wish to use the same binaries for a system daemon and a user daemon. The settings `[state_dir = /home/foo/dovecot/state](/main/core/summaries/settings.html#state_dir)` and `[base_dir = /home/foo/dovecot/run](/main/core/summaries/settings.html#base_dir)` give an example of usage. ### `virtual_max_open_mailboxes` | Key 1 | | --- | | Default | `64` | | Value | [unsigned integer](/main/core/settings/types.html#unsigned-integer) | | Plugin | [virtual plugin](/main/core/plugins/virtual.html) | | Advanced Setting; this should not normally be changed. | How many mailboxes to open in virtual plugin. # Raw Data Files This page provides download links to the raw data files used to generate the Dovecot documentation. The format and content of each data file is available by looking at the data file source in GitHub (links for each file below). ## Data File List | File | Source | | --- | --- | | `[doveadm.json](/main/datafiles/doveadm.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/doveadm.js) | | `[event_categories.json](/main/datafiles/event_categories.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/event_categories.js) | | `[event_reasons.json](/main/datafiles/event_reasons.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/event_reasons.js) | | `[events.json](/main/datafiles/events.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/events.js) | | `[lua.json](/main/datafiles/lua.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/lua.js) | | `[settings.json](/main/datafiles/settings.json)` | [Source](https://github.com/dovecot/documentation/blob/main/data/settings.js) | ## LLM Resources This documentation provides Large Language Model (LLM) friendly formats following the [/llms.txt standard](https://llmstxt.org/). These resources provide structured, plain-text Markdown designed for ingestion by AI assistants, coding tools, and LLMs during inference time. ### Site-Wide LLM Files - **[`llms.txt`](/main/llms.txt)** A curated index of all documentation pages, structured with summary information and direct links to clean Markdown versions of each page. - **[`llms-full.txt`](/main/llms-full.txt)** A single aggregated text document containing the full concatenated content of all documentation pages, optimized for loading the entire site into an LLM context window at once. ### Page-Specific Markdown Every page on this site has a corresponding clean Markdown version (`.md`). # Coding Style Guide Dovecot coding style is similar to what Linux Kernel developers use. Here is a short list of important things to note. Note that all of these rules are not strict and common sense can be used. If unsure, ask your colleagues. This document is subject to alterations and can be edited when found wrong. Simplicity provides security. The more you have to remember to maintain security the easier it is to forget something. ## Design Principles ### Deinitialize Safely Whenever you free a pointer, set it to NULL. That way if you accidentally try to free it again, it's less likely to cause a security hole. Dovecot does this automatically with most of its free() calls, but you should also make it a habit of making all your \_destroy() functions take a pointer-to-pointer parameter which you set to NULL. ### Don't Keep Secrets We don't do anything special to protect ourself against read access buffer overflows, so don't store anything sensitive in memory. We use multiple processes to protect sensitive information between users. When dealing with passwords and such, erase them from memory after you don't need it anymore. Note that such memset() may be optimized away by compiler, use safe\_memset(). ### Use GCC Extensions GCC makes it easy to catch some potential errors: Format string vulnerabilities can be prevented by marking all functions using format strings with `__attr_format__()` and `__attr_format_arg__()` macros and using `-Wformat=2` GCC option. `-W` option checks that you don't compare signed and unsigned variables. Hopefully GCC will later emit a warning whenever there's potential integer truncation. `-Wconversion` kind of does that, but it's not really meant for it and it gives too many other useless warnings. ### Use Multiple Layers of Security Input validation is useful to prevent clients from taking too much server resources. Add the restrictions only where it's useful. For example a simple "maximum line length" will limit the length of pretty much all possible client input. Don't rely on input validation. Maybe you missed something. Maybe someone calls your function somewhere else where you didn't originally intend it. Maybe someone makes the input validation less restrictive for some reason. Point is, it's not an excuse to cause a security hole just because input wasn't what you expected it to be. Don't trust memory. If code somewhere overflowed a buffer, don't make it easier to exploit it. For example if you have code: c ``` static char staticbuf[100]; .. char stackbuf[100]; strcpy(stackbuf, staticbuf); ``` Just because `staticbuf` was declared as `[100]`, it doesn't mean it couldn't contain more data. Overflowing static buffers can't be directly exploited, but the `strcpy()` overflowing `stackbuf` makes it possible. Always copy data with bounds checking. ## Commits In Dovecot code, commits should only contain changes the commit message refers to. A commit message title should be written in imperative form, so instead of "lib-ssl-iostream: Added parameter", write "lib-ssl-iostream: Add parameter". The commit message itself should adequately describe the commit if it's not obvious from commit title. Extra information can be provided there. When fixing regressions, it is important to refer to the original commit causing the regression. There must be no unrelated changes in commits, including unrelated whitespace changes. Those need to be done in separate commits. A good rule of thumb: if your commit message says "Do this and that", you probably should split it into two. ## Code Style ### Indentation, Line Length, and Wrapping - Maximum line length is 80 characters. This is a soft limit, and can be sometimes shortly exceeded if it makes sense. - Indentation is done with hard tabs. The last indentation can be filled with spaces, when necessary, such as if conditional continuations. - If function definition grows too large, you can move the function from name forwards to next line, or you can wrap the variables. Whichever is easier. - Line wrapping must be done to match the indentation of the previous line. Example: c ``` for (i = 0, used = 0; i < *gid_count; i++) { if (gid_list[i] >= set->first_valid_gid && (set->last_valid_gid == 0 || gid_list[i] <= set->last_valid_gid)) { if (gid_list[i] == 0) *have_root_group = TRUE; gid_list[used++] = gid_list[i]; } } ``` ### When to Use Spaces Before and After - Space should be added before initial brace in for, while, if etc. (see above example). - Spaces are wanted before and after conditionals. - Spaces should be added when AND'ing (&&) and OR'ing (||) values. - Spaces should be added when doing arithmetics. ### Bracing - Braces are kept on same line, and not moved onto line on it's own. - Unnecessary braces can be left out, but avoid removing them to create extra diff. ## Variables, Structures, and Members ### Naming - Variables must be named in English. - The name should reflect the content and purpose of the variable and be readable. - For local looping and counting, one-letter variables like i,j,k,l,m can be used. - For local purposes, it is ok to use str, ret, etc. - Avoid using "tmp", "foo" etc. as variable name. They do not convey any information what that variable contains. - Dovecot does not use CamelCasing in variable names. - Excessively long variable names should be avoided because of the line length restriction. ### Types - Unsigned types are used whenever the value isn't allowed to be negative. This makes it easier to do "value too large" checks when you don't also have to check for negative values. Also in arithmetic it's better to have the value wrap (and hopefully checked later!) than cause undefined behavior with a signed integer overflow. - Use char \* only for NUL-terminated strings. Use unsigned char \* if it's not guaranteed to be NUL-terminated. - size\_t should generally be used when pointing to a (large) memory area, especially for mmap(). Since size\_t can be slower to access than unsigned int (or at least use memory), it's fine to use unsigned int when it's "very unlikely" that the size ever goes beyond 4 GB (e.g. string\_t). - uoff\_t is used for file offsets/sizes. This is usually 64bit, even with 32bit machines. - uint32\_t vs. unsigned int: Use uint32\_t when the type really should be 32bit, but don't spend too much energy trying to avoid mixing it with unsigned int, since they are going to be the same types probably for the rest of Dovecot's life. - uint8\_t vs. unsigned char: I doubt Dovecot will ever be compiled anywhere where these differ from one another, but for readability use uint8\_t for binary data and unsigned char for text data. - Use const whenever you can. - For reference counters, use signed integers. This is to make sure that the reference count can become negative to catch bugs. This also means that when doing comparisons of reference counters, use > 0 or <= 0 . Avoid using == 0. ### Type Safety - Try to avoid using void pointers. - Try to avoid casting types to other types, especially if the cast isn't necessary to avoid a compiler warning. - It's better if compiler can give a warning when something is accidentally used wrong. - Dovecot has some helper macros to make callbacks' context parameters type-safe. See `CALLBACK_TYPECHECK()` macro and for example `io_add()` for example usage. - Use `container_of()` macro to access nested structures. - Use `CONST_PTR_OFFSET`, `PTR_OFFSET` when applicable. ## Boolean Expressions Try to use boolean expressions the way they work in Java. C doesn't require this, but I think it makes the code easier to understand and reduces bugs in some cases (e.g. if (!foo()) when thinking foo() returns bool/FALSE, but actually returns int/-1 on error). We've a clang patch to give warnings in these cases. As expected, it found quite a lot of bugs (some real bugs and a lot of "it just accidentally worked"). - bool x and bool x:1 are the boolean types - `TRUE` and `FALSE` are the only valid explicit boolean values (not 0 or 1, and currently also not true/false although that could be changed) - !=, ==, <, >, etc. comparisons create a boolean - if, for, while, etc. require a boolean ### Structures and unions - When adding members to structures, ordering and bit length should be used when possible to reduce memory footprint - Since structures are usually padded when there are gaps, it is good idea to make sure they pack nicely. Especially when doing structures that are used heavily. - Put pointers next to each other when possible. - Same goes with integers - When using booleans, try to place them together and use bit lengths c ``` struct foobar { const char *abc; struct foobar2 *ptr; unsigned int a; unsigned int b; char a; char b; bool whatnot:1; bool notwhat:1; }; ``` - Structure inheritance in Dovecot code is done by putting the parent structure as **first** member of the structure. This is important so that the containing structure can be accessed properly. - Use container\_of() macro to access these - When using pooled memory, create the pool first, then allocate the structure using p\_new and put the pool as struct member. ### Use Unions Safely Suppose there was code: c ``` union { unsigned int number; char *str; } u; ``` If it was possible for user to set number arbitrarily, but access the union as string it'd be possible to read or write arbitrary memory locations. There's two ways to handle this. First would be to avoid union entirely and use a struct instead. You don't really need the extra few bytes of memory that union saves. Another way is to access the union only through macro that verifies that you're accessing it correctly. See `IMAP_ARG_*()` macros in lib-imap/imap-parser.h. ## Functions ### Naming - Function names must be in English. - The name should reflect the scope and purpose of the function and be readable. - The name can contain \_real, \_continue, \_more, \_start, etc. suffixes to group functions performing related task split over multiple functions. - Functions that are public should include scoping prefix. This means that instead of writing get\_value , one should use mail\_user\_get\_value . - Private functions can omit scoping prefix. ### Function parameters - Parameter naming follows variable naming, see Naming. - When you have parameters that used only to return values from the function, they should have \_r suffix. If the parameter is updated (read and written), then it does not need the suffix. - In initialization functions, it is good idea to use struct foobar\_settings to pass multiple values. This makes it easier to extend the initialization later on. ### Return Values - When function cannot fail, it is OK to return the value directly. - When function has error handling, the function should avoid returning NULL, false, 0 etc. Instead the function should return negative number on error, zero on success, or boolean, and use \_r suffixed function parameter for returning values. c ``` bool doveadm_log_type_from_char(char c, enum log_type *type_r); ``` - Here the type\_r contains the type, and bool signals whether the conversion succeeded or not. - Do not return error strings, use const char `**error_r` for that. - Use `ATTR_WARN_RESULT` when it is critical that the error is checked. ### Callback Functions Callback functions make the code more difficult to follow, especially when a callback calls another callback, or when using function pointers to jump to different callbacks depending on state. Of course with asynchronous C code it's pretty much impossible to avoid callbacks. Still, try to avoid them where possible to keep the code readable. lib-fs/fs-api.h is an example API which supports async operations but with a single common "do more now" callback rather than every single operation having its own callback parameter. This makes it similar to async network IO with read()/write() `EAGAIN` handling. Often callback functions can be avoided by creating iterator functions instead. For example instead of c ``` parse(callback, context); ``` use: c ``` ctx = parse_init(); while (parse_next(ctx)) { .. } parse_deinit(&ctx); ``` ## Memory Memory is always allocated through one of Dovecot's wrappers, e.g. i\_malloc() or i\_new(). All of Dovecot's memory allocations always succeed or kill the process. There's no point in writing a lot of code to check for memory allocation failures that happen just about never. The only reason some memory allocations fail in Dovecot currently is because a process VSZ limit is reached, which usually indicates either a memory leak or trying to access a mailbox that is too large. In either of these cases it's better to just completely restart the process than try to limp along without getting anything useful done anymore. Memory allocations can be assumed to be zero-initialized. All of the memory allocation functions do it, except t\_malloc() and t\_buffer\_get(), which you should almost never use directly anyway. The code currently also assumes that pointers in zero-initialized memory area are NULL, which isn't guaranteed by ANSI-C, but practically Dovecot isn't going to be run in systems where it's not true and you're not going to remember to NULL-initialize all of your pointers anyway without compiler/runtime failure. When using a struct, always zero-initialize it with `i_zero()` instead of setting each field separately to 0. It's too easy to cause bugs by adding a new field to the struct and forgetting to initialize it. Double-frees in C are bad. Better to crash with NULL pointer dereference than possibly allow an attacker to exploit heap corruption and run executable code. Most of the pointers are set to NULL when they are freed: - `i_free_and_null()` is a macro to free the memory and set the pointer to NULL. - `i_free()` is exactly same as `i_free_and_null()`. - Most `deinit()` functions take a pointer-to-pointer parameter and set the original one to NULL. There's no need to explicitly set the same pointer to NULL afterwards. ## Buffers Use dynamically growing strings/buffers wherever necessary instead of a static sized buffer, where on larger input the function fails or truncates the data. It's of course not good to allow users to infinitely grow memory usage, so there should be some limits added, but it shouldn't fail even if the limit is set to infinite. Avoid explicitly calculating memory usage for allocations. If you do, mark it with `/* @UNSAFE */` comment unless it's the calculation is "so obvious that you see it's correct at the first glance". If in doubt, just mark it UNSAFE. The idea is that anyone can easily grep for these and verify their correctness. Avoid writing to buffers directly. Write everything through buffer API (lib/buffer.h) which guarantees protection against buffer overflows. There are various safe string APIs as well (lib/str.h, lib/strfuncs.h). Dovecot also provides a type safe array API (lib/array.h). If you do write to buffers directly, mark the code with /\* @UNSAFE \*/ unless it's _obviously_ safe. Only obviously safe code is calling a function with (buffer, sizeof(buffer)) parameters. If you do _any_ calculations with buffer size, mark it unsafe. Use const with buffers whenever you can. It guarantees that you can't accidentally modify it. ## Avoid free() Accessing freed memory is the most difficult problem to solve with C code. Only real solution is to use garbage collector, but it's not possible to write a portable GC without radical changes in how you write code. There are a few ways to avoid most free() calls however: data stack and memory pools. Data stack works in somewhat similar way to C's control stack. alloca() is quite near to what it does, but there's one major difference: Stack frames are explicitly defined, so functions can return values allocated from data stack. t\_strdup\_printf() call is an excellent example of why this is useful. Rather than creating some arbitrary sized buffer and using snprintf() which may truncate the value, you can just use t\_strdup\_printf() without worrying about buffer sizes being large enough. Try to keep the allocations from data stack small, since the data stack's highest memory usage size is kept for the rest of the process's lifetime. The initial data stack size is 32kB and it should be enough in normal use. See lib/data-stack.h. Memory pools are useful when you have to construct an object from multiple pieces and you can free it all at once. Actually Dovecot's Memory Pool API is just an abstract class for allocating memory. There's system\_pool for allocating memory with calloc(), realloc() and free() and you can create a pool to allocate memory from data stack. If your function needs to allocate memory for multiple objects, you may want to take struct pool as parameter to allow caller to specify where the memory is allocated from. See lib/mempool. # Debugging Tips ## Core Dumps Whenever Dovecot crashes, you see something like this in log file: ``` dovecot: Apr 23 11:16:05 Error: child 86116 (imap) killed with signal 11 ``` No matter how that happened, it’s a bug and will be fixed if you can provide enough information on how it happened. ### Generating Core Dumps The best way to debug is to get a backtrace from gdb, but the problem is that Dovecot doesn’t dump core files by default. You can solve this in a few ways: #### systemd-coredump Install [systemd-coredump](https://www.freedesktop.org/software/systemd/man/systemd-coredump.html) if possible. It has proven to be good at capturing core dumps for Dovecot. #### `core dumps disabled` Run `ulimit -c unlimited` before starting Dovecot. If you start Dovecot using some script, note that they often override the limit, so you might have to override. Example: console ``` echo ‘DAEMON_COREFILE_LIMIT=”unlimited”‘ >> /etc/sysconfig/dovecot ``` #### `core not dumped` ##### Write Permissions The process dumping the core needs to be able to write to the core dump directory. Check where the core file should be written to and make sure it has enough write permissions. If core doesn’t contain an absolute path, it’s relative to the process’s current directory. With imap/pop3 processes this means that the core is written to the user’s home directory. Make sure home is returned by [userdb](/main/core/config/auth/userdb.html) by setting `[mail_debug = yes](/main/core/summaries/settings.html#mail_debug)` and checking from the logs that the correct home directory is returned. ###### Linux You can specify where core gets written: console ``` echo "/var/core/core.%e.%p" > /proc/sys/kernel/core_pattern ``` The crashing process needs to have write permissions for it: console ``` chmod 1777 /var/core ``` You need to allow core dumps in systemd as well. Run `systemctl edit dovecot` and add: ``` [Service] LimitCORE=infinity ``` ###### FreeBSD You can specify core location with `kern.corefile` sysctl. ##### Chrooted Process chrooted processes like imap-login will try to write the core inside the chroot, even when `core_pattern` is an absolute path. ##### Setuid Process For some services you may be able to tell Dovecot to drop privileges before execv(), making the process non-setuid. See `[service_drop_priv_before_exec](/main/core/summaries/settings.html#service_drop_priv_before_exec)`. ###### Linux Dovecot usually tells the kernel to dump the core anyway. Modern Linux systems require running this to enable: console ``` sysctl -w fs.suid_dumpable=2 ``` ###### FreeBSD console ``` sysctl kern.sugid_coredump=1 ``` ##### Core File Already Exists If there already exists a core file with the same name, it’s not overwritten. ##### Out of Disk Space If core files are written to their own filesystem, you may not notice if it runs out of disk space. ##### Core is Too Large If `ulimit -c` wasn't set to `unlimited`, maybe the core size limit was reached? #### `core dumped` Core file was successfully written! ### Direct GDB Debugging You can run, e.g., imap binary directly with gdb and talk IMAP protocol to it. ``` # gdb --args /usr/local/libexec/dovecot/imap -u user@domain ... (gdb) r Starting program: /usr/local/libexec/dovecot/imap * PREAUTH [CAPABILITY IMAP4rev1 SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN LISTEXT LIST-SUBSCRIBED] Logged in as user ``` Then start typing IMAP commands, e.g., `x select inbox, x fetch 1:* (flags envelope bodystructure), x fetch 1 body.peek[]`. When the program crashes, execute `bt full` command in gdb to debug. ## Hangs Check for a few seconds what system calls the process is doing. See [Process Tracing](/main/troubleshooting/debug.html#process-tracing). GDB backtrace can also be highly helpful, especially if process tracing doesn't show anything happening. Then it means Dovecot is in some infinite loop. You can get the backtrace by attaching GDB into the existing process: ``` gdb -p pid-of-hanging-process .. (gdb) bt full #0 cmd_select (client=0x808d800) at cmd-select.c:87 #1 0x0804d36a in client_handle_input (client=0x808d800) at client.c:306 #2 0x0804d439 in _client_input (context=0x808d800) at client.c:342 .. (gdb) quit ``` ## Client Traffic Sniffing If a problem happens only with a specific client, the best way to figure out what’s happening is to find out what it's actually talking to server. Some clients may provide logging on their own, or you can use some TCP traffic sniffer such as Wireshark or ngrep. You can also use Dovecot’s [rawlog](/main/core/admin/rawlog.html) to log the traffic. (It works with TLS/SSL.) ### Debugging Core Dumps You need the core dump, the binary that produced it, and ALL the shared libraries on the system. TIP If using `dovecot-sysreport`, all of this should be done for you. Manual collection sh ``` #!/bin/sh binary=/usr/libexec/dovecot/imap core=/var/core/core.12345 dest=core.tar.gz (echo "info shared"; sleep 1) | gdb $binary $core | grep '^0x.*/' | sed 's,^[^/]*,,' | xargs tar czf $dest --dereference $binary $core ``` #### Scripting gdb When you have multiple core dumps, it's troublesome to manually obtain the backtraces for all of them. Here's a script that takes a number of dovecot-sysreport-\*.tar.gz files as parameters and creates dovecot-sysreport-\*.tar.bt output files for them: Details bash ``` #!/bin/bash -e for fname in $*; do mkdir tmp-gdb cd tmp-gdb tar xzf ../$fname core_path=$(find . -name '*core*') # FIXME: handles only libexec files - should also support doveadm at least binary_name=$(file $core_path \| grep "dovecot/" \| sed "s/^.*from 'dovecot\/\([^']*\).*$/\1/") cat < ../$fname.bt set pagination off set solib-absolute-prefix . core $core_path bt full quit EOF cd .. rm -rf tmp-gdb done ``` ## Debug Pre-auth IMAP with libexec/dovecot/imap The imap executable can be started in IMAP PREAUTH mode. This circumvents login and authorization and lets you connect to the imap process directly, without having to go through the imap-login process. Start imap in PREAUTH with one of: sh ``` /usr/libexec/dovecot/imap -u src/imap/imap -u doveadm exec imap -u ``` This also works for pop3 and managesieve. lmtp can also be started from command line without the -u parameter (because the recipient is given with RCPT TO command). ## Debug Pre-auth IMAP with a Real Client 1. Run the following script: sh ``` rm -f /tmp/f && mkfifo /tmp/f && \ cat /tmp/f | ./imap -u testuser | nc -l 127.0.0.1 143 > /tmp/f ``` 2. Connect to port 143 with the imap client of your choice. 3. Attach with gdb to the running imap process, if you so wish. This can also be used together with imaptest, if you define imaptest to use only one username. Some configurations or use cases might require dovecot to be running (e.g. for authentication). In those cases, select a different port for "nc" or change dovecot not to listen on 143. ## Debug Most Dovecot Executables Inside the Source Tree To avoid having to make install dovecot before running an executable in gdb or valgrind, use libtool. For example: sh ``` libtool e gdb --args src/imap/imap ``` Maybe add a helpful alias to your `.bashrc` or similar: bash ``` alias lgdb='libtool --mode=execute gdb' ``` ## Debug Dovecot Master ``` GDB=1 gdb --args dovecot -F ``` Start with `-F` = foreground mode. If you've compiled --with-devel-checks, GDB=1 environment needs to be set so it doesn't fail when it detects extra "leaked" file descriptors. ## Standalone Non-Root Debugging Environments Dovecot can be instructed to run the imap handler as a non-root user, and therefore that binary can be debugged by that same non-root user. At the moment, only manual (telnet) interaction is possible. This setup is documented in [Rootless Installation](/main/core/config/rootless.html). ## Disabling Optimizations gdb can be difficult if code is compiled with optimizations (-O2). Printing variables may fail and stepping over code can jump confusingly all over the place. The easiest way to avoid this is to just disable optimizations either for Dovecot entirely or just some files: - Globally: `EXTRA_CFLAGS=-O0 ./configure ...` - Within a directory: Remove -O2 from CFLAGS in Makefile - Edit/touch some .c file(s) in the directory to recompile, or make clean to rebuild everything in the directory If you want to just select a few functions to be built with -O0, then create 2 files, src/lib/optimise-off.h and src/lib/options-restore.h as follows: c ``` #if defined(__GNUC__) #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 4)) #pragma GCC push_options #pragma GCC optimize (0) #else #warning GCC not new enough to support local optimization pragmas #endif #endif ``` and c ``` #if defined(__GNUC__) #if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 4)) #pragma GCC pop_options #else /* He already knows */ #endif #endif ``` Then just use #include those files around just the functions you want un-optimised. Another dirty and quick way to opt out the optimization is to use pragma to quote the code. c ``` #pragma GCC push_options #pragma GCC optimize ("O0") .... code #pragma GCC pop_options ``` ## Following Deep Inside Structs Dovecot implements classes/objects using C structs. For example there is the `struct connection` base object, which is extended with: c ``` struct dict_connection { struct connection conn; ... } ``` However, many places still refer to these extended objects using their base classes, so you need to cast them to get all of their wanted fields visible. For example: c ``` (gdb) p dict_connections $1 = (struct connection_list *) 0x55823025e9a0 (gdb) p dict_connections.connections $2 = (struct connection *) 0x55823025c160 (gdb) p *dict_connections.connections $3 = {prev = 0x0, next = 0x0, list = 0x55823025e9a0, ... the rest of struct connection (gdb) p *(struct dict_connection *)dict_connections.connections $4 = {conn = {prev = 0x0, next = 0x0, list = 0x55823025e9a0, ... the rest of struct dict_connection ``` It's a bit more tricky to look inside dynamic array types. As an example lets consider `ARRAY(struct dict_connection_cmd *) cmds`. This ends up being expanded into: c ``` struct array { buffer_t *buffer; size_t element_size; }; union { struct array arr; struct dict_connection_cmd *const *v; struct dict_connection_cmd **v_modifiable; } cmds; ``` You can find out the size of the array with: c ``` p cmds.arr.buffer.used / cmds.arr.element_size ``` You can access the elements of the array with: c ``` p *(*cmds.v)[0] p *(*cmds.v)[1] p *(*cmds.v)[...] ``` So to actually access the `dict_connection.cmds` array for the first connection in `dict_connections`, the gdb print commands get a bit long: c ``` (gdb) p ((struct dict_connection *)dict_connections.connections).cmds $5 = {arr = {buffer = 0x55823026da80, element_size = 8}, v = 0x55823026da80, v_modifiable = 0x55823026da80} (gdb) p ((struct dict_connection *)dict_connections.connections).cmds.arr.buffer.used / 8 $6 = 1 (gdb) p *(*((struct dict_connection *)dict_connections.connections).cmds.v)[0] $7 = {cmd = 0x55822ecc8b00 , conn = 0x55823025c160, start_timeval = { tv_sec = 1632257119, tv_usec = 530341}, event = 0x558230280b98, reply = 0x0, iter = 0x0, iter_flags = 0, async_reply_id = 0, trans_id = 0, rows = 0, uncork_pending = false} ``` There can of course be multiple dict connections, which you can access by following the linked list: c ``` (gdb) p *dict_connections.connections.next (gdb) p *dict_connections.connections.next.next (gdb) p *dict_connections.connections.next.next.next ``` ## Debugging Data Stack Growth Dovecot uses [Data Stack](/main/developers/design/memory.html#data-stack) to implement its own secondary stack. This stack is intended to usually stay rather small, ideally within its initial 32 kB size. There are `data_stack_grow` events sent when it grows. To debug why data stack grows, you can have it panic: ``` log_core_filter = event=data_stack_grow # Or have it panic later: log_core_filter = event=data_stack_grow and alloc_size >= 10240 ``` The core dump can then be analyzed: c ``` (gdb) p *current_frame $1 = {prev = 0x555555874e78, block = 0x555555910760, block_space_left = 15640, last_alloc_size = 96, marker = 0x5555557e726c "data-stack.c:514", alloc_bytes = 96, alloc_count = 1} (gdb) p *current_frame.prev $2 = {prev = 0x555555874e18, block = 0x5555558742a0, block_space_left = 7264, last_alloc_size = 744, marker = 0x5555557c011f "index-storage.c:1056", alloc_bytes = 7312, alloc_count = 71} (gdb) p *current_frame.prev.block $3 = {prev = 0x0, next = 0x555555910760, size = 10240, left = 696, left_lowwater = 696, canary = 0xbadbadd5badbadd5, data = 0x5555558742d0 "8"} ``` First look at the `block` variable for these frames, and note how it changes for the 2rd one. So the data stack is grown between the 1nd and the 2rd frame. And since `block_space_left` was about 7 kB while the block's full size was 10240 bytes, most of the space is allocated sometimes after `index-storage.c:1056`. We can also look further into the data stack frames to see if there are any other frames that use up a lot of memory: c ``` (gdb) p *current_frame.prev.prev $4 = {prev = 0x555555874db8, block = 0x5555558742a0, block_space_left = 7360, last_alloc_size = 96, marker = 0x5555557aef7c "mail-storage.c:2818", alloc_bytes = 96, alloc_count = 1} ... $5 = {prev = 0x5555558743e0, block = 0x5555558742a0, block_space_left = 8440, last_alloc_size = 560, marker = 0x5555557a5467 "cmd-copy.c:328", alloc_bytes = 984, alloc_count = 6} (gdb) p *current_frame.prev.prev.prev.prev.prev $6 = {prev = 0x555555874338, block = 0x5555558742a0, block_space_left = 9976, last_alloc_size = 112, marker = 0x5555557a720f "imap-client.c:1357", alloc_bytes = 1536, alloc_count = 14} ``` So there was also some 1.5 kB used between `imap-client.c:1357` and `cmd-copy.c:328` which might be worth looking into. Once you start debugging, get a gdb backtrace and start inserting further data stack frames into the function calls that the gdb backtrace shows. For example: c ``` (gdb) bt #0 data_stack_send_grow_event (last_alloc_size=744) at data-stack.c:400 #1 t_malloc_real (size=, permanent=) at data-stack.c:523 ... #10 0x000055555565257c in index_list_get_metadata (box=0x5555558ee8b0, items=MAILBOX_METADATA_CACHE_FIELDS, metadata_r=0x7fffffffe180) at mailbox-list-index-status.c:343 #11 0x00005555555ea928 in mailbox_get_metadata (box=0x5555558ee8b0, items=items@entry=MAILBOX_METADATA_CACHE_FIELDS, metadata_r=metadata_r@entry=0x7fffffffe180) at mail-storage.c:2204 #12 0x0000555555672794 in index_copy_cache_fields ( ctx=ctx@entry=0x5555559093b0, src_mail=src_mail@entry=0x555555904408, dest_seq=1) at index-storage.c:1068 ``` Here you can see that #1 matches is inside the `data-stack.c:514` data stack frame and #12 is inside the `index-storage.c:1056` data stack frame. So you could start placing more `T_BEGIN { .. } T_END` frames between #2 and #11 frames shown by gdb to get more details where the data stack is being used. ## Valgrind The most useful Valgrind parameters: | Parameter | Description | | --- | --- | | `--vgdb=no` | Needed on some systems to avoid problems. | | `--keep-debuginfo=yes` | Prevent unhelpful `??` for already unloaded plugins. | | `--num-callers=` | Display last functions calls in the backtrace for each error. | | `--leak-check=full` | Show also unfreed memory as being leaked. | | `--trace-children=yes` | Trace also the forked child processes. | | `--suppressions=` Suppressions for things that can't be fixed. Usually in external libraries. | | | `-q` | Quiet: don't log about initialization etc. | ### Standalone sh ``` valgrind --leak-check=full /usr/libexec/dovecot/imap -u user@example.com ``` ### Service Settings doveconf ``` service imap { executable = /usr/bin/valgrind --vgdb=no --num-callers=50 --leak-check=full -q /usr/local/libexec/dovecot/imap } ``` ### Debugging Valgrind errors in a Live Program Using GDB sh ``` valgrind --vgdb-error=0 ``` And follow the instructions given by valgrind. Also works within emacs. ### Debugging over forks with Valgrind If you're an emacs user, you're only allowed to debug one process at a time (per emacs). As above, sh ``` valgrind --vgdb-error=0 ``` Method 1: Just start the first gdb in a different shell or emacs window as normal, and then pretty much ignore it. After the fork, pick up the process in your primary emacs gdb window as normal. Method 2: Follow the gdb instructions, "cont" the program, ^C, and "detach" the program. I'm not sure what happens to the parent after that. When valgrind stops on the fork, follow the new set of gdb instructions and continue as normal. In neither method do you need to "set follow-fork-mode child" as gdb isn't aware of the fork. ### Valgrinding Plugin Memory Leaks Valgrind output at exit may contain very unhelpful `??` lines, which point to already unloaded plugins. You can avoid this by giving `--keep-debuginfo=yes` parameter. Alternative way would be to set GDB=1 environment to disable all plugin unloading. This will cause some extra warnings about leaking memory in dl\*() functions which can be ignored. You can also do this in dovecot.conf: doveconf ``` import_environment { GDB = 1 } ``` ## Debugging Tools See [debugging](/main/troubleshooting/debug.html) for scripts that are useful for reporting issues to the Dovecot developers. ### Docker Debugging You can run a local, temporary instance of Dovecot to test and debug. See [Docker Container Debugging/Testing](/main/installation/docker.html#running-as-debug-testing-install). # Dynamic Arrays `lib/array.h` and `lib/array-decl.h` describes Dovecot's type-safe dynamic arrays. Trying to add wrong typed elements gives a compiler warning. ## Declaring Arrays can be declared in two ways: 1. Directly: c ``` ARRAY(int) numbers; ARRAY(struct foo) foos; ``` 2. Via predefined type: c ``` ARRAY_DEFINE_TYPE(foo, struct foo); ... ARRAY_TYPE(foo) foos; ``` The main reason to define a type for an array is to be able to pass the array as a function parameter, like: c ``` void func(ARRAY_TYPE(foo) *foos) { .. } ``` Trying to do the same with `ARRAY()` will generate a compiler warning about type mismatch. `lib/array-decl.h` defines several commonly used types. ## Initializing Arrays are typically initialized by calling `i_array_init()`, `p_array_init()` or `t_array_init()` depending on where you want to allocate the memory from. Arrays are internally handled as [buffers](./buffers.html), sp the initial size is just multiplied by element size and passed to `buffer_create_dynamic()`. Example: c ``` ARRAY(struct foo *) foo; i_array_init(&foo, 32); /* initialize array with 32 elements until it needs to be grown */ ``` Arrays can be freed with `array_free()`, but this isn't necessary if the memory gets freed by other means (i.e. it was allocated from alloconly-pool or data stack). ## Writing ### `array_push_back(array, data)` Append one element to the end of the array. ### `array_push_front(array, data)` Prepend one element to the beginning of the array. ### `array_append(array, data, count)` Append multiple elements to the end of the array. ### `array_append_zero(array)` Append a zero-filled element to the end of the array. ### `array_append_array(dest, src)` Append src array to the end of the dest array. ### `array_copy(dest, dest_idx, src, src_idx, count)` Copy (overwrite) a slice of the src array over the dest array. ### `array_insert(array, idx, data, count)` Insert element at the specified index (0 = first) ### `array_idx_set(array, idx, data)` Replace data at the specified index. If index points after the end of the array, the other newly added elements are zero-filled. ### `array_idx_clear(array, idx)` Zero-fill the data at the specified index. If index points after the end of the array, the other newly added elements are zero-filled. ### `array_delete(array, idx, count)` Delete the specified slice of the array. ### `array_pop_back(array)` Delete the last element of the array. ### `array_pop_front(array)` Delete the first element of the array. ### `array_clear(array)` Delete all elements in the array. ## Reading Usually array is read by going through all of its elements. This can be done by returning all the elements: c ``` unsigned int count; const struct foo *foo = array_get(&array, &count); struct foo *foo = array_get_modifiable(&array, &count); ``` or the array can also be iterated easily: c ``` const struct foo *foo; array_foreach(&foo_array, foo) { /* foo changes in each iteration */ } struct foo *foo; array_foreach_modifiable(&foo_array, foo) { ... } ``` The `_modifiable()` versions return a non-const pointer. Arrays that are pointers-to-pointers can be iterated like: c ``` ARRAY(struct foo *) foo_array; struct foo *const *foop; array_foreach(&foo_array, foop) { struct foo *foo = foop; } ``` Or more simply using `array_foreach_elem()`: c ``` ARRAY(struct foo *) foo_array; struct foo *foo; array_foreach_elem(&foo_array, foo) { ... } ``` Note that deleting an element while iterating will cause the iteration to skip over the next element. So deleting a single element and breaking out of the loop is fine, but continuing the loop is likely a bug. Use instead: c ``` array_foreach_reverse(&foo_array, foo) { if (want_delete(foo)) array_delete(&foo_array, array_foreach_idx(&foo_array, foo), 1); } ``` There's also an equivalent `array_foreach_reverse_modifiable()`. It's a bug to attempt to use the read functions before the array is initialized. Use `array_is_created()` to check if it's initialized. There are also `array_idx_elem()` and `array_foreach_elem()` to access arrays of pointers more easily. They work by making a copy of the dereferenced pointer. For example: c ``` ARRAY(const char *) strings; // array_idx() requires dereferencing: const char *const *strp = array_idx(&strings, idx); printf("%s\n", *strp); // array_idx_elem() dereferences already: printf("%s\n", array_idx_elem(&strings, idx)); // array_foreach() requires dereferencing: const char *const *strp; array_foreach(&strings, strp) printf("%s\n", *strp); // array_foreach_elem() dereferences already: const char *str; array_foreach_elem(&strings, str) printf("%s\n", str); ``` ### `array_idx(array, idx)` Return a const pointer to the specified index. Assert-crashes if the index doesn't already exist. ### `array_front(array)` Return a const pointer to the first element in the array. Assert-crashes if the array is empty. ### `array_back(array)` Return a const pointer to the last element in the array. Assert-crashes if the array is empty. ### `array_count(array)` Return the number of elements in an array. ### `array_is_empty(array)` Return TRUE if array has zero elements. ### `array_not_empty(array)` Return TRUE if array has more than zero elements. ## Unsafe Read/Write Functions below have similar problems to [buffer's](./buffers.html)`*_unsafe()` functions. Memory returned by them must not be accessed after calls to other `array_*()` modifying functions, because they may reallocate the array elsewhere in memory. ### `array_append_space(array)` Append a new element into the array and return a writable pointer to it. ### `array_insert_space(array, idx)` Insert a new element into the array and return a writable pointer to it. ### `array_idx_get_space(array, idx)` Return a writable pointer to the specified index in the array. If index points after the end of the array, the newly added elements are zero-filled. ### `array_get_modifiable(array, &count)` Return a non-const pointer to all the elements in the array and the number of elements in the array. ### `array_idx_modifiable(array, idx)` Return a non-const pointer to the specified index. Assert-crashes if the index doesn't already exist. See also [`array_idx_get_space()`](#array-idx-get-space-array-idx). ### `array_front_modifiable(array)` Return a non-const pointer to the first element in the array. Assert-crashes if the array is empty. ### `array_back_modifiable(array)` Return a non-const pointer to the last element in the array. Assert-crashes if the array is empty. ## Others ### `array_cmp(array1, array2)` Return TRUE if the arrays contain exactly the same content. ### `array_reverse(array)` Reverse all elements in the array. ### `array_sort(array, cmp_func)` Type-safe wrapper for `qsort()`. The parameters in the `cmp_func` should be the same type as the array instead of `const void *`. ### `array_bsearch(array, key, cmp_func)` Type-safe wrapper for `bsearch()`, similar to `array_sort()`. ### `array_equal_fn(array1, array2, cmp_func)` Return TRUE if arrays are equal. Each element in the array is compared with the `cmp_func`. ### `array_equal_fn_ctx(array1, array2, cmp_func, context)` Like `array_equal_fn()`, except `cmp_func` has a context parameter. ### `array_lsearch(array, key, cmp_func)` Returns a const pointer to the first element where `cmp_func(key, element)==0`. ### `array_lsearch_modifiable(array, key, cmp_func)` Returns a non-const pointer to the first element where `cmp_func(key, element)==0`. # Dovecot Authentication Process Design See [processes](./processes.html) for an overview of how the Dovecot processes work. There are four major classes in the code: - `struct mech_module`: Authentication (SASL) mechanism - `struct password_scheme`: Password scheme - `struct passdb_module`: Password database - `struct userdb_module`: User database There are many implementations for each of these, and it's simple to add more of them. They can also be added as plugins. The code flow usually goes like: - The auth process listens for new authentication client connections. - A new authentication client (e.g. login process) connects to the `login` or `auth-client` UNIX socket. - Authentication client sends a request to begin a SASL authentication. - Authentication mechanism backend handles it (`mech->auth_initial()` and `mech->auth_continue()` in `mech-*.c`) - The mechanism either asks the passdbs to verify a username/password pair (`auth_request_verify_plain()`), or it looks up the credentials itself (`auth_request_lookup_credentials()`) and verifies that they are valid. - Success reply is sent to the authentication client. - If this is a login from login process, it creates a mail process by connects to the process type-specific socket (e.g. `imap` or `pop3`) and sending the authentication reply information to it. - The mail process connects to the `auth-master` UNIX socket and finishes the authentication request. This includes doing a userdb lookup, which is returned back to the mail process. The authentication is fully asynchronous and it supports handling multiple requests in parallel. It's also possible to do passdb and userdb lookups directly without full authentication. The login socket is mostly treated as untrusted. It's not possible for it to authenticate users without actually providing the proper credentials. However, there are some fields that need to be trusted: - Client IP address and port - Local server IP address and port - Connecting proxy's IP address and port - Client TLS certificate's username and trust status. This means that if authentication is done via client TLS certificates, the auth process simply trusts the login process to verify the certificate. This breaks the trust model and should be fixed some day. ## Authentication (SASL) Mechanisms These are SASL authentication mechanism implementations. See [SASL](/main/core/admin/sasl.html) for a list of mechanisms supported by Dovecot. A new mechanism is created by filling a `struct mech_module` (in `mech.h`) and passing it to `mech_register_module()`. ### Struct Fields #### `mech_name` The public name of the mechanism. This is shown to clients in the IMAP, POP3 and SMTP capability lists. If you create a new non-standard mechanism, please prefix it with "X-". #### `flags` Describes how secure the mechanism is. Also `MECH_SEC_PRIVATE` flag specifies that the mechanism shouldn't be advertised in the capability list. This is currently used only for APOP mechanism, which is defined by the POP3 protocol itself. #### `passdb_need_plain` This mechanism uses passdb's `verify_plain()` function to verify the password's validity. This means that the mechanism has access to the cleartext password. This is true only for cleartext mechanisms such as PLAIN and LOGIN. The main purpose of this flag is to make dovecot-auth complain at startup if there are no passdbs defined in the configuration file. Note that a configuration without any passdbs is valid with eg. GSSAPI mechanism which doesn't need a passdb at all. #### `passdb_need_credentials` This mechanism uses passdb's `lookup_credentials()` function. See below for description of the credentials. #### `auth_new()` Allocates a new `struct auth_request`. Typically with more complex mechanisms it really allocates a `struct _auth_request` which contains `struct auth_request` as the first field, followed by mechanism-specific fields. #### `auth_initial(request, data, data_size)` This begins the authentication, data and data\_size containing the initial response sent by the client (decoded, not in base64). Call `request->callback()` once you're done (see below). #### `auth_continue(request, data, data_size)` Continues the authentication. Works the same as `auth_initial()`. #### `auth_free()` Free the request. Usually all the memory allocations for the request should be allocated from `request->pool`, so you can use `mech_generic_auth_free()` which simply frees the pool. ### SASL Authentication `auth_initial()` and `auth_continue()` can send SASL continuation requests. For success and failure replies, use one of these functions: - `auth_request_success()` - `auth_request_fail()` - `auth_request_internal_failure()`: Use this if you couldn't figure out if the authentication succeeded or failed, for example because passdb lookup returned internal failure. SASL authentication in general works like: 1. Client begins the authentication, optionally sending an "initial response", meaning some data that the mechanism sees in `auth_initial()`. - Note that not all protocols support the initial response. For example IMAP supports it only if the server implements SASL-IR extension. Because of this mechanisms, such as PLAIN, support doing the authentication either in `auth_initial()` or in `auth_continue()`. - If the client initiates the authentication (ie. server's initial reply is empty, such as with PLAIN mechanism) you can use `mech_generic_auth_initial()` instead of implementing your own. 2. Server processes the authentication request and replies back with `auth_request_handler_reply()`. - If the authentication failed, it's placed into `auth_failures` array unless `request->no_failure_delay=TRUE`. The failures are flushed from the buffer once every 2 seconds to clients and `mechanism->auth_free()` is called. - If the authentication succeeded and - there is a master connection associated with the request (IMAP/POP3 login), the authentication now waits for master connection to do a verification request. If this for some reason doesn't happen in `AUTH_REQUEST_TIMEOUT` seconds (3,5 mins), it's freed. - there isn't a master connection (SMTP AUTH), the authentication is freed immediately. 3. Client processes the reply: - If the authentication continues, it sends back more data which is processed in `auth_continue()`. Goto 2. - If the authentication failed, it's done. 4. If the authentication succeeded, the client connects to the post-login mail process, which in turn connects to auth and does the final auth verification. - Besides verifying the authentication, auth process also does a userdb lookup to return the userdb information to the mail process. - If the verification fails (normally because userdb lookup fails), the client gets "internal authentication failure" - If the verification succeeds, the user is now logged in - In either case, `mechanism->auth_free()` is called now. ## Credentials Most of the non-cleartext mechanisms need to verify the authentication by using a special hash of the user's password. So either the passdb credentials lookup returns a cleartext password from which the hash can be created, or the hash directly. Both of these cases can be handled simply by calling `auth_request_lookup_credentials()` with the wanted password scheme. ### Password Schemes #### `struct password_scheme` Fields ##### `name` Name of the scheme. This only shows up in configuration files and maybe in the passwords stored in passdb ("{scheme\_name}password\_hash"). ##### `password_verify(cleartext, params, raw_password, raw_password_size, error)` Returns 1 if `raw_password` hash matches the cleartext password given in `cleartext` parameter. The `raw_password` is in binary, i.e. not hex or base64-encoded. ##### `password_generate(cleartext, params, raw_password_r, raw_password_size_r)` Returns the password hash for given cleartext password. The `params` can be used to specify some extra parameters: - `user`: Used if the password hash depends on the username (eg. with DIGEST-MD5). - `rounds`: Some schemes support a configurable number of hash rounds. #### New Password Scheme A new password scheme can be created simply by creating a `struct password_scheme` named `_scheme`, compiling a shared object and placing it to `$moduledir/auth/` directory. ### Password Databases See [passdb](/main/core/config/auth/passdb.html) for a description of passdbs and a list of already implemented ones. Plaintext authentication mechanisms typically call `verify_plain()`, which is possible to implement with all the passdbs. Non-cleartext mechanisms typically call `lookup_credentials()`, which isn't possible to implement always (eg. PAM). If it's not possible to implement `lookup_credentials()`, the pointer can be left NULL. If the passdb uses connections to external services, it's preferred that they use non-blocking connections. Dovecot does this whenever possible (PostgreSQL and LDAP for example). If it's not possible, set `[passdb_use_worker = yes](/main/core/summaries/settings.html#passdb_use_worker)`. With both functions `auth_request->passdb->passdb` contains the passdb\_module returned by your `preinit()` function. `auth_request->user` contains the username whose password is being verified. There's no need to worry about [Master Users](/main/core/config/auth/master_users.html) here. It's also possible to use any other fields in `auth_request` to do the lookup, such as `service`, `local_ip` or `remote_ip` if they exist. Often you want to let user to configure the lookup with `[Settings variables](/main/core/settings/variables.html)` (e.g., SQL query). In that case you can use `auth_request_get_var_expand_table()` to retrieve the variable table for `var_expand()`. #### Passdb Lookup Results The passdb lookup can return one of the following results: ##### `PASSDB_RESULT_INTERNAL_FAILURE` The lookup failed. For example SQL server is down. ##### `PASSDB_RESULT_SCHEME_NOT_AVAILABLE` `lookup_credentials()` requested a scheme which isn't in the passdb. ##### `PASSDB_RESULT_USER_UNKNOWN` The user doesn't exist in the database. ##### `PASSDB_RESULT_USER_DISABLED` The user is disabled either entirely, or for this specific login (eg. only POP3 logins allowed). This isn't commonly implemented in passdbs. ##### `PASSDB_RESULT_PASS_EXPIRED` The user's password had expired. This isn't commonly implemented in passdbs. ##### `PASSDB_RESULT_NEXT` Internal use only: The "noauthenticate" field is set. ##### `PASSDB_RESULT_PASSWORD_MISMATCH` The password given in `verify_plain()` wasn't valid. ##### `PASSDB_RESULT_OK` Success. #### `struct passdb_module` fields: ##### `cache_key` A string containing `[Settings variables](/main/core/settings/variables.html)`. When expanded, it uniquely identifies a passdb lookup. This is `%{user}` when the passdb lookup validity depends only on the username. With more complex databases such as SQL and LDAP this is created dynamically based on the password query in the configuration file. If there are multiple variables, they should be separated so that their contents don't get mixed, for example `%{user}%{rmote_ip}%{local_ip}`. `auth_cache_parse_key()` can be used to easily create a cache key from a query string. ##### `default_pass_scheme` The default scheme to use when it's not explicitly specified with a "{scheme}" prefix. ##### `blocking` If TRUE, the lookup is done in auth-worker process. This should be used if the lookup may block. ##### `iface.preinit(pool, args)` Allocate `struct passdb_module` from the `pool` and return it. This function is called before chrooting and before privileges are dropped from the auth process, so if should do things like read the configuration file. `args` contains the args parameter in the configuration file. ##### `iface.init(module)` The privileges have been dropped before calling this. `module` contains the structure returned by `preinit()`. Typically this function will do things like connect to the database. ##### `iface.deinit(module)` Close the connection to the password database and free all the used memory. ##### `iface.verify_plain(auth_request, password, callback)` Check if the given cleartext password matches. `auth_request->wanted_credentials_scheme == NULL` always. When the verification is done, call the given callback with the result in `result` parameter. ##### `iface.lookup_credentials(auth_request, callback)` Look up the password credentials. `auth_request->wanted_credentials_scheme` contains the credentials that the mechanism wants. Afterwards call `passdb_handle_credentials()` to finish the request. ### User Databases See [userdb](/main/core/config/auth/userdb.html) for a description of userdbs and a list of already implemented ones. `struct userdb_module` is very similar to `struct passdb_module`. The lookup callback is a bit different though: c ``` typedef void userdb_callback_t(enum userdb_result result, struct auth_request *request); ``` There is no equivalent for `PASSDB_RESULT_USER_DISABLED` currently. When logging in with IMAP or POP3, the user's existence was already checked in passdb lookup, so only in rare conditions when a user is logging in at the same time as it's being deleted, the userdb result is `USER_UNKNOWN`. #### User Database Lookup Results The results are added to the auth\_request using `auth_request_set_field()` and `auth_request_set_userdb_field()`. `result` contains one of: ##### `USERDB_RESULT_INTERNAL_FAILURE` The lookup failed. For example SQL server is down. ##### `USERDB_RESULT_USER_UNKNOWN` The user doesn't exist in the database. ##### `USERDB_RESULT_OK` Success. # Dovecot Authentication Protocol INFO This document specifies the Dovecot Auth protocol v1.3. This is a line based protocol. Each line is a command which ends with an LF character. The maximum line length isn't defined, but it's currently expected to fit into 16384 bytes. Authentication mechanism specific data transfers are the largest single parameters. Each command is in format: ``` TAB ``` Parameters are split into required and optional parameters. Required parameters aren't in any specific format, but optional parameters are either booleans without a value, or a name=value pair. If optional parameter name is unknown, the parameter should just be ignored. Typical command looks like (without spaces): ``` command TAB param1 TAB param2 TAB optname=value TAB optboolean ``` The parameters use Dovecot's generic "tab-escaping", where `\001` is used as the escape character. After it follows: - `0`: NUL character - `1`: `\001` character - `t`: TAB - `r`: CR - `n`: LF - anything else writes the character itself (skipping the `\001`) ## Client <-> Server Client is an untrusted authentication client process. For example in Dovecot the imap-login process is an auth client. The same auth client can perform multiple authentications against different users. Server is the auth process. The connection starts by both client and server sending the VERSION: ``` C: "VERSION" TAB TAB S: "VERSION" TAB TAB ``` The client can also pipeline the rest of the handshake with the VERSION: ``` C: "CPID" TAB ``` Changed: 2.4.0 The server no longer pipelines the rest of the handshake with the VERSION. It first waits for the client to provide the VERSION. Based on that, it may give a different response. The rest of the server handshake: ``` S: "SPID" TAB S: "CUID" TAB S: "COOKIE" TAB S: "MECH" TAB [TAB ] (multiple times) S: "DONE" ``` Both client and server should check that they support the same major version number. If they don't, the other side isn't expected to be talking the same protocol and should be disconnected. Minor version can be ignored. - CPID and SPID specify client and server Process Identifiers (PIDs). They should be unique identifiers for the specific process. UNIX process IDs are good choices. - CUID is a server process-specific unique connection identifier. It's different each time a connection is established for the server. - CPID is used by master's REQUEST command. - SPID can be used by authentication client to tell master which server process handled the authentication. - CUID is currently useful only for APOP authentication. - COOKIE returns connection-specific 128 bit cookie in hex. It must be given to REQUEST command. - DONE finishes the handshake from server. CPID finishes the handshake from client. ### Authentication Mechanisms MECH command announces an available authentication SASL mechanism. If the client advertises minor version 3 or higher, also SASL mechanisms using channel binding are returned. Mechanisms may have parameters giving some details about them: | Parameters | Description | | --- | --- | | `anonymous` | Anonymous authentication | | `plaintext` | Transfers plaintext passwords | | `dictionary` | Subject to passive (dictionary) attack | | `active` | Subject to active (non-dictionary) attack | | `forward-secrecy` | Provides forward secrecy between sessions | | `mutual-auth` | Provides mutual authentication | | `channel-binding` | Uses channel binding (v1.3+ protocol) | | `private` | Don't advertise this as available SASL mechanism (eg. APOP) | ### Authentication Request ``` C: "AUTH" TAB TAB TAB service= [TAB ] S1: "FAIL" TAB [TAB ] S2: "CONT" TAB TAB S3: "OK" TAB [TAB ] ``` ID is a connection-specific unique request identifier. It must be a 32bit number, so typically you'd just increment it by one. Service is the service requesting authentication, eg. pop3, imap, smtp. AUTH and USER (see below) common parameters are: | Parameters | Description | | --- | --- | | `session=` | Unique session ID. Mainly used for logging. | | `lip=` | Local IP connected to by the client. In standard string format, e.g. `127.0.0.1` or `::1`. | | `rip=` | Remote client IP | | `lport=` | Local port connected to by the client. | | `rport=` | Remote client port | | `real_rip` `real_lip` `real_lport` `real_rport` | When Dovecot proxy is used, the real\_rip/real\_port are the proxy's IP/port and real\_lip/real\_lport are the backend's IP/port where the proxy was connected to. | | `local_name=` | TLS SNI name | | `debug` | Enable debugging for this lookup. | | `forward_fields` | List of fields that will become available via `%{forward_*}` variables. The list is double-tab-escaped, like: `tab_escaped[tab_escaped(key=value)[...]` | AUTH-only parameters are: | Parameters | Description | | --- | --- | | `secured[=tls]` | Remote user has secured transport to auth client (e.g. localhost, SSL, TLS). | | `transport=` | The value can be "insecure", "trusted" or "TLS". | | `tls_cipher=` | TLS cipher being used. | | `tls_cipher_bits=` | The number of bits in the TLS cipher. | | `tls_pfs=` | TLS perfect forward secrecy algorithm (e.g. DH, ECDH) | | `tls_protocol=` | TLS protocol name (e.g. `TLSv1.2`) | | `valid-client-cert` | Remote user has presented a valid SSL certificate. | | `no-penalty` | Ignore auth penalty tracking for this request | | `cert_username` | Username taken from client's SSL certificate. | | `client_id` | IMAP ID string | | `resp=` | Initial response for authentication mechanism. NOTE: This must be the last parameter. Everything after it is ignored. This is to avoid accidental security holes if user-given data is directly put to base64 string without filtering out tabs. | FAIL parameters may contain: | Parameters | Description | | --- | --- | | `reason=` | `` should be sent to remote user instead of the standard "Authentication failed" messages. For example "invalid base64 data". It must NOT be used to give exact reason for authentication failure (i.e. "user not found" vs. "password mismatch"). | | `code=temp_fail` | This is a temporary internal failure, e.g. connection was lost to SQL database. | | `code=authz_fail` | Authentication succeeded, but authorization failed (master user's password was ok, but destination user was not ok). | | `code=user_disabled` | User is disabled (password may or may not have been correct) | | `code=pass_expired` | User's password has expired. | A CONT response means that the authentication continues, and more data is expected from client to finish the authentication. Given base64 data should be sent to client. The client may continue the process issuing ``` C: "CONT" TAB TAB ``` The `` must match the `` of the AUTH command. FAIL and OK may contain multiple unspecified parameters which authentication client may handle specially. The only one specified here is `user=` parameter, which should always be sent if the userid is known. ## Server <-> Master Master is a trusted process which may query results of previous client authentication or information about a specific user. Master is optional and in SMTP AUTH case it's not needed. The connection starts by both server and master sending handshakes: ``` S: "VERSION" TAB TAB S: "SPID" TAB M: "VERSION" TAB TAB ``` Auth with client <-> server, both should check that the version numbers are valid. SPID can be used to let master identify the server process. ### Master Requests ``` M: "REQUEST" TAB TAB TAB TAB M: "USER" TAB TAB TAB service= [TAB ] S: "NOTFOUND" TAB S: "FAIL" TAB TAB S: "USER" TAB TAB [TAB ] ``` Master commands can request information about existing authentication request, or about a specified user. USER command's service and parameters are the same as with AUTH client request. ID is a connection-specific unique request identifier. It must be a 32bit number, so typically you'd just increment it by one. NOTFOUND reply means that the user wasn't found. FAIL reply means an internal error occurred. Usually either a configuration mistake or temporary error caused by lost resource (e.g. database down). Also unknown request IDs are reported as FAILs. USER reply is sent if request succeeded. It can return parameters: | Parameters | Description | | --- | --- | | `uid=` | System user ID. | | `gid=` | System group ID. | | `home=` | Home directory. | | `chroot=` | Chroot directory. | There can be also other extra fields. # Buffers `lib/buffer.h` describes Dovecot's buffer API. Unless your code happens to be VERY performance critical, you shouldn't handle writing to buffers/arrays manually, but instead use the buffer API's safe functions to guarantee that your code can't write past the buffer and cause a security hole. Dovecot's buffers are the basic building block for [arrays](./arrays.html) and [strings](./strings.html). Use them instead if they make more sense than buffers. There are a two different ways to create buffers: statically and dynamically allocated. ## Static Buffers You can create statically allocated buffers with `buffer_create_data()`. Trying to write past the given buffer size will panic. Static buffers don't need to be freed. The code to initialize static buffers looks like: c ``` unsigned char buf_data[1024]; buffer_t buf; buffer_create_from_data(&buf, buf_data, sizeof(buf_data)); ``` Trying to write more than 1024 bytes to the buffer will cause an assert-crash, so these buffers shouldn't be used unless you know exactly what the maximum buffer size is. To avoid accidental buffer overflows, don't use complex calculations in the size parameter of `buffer_create_from_data()`. It should always be `sizeof(data_buffer)`. You can also create non-writable buffers with `buffer_create_from_const_data()`. ## Dynamic Buffers Dynamically growing buffers can be created with `buffer_create_dynamic(pool, init_size)`. Memory for the buffer is allocated from the given pool. When memory needs to be grown, it's grown exponentially 2n, with some exceptions to avoid growing the given memory pool unless necessary. The initial buffer size is always a guess - try to make it large enough that buffer wouldn't be grown most of the time, but not so large that it wastes memory. You should be careful with memory returned by `buffer_get_space_unsafe()` and `buffer_append_space_unsafe()`. This returned memory should be accessed immediately afterwards and it must not be accessed anymore after other `buffer_*()` calls, because they may reallocate the buffer and move it elsewhere in memory. Buffers always look like they're filled with NUL bytes. If you write past the end of buffer, all the inserted bytes are filled with NULs. If you shrink the buffer with `buffer_set_used_size()` and again write past the end of used size, all the old data is again gone and filled with NULs. If you for some reason want to just temporarily shrink the buffer size and then change it back, you can use `buffer_set_used_size()` to grow it back to its original size (but no larger). # lib-dcrypt lib-dcrypt is component for abstracting asymmetric and symmetric cryptographic operations. It can be used for public/private key handling too. Currently we support OpenSSL backing, but it is possible to write alternative backends for dcrypt. ## ECDH algorithm ECDH (Elliptic curve Diffie-Hellman) is widely used in lib-dcrypt for both key and data storage. This algorithm is also known as [ECIES (Elliptic Curve Integrated Encryption Scheme)](https://en.wikipedia.org/wiki/ECIES). When encrypting data, we perform following steps, this is the currently used algorithm. There is also a legacy algorithm, but since that has not been used publicly, we do not describe it here. You can deduce it from the code if you want to. ### `ENCRYPT(RECIPIENT-KEY, DATA)` 1. Ensure recipient key is not point at infinity 2. Generate new keypair from same group 3. Choose ephemeral public key as `R` 4. Calculate P\=R∗RECIPIENT−KEY 5. From P\=(x,y) choose `x` as `S` 6. Generate random IV+key and HMAC seed or AAD as encryption key material 7. Use PBKDF2(mac−algorithm,S,salt,rounds) to produce IV+key, and AAD if used by cipher algorithm for encrypting the encryption key 8. Encrypt encryption key material with the values generated in step 7 9. Encrypt data using encryption key material 10. OUTPUT R, salt and encrypted data. In dcrypt-openssl.c, we use `EVP_PKEY_derive_*` for the actual derivations. ephemeral public key is exported with EC\_POINT\_point2oct in compressed form. ### `DECRYPT(PRIVATE-KEY, R, SALT, DATA)` 1. Ensure R is not point at infinity 2. Calculate P\=R∗PRIVATE−KEY 3. From P\=(x,y) choose x as S 4. Use PBKDF2(mac−algorithm,S,salt,rounds) to produce IV+key, and HMAC seed or AAD for encryption key decryption 5. Decrypt encryption key (if you are using GCM, AAD and TAG need to provided for encryption key decryption) 6. Decrypt data using encryption key 7. OUTPUT decrypted data ## Key Formats lib-dcrypt can consume keys in PEM format [RFC 1421](https://datatracker.ietf.org/doc/html/rfc1421) (with or without password), and in Dovecot's special format intended for dict storage. Dovecot's format consists from unencrypted and encrypted keys. You can encrypt keys using password or another key. There are also two version, version 1 (deprecated) and version 2 (current). Both versions support either tab or : separated fields. ECC keys are stored always in compressed form. Version 1 format is not described as it's deprecated and should not be used. ### Version 2 Format ``` public key id: HEX(SHA256(public key in DER format)) key data: RSA: i2d_PrivateKey ECC: BN_bn2mpi using compressed form XD: Public key bits public key: 2:HEX(public key in DER format):public key ID private key (unencrypted) : 2:key algo oid:0:key data:public key ID private key (encrypted, key) : 2:key algo oid:1:symmetric algo:salt:digest algo (for pbkdf2):rounds:encrypted key data:ephemeral public key:digest of encryption key:public key ID private key (encrypted, pwd) : 2:key algo oid:2:symmetric algo:salt:digest algo (for pbkd2f):rounds:encrypted key data:public key ID ``` ## Flags Currently supported flags are: - 0x01 - Use HMAC for data integrity - 0x02 - Use AEAD for key and data integrity - 0x04 - No data integrity verification - 0x08 - Encrypted using obsolete version 1 algorithm - 0x10 - Use same cipher algorithm for key and data Added: 2.4.0 ## File Format This library can also generate encrypted files that are encrypted using asymmetric key pair. File encryption can be done using whatever algorithm(s) the underlying library supports. For integrity support, either HMAC based or AEAD based system is used when requested. File format 2 is described below ``` ----- header ----- 000 - 008 CRYPTED\x03\x07 (MAGIC) 009 - 009 \x02 (VERSION FIELD, 2) 010 - 013 MSB flags 014 - 017 MSB total header length (starting from 000) 018 - cod cipher oid in DER format cod - mod MAC algorithm oid in DER format mod - +4 MSB PBKDF2 rounds +5 - +8 MSB length of key data +9 - +9 number of key blocks ----- key block ----- +10 - +10 key type (1 = RSA, 2 = EC) +11 - +43 public key id (SHA256 of public key in DER format, point compressed) +44 - +48 MSB length of ephemeral key +49 - epk ephemeral key epk - +4 MSB length of encryption key +4 - ek encrypted key[+ TAG when AEAD used] ek - +4 MSB length of checksum +4 - chk checksum of the payload ----- end of key block (this can then repeat) ----- eokb - +4 MSB length of encryption key hash +4 - ekh encryption key hash ekh - (eof-macl) payload data macl - eof message integrity tag ``` ## Decryption Script There is a small script for decrypting these files, see [`dcrypt-decrypt.py`](https://github.com/dovecot/tools/blob/main/dcrypt-decrypt.py). ### Dependencies First fetch the script by cloning the [dovecot-tools](https://github.com/dovecot/tools/) repository or fetching the single tool. INFO To clone the repository: sh ``` git clone https://github.com/dovecot/tools ``` To fetch only the script (e.g. by using `wget`): sh ``` wget https://raw.githubusercontent.com/dovecot/tools/refs/heads/main/dcrypt-decrypt.py ``` Setup a local python environment (optional) and install the necessary dependencies. sh ``` # Optional but recommended: Create a virtual environment for the necessary # python dependencies to run this script. python -m venv venv source venv/bin/activate # Install the dependencies. pip install asn1 cryptography ``` ### Operation Probably the most common installation uses the [mail-compress plugin](/main/core/plugins/mail_compress.html) and [mail-crypt plugin](/main/core/plugins/mail_crypt.html) plugins for compression and encryption respectively. See also [Mail Crypt Plugin: fs-crypt](/main/core/plugins/mail_crypt.html#fs-crypt) for this setup. To simulate this scenario consider storing an input file `$in_file` using: - `$private_key_path` and `$public_key_path` - the private and public key files to allow encryption with, - `$prefix` - the path prefix to use for storing the output file, - `$out_file` - the file path to store the file as. sh ``` doveadm fs put compress \ gz:6:crypt:private_key_path=$private_key_path:public_key_path=$public_key_path:posix:prefix=$prefix \ $in_file $out_file ``` Using the `dcrypt-decrypt` script you can then retrieve the key and file information using the following command. Make sure to supply the same `$private_key_path`, `$prefix` and `$out_file` as above: sh ``` dcrypt-decrypt.py -i -k $private_key_path -f ${prefix}${out_file} | gunzip ``` INFO The piped `gunzip` will ensure the contents are decompressed after decryption. ### Output Description Run `dcrypt-decrypt.py --help` to read the tool's help text, which gives hints about its supported flags and its output. Supplying the private key file via the `-k`/`--key` flag will decrypt the encrypted file contents. INFO Fields that contain binary data are represented in hexadecimal form. Other fields are converted or displayed in human readable form. #### File information The `-i`/`--info` flag outputs a list of fields of the supplied file: `Version` What version of the dovecot [Key Formats](/main/developers/design/dcrypt.html#key-formats) has been used to encrypt this file. `Flags` What [Flags](/main/developers/design/dcrypt.html#flags) have been used when encrypting the file. `Header length` Number of bytes in the header. `Cipher algo` Name and Object Identifier (OID) of the cipher used for encrypting the data. If the same cipher algorithm [flag](/main/developers/design/dcrypt.html#flags) is set this algorithm is used to encrypt the encryption key material as well. By default the encryption key material is encrypted with `AES256-CTR`. `Digest algo` Name and OID of the digest algorithm used for the key and data. Key(s) used for encrypting the file `Key type` Either `EC` for Elliptic-Curve or `RSA` for Rivest-Shamir-Adleman key type. `Key digest` Hash of public key id in Distinguished Encoding Rules (DER) format. `Peer key` Ephemeral key used to derive shared secret used for key material encryption. `Encrypted` The encrypted encryption key material. `Kd hash` The checksum of the key data. #### Key information The following information are only available if the `-k`/`--key` flag was supplied with the matching key. `Provided key` Checksum of the key that was provided. ##### Key derivation data `Key derivation` General information about key derivation for the [Key Encapsulation Mechanism](https://en.wikipedia.org/wiki/Key_encapsulation_mechanism) to decrypt the encryption key. With its attributes: `Rounds` Number of iterations that the digestion algorithm is repeated. `Secret` Decrypted secret from the peer key. `Salt` Salt used for hashing. `Peer key` is used here. ##### Encryption and Decryption data `Encryption key decryption` General information regarding the payload decryption. This uses the decapsulated random data to derive the key for decrypting the payload. `Decryption` General information regarding the key used to encrypt the data. Both sections contain: `Key` In the Encryption section: Ephemeral key material. In the Decryption section: Decryption key material. `IV` The initialization vector from derived or deciphered key material. Decryption additionally contains: `AAD` Additional data used to authenticate the data. `TAG` Tag used to verify the message integrity. # Dovecot Dict Protocol INFO This document describes Dovecot Dict protocol v4. Dovecot's dict protocol is a line based protocol between the dict client and server processes. The dict server receives lines containing commands and responds to the client with results. Each line ends with an LF character and the maximum line the client is allowed to send a command is 65536 (64KB of data). There is no maximum length enforced for dict server's response. Each command is in format: ``` ``` The command short name is a single character and the rest of the line containing the command parameters are followed immediately without any whitespace in between. Commands available in dict protocol and their corresponding short name: | Command | Command Short Name | | --- | --- | | `HELLO` | `H` | | `LOOKUP` | `L` | | `ITERATE` | `I` | | `BEGIN` | `B` | | `COMMIT` | `C` | | `ROLLBACK` | `R` | | `SET` | `S` | | `UNSET` | `U` | | `ATOMIC_INC` | `A` | | `TIMESTAMP` | `T` | The reply from dict server is in the same format. ``` () ``` Result statuses relevant for each command and their corresponding short name explained in each command section. Command parameters and extra result values are tab-escaped using the dovecot's generic tab-escaping. Escaping is described in more details in [auth protocol](/main/developers/design/auth_protocol.html). After responses to certain commands, an execution duration summary line is also sent to the client containing timestamp information about how long the command took to finish. The commands that trigger this timestamp response are `LOOKUP`, `ITERATE`, and `COMMIT`. Format of this line is as follows: ``` TABTABTABTAB ``` Where the unix timestamp is `.` for each pair. The connection starts by the client sending a `HELLO` message. The message contains the initial handshake information. ``` C: HTABTABTABTAB ``` INFO In older dict protocol versions, `user` was included in the initial handshake but it's currently not used and the field is empty. The server validates the client's protocol version and checks that the requested dictionary is configured. If validation fails (unsupported major version, unconfigured dictionary name, or malformed message), the server closes the connection. Otherwise the server accepts the handshake and the client proceeds to send commands immediately — no response is sent by the server for the `HELLO` command. Currently, Dovecot's client and server check that they support the same major version number. Minor version is not currently checked and can differ between client and server. Other dict commands and their line format is described as follows. ## `LOOKUP` Command Used to lookup a key value from dict. ``` C: LTAB ``` | Result Status | Status Short Name | Description | | --- | --- | --- | | `OK` | `O` | Lookup was performed successfully and there was a single value for the key. Value is then appended to the response line. | | `MULTI_OK` | `M` | Lookup was successful and returned multiple values. Each value is tab-escaped individually, then joined with tabs. The entire joined string is then tab-escaped once more so it appears as a single parameter in the protocol. To recover the values, the client must first unescape the parameter to restore the tab-separated structure, then split the result by tabs and unescape each individual value. | | `NOTFOUND` | `N` | Lookup was performed successfully but no value was found with this key. | | `FAIL` | `F` | Lookup failed due to an error. A tab-escaped error string is appended to the response line. | ## `ITERATE` Command Used to iterate over a key path. ``` C: ITABTABTAB ``` The iteration flag is a integer with following options, which can be `OR`ed together. | Flag | Value | Description | | --- | --- | --- | | `RECURSE` | `0x01` | Recurse to all the sub-hierarchies | | `SORT_BY_KEY` | `0x02` | Sort returned results by key | | `SORT_BY_VALUE` | `0x04` | Sort returned results by value | | `NO_VALUE` | `0x08` | Don't return values, only keys | | `EXACT_KEY` | `0x10` | Don't recurse at all. This is basically the same as LOOKUP command but it will return all of the rows instead of only the first one. | | `FLAG_ASYNC` | `0x20` | Perform iteration asynchronously | Possible responses include: | Result Status | Status Short Name | Description | | --- | --- | --- | | `OK` | `O` | Iteration was done successfully. Note that for each key-value pair A separate OK status is appended to the response line with the key following immediately and all values associated to it joined with tabs. | | `FAIL` | `F` | Iteration failed due to an error. A tab-escaped error string is appended to the response line. | | `ITER_FINISHED` | `` | Iteration completed. This is sent after all key value pairs are added to the response. | ## `BEGIN` Command Begins a dict transaction with the given ID. ``` C: BTAB ``` Note that transactions commands don't have replies with the exception of `COMMIT`. After a transaction is successfully started, transaction operations i.e. `SET`, `UNSET`, `ATOMIC_INC`, and `TIMESTAMP` can be used. ## `COMMIT` Command Commit the transaction corresponding to the given ID. ``` C: C ``` Note that the transaction is looked up from the ID so no username is required. Possible responses include: | Result Status | Status Short Name | Description | | --- | --- | --- | | `OK` | `O` | Commit was done successfully and all transaction operations completed. | | `NOTFOUND` | `N` | Transaction ID specified in the commit was not found. | | `WRITE_UNCERTAIN` | `W` | The transaction may or may not have succeeded (e.g. a write timeout occurred or the connection to dict driver was closed by the backend server). A tab-escaped error string is appended to the response line. | | `FAIL` | `F` | Iteration failed due to an error. A tab-escaped error string is appended to the response line. | ## `ROLLBACK` Command Rollback changes made in the transaction. ``` C: R ``` ## `SET` Command Set a value for the given key. ``` C: STABTAB ``` ## `UNSET` Command Unset a record in the dictionary given the key. ``` C: UTAB ``` ## `ATOMIC_INC` Command Atomically increment numeric value of a key with the given increment value. Note that the value is changed when transaction is being committed, so it's not known beforehand what the value will become. The value is updated only if it already exists. ``` C: ATABTAB ``` ## `TIMESTAMP` Command Set the unix timestamp for the given transaction. Note that this will set the timestamp for the entire transaction. This must be set before any changes are done and can't be changed afterwards. Currently only dict-sql with Cassandra driver does anything with this. ``` C: TTABTAB ``` # Doveadm Protocol TIP See also: - [Doveadm HTTP API](/main/core/admin/doveadm.html#http-api). doveadm-server can be accessed via UNIX sockets or TCP protocol (by adding `inet_listener` to doveadm service). The doveadm server supports the following protocol versions (major.minor): - `1.0`: Supported since Dovecot v2.0.0. - `1.1`: Supported since Dovecot v2.2.33. Using this minor number changes the protocol to use multiplex-stream for proxying logs. - `1.2`: Supported since Dovecot v2.3.9. Indicates that the server supports the STARTTLS command. For external doveadm clients it's easiest to just support the 1.0 version. ## Protocol Overview Initial handshake from client to server: ``` C: VERSIONdoveadm-server10 ``` All the fields are TAB-separated. The server will send you back either: - `+` means you are preauthenticated and can start sending commands. This happens when connecting to the UNIX socket. - `-` means you need to authenticate first. TIP As of Dovecot v2.2.34, Dovecot sends the "+" or "-" only after `VERSION`, while earlier versions sent it already before `VERSION`. This shouldn't change much practically, because the client was supposed to have sent the `VERSION` immediately anyway. ## Authentication The authentication is done with a regular SASL PLAIN authentication, i.e. `PLAINbase64(\0username\0password)`. Currently the username must be `doveadm`. For example for user=doveadm, password=secret use: ``` C: PLAINAGRvdmVhZG0Ac2VjcmV0 S: + ``` ## Running Commands The actual commands are in format: `flagsusernamecommand name[parameter[parameter2...]]`, where the flags can be either empty, "v" (verbose) or "D" (debug). Note that if the command name has spaces, they are sent as spaces instead of as tabs (e.g. "quota get", not `quotaget`). So for example to get a quota for user tss: ``` C: tssquotaget S: user STORAGE 1814 - 0 user MESSAGE 6 - 0 S: + ``` The storage values are all given in kilobytes. The server replies using the same fields TAB-separated as what a regular doveadm command sends. The reply itself ends with LF. So if the reply is large, it may return a very long line as a reply. After the reply follows a status line: - `+` = success. - In future the `+` may be followed by more text, for now you should just ignore those. - `-` = failed. The error was probably logged to Dovecot's error log. The `-` may be directly followed by an error code: - `NOUSER`: The user doesn't exist. - `TEMPFAIL`: Temporary failure. - `NOPERM`: Permission denied. - `PROTOCOL`: Protocol-related error. - `DATAERR`: Input data (e.g. command parameters) were wrong. - `NOTFOUND`: The command didn't find the requested object. ## Available Commands The command names and output are exactly the same as what regular doveadm commands on command line do. Earlier Dovecot versions supported running only "mail commands" (commands with -u username parameter), but v2.3 should support all commands. You can use the doveadm itself to find out what the output format will look like. For example: sh ``` doveadm -f tab search mailbox inbox 1:2 mailbox-guid uid fa8cb722dfad9c52b62600007049b30b 125159 fa8cb722dfad9c52b62600007049b30b 125160 ``` There are two fields, "mailbox-guid" and "uid" in the output. The title names won't be sent via doveadm protocol, but everything else will be sent in one line. So in the above case the protocol output will be: ``` fa8cb722dfad9c52b62600007049b30b125159fa8cb722dfad9c52b62600007049b30b125160 ``` ## Multiplex Stream Multiplex streaming is enabled if client sends protocol minor version `1` or higher: ``` C: VERSIONdoveadm-server11 ``` After authentication has successfully finished (server returned `+`), the client must switch reading and writing to the "multiplex mode". This protocol works by sending packets: - 1 byte: Channel ID - 4 bytes: Following data length in big-endian - Data The channel ID is: - 0: doveadm protocol - 76 ('L'): Logs sent by doveadm-server ## STARTTLS If doveadm-server returns minor version `2` or higher, it supports the STARTTLS command. This command can be sent only if the server hasn't already pre-authenticated the client, because it's running on a trusted UNIX socket. So when the doveadm-server returns the `-` line indicating authentication is needed, the doveadm-client can send `STARTTLS` line. After this both the client and server switch to TLS. There is no response to this command. ## Example Clients Perl: [Net::Doveadm](https://metacpan.org/pod/Net::Doveadm) # Dsync Design DANGER This describes the design for v2.0/v2.1. The v2.2 design is somewhat different. ## Two-way Synchronization dsync attempts to preserve all changes done by both sides of the synced mailboxes. ## Mailbox List Mailboxes have 128 bit globally unique IDs, which are used for figuring out when two mailboxes should actually be synchronized. This solves two major problems: - If mailbox has been renamed in one side, dsync finds it because its GUID hasn't changed. - If mailbox has been deleted and recreated, dsync doesn't attempt to sync it because it's a different mailbox. Then there's the problem of how to correctly sync mailbox renames and deletions. How do you know which side of the sync has the most recent name for the mailbox? How do you know if one side had deleted mailbox, or if the other side had created it? To solve these problems, Dovecot v2.0 created a "mailbox log", which adds a record with mailbox GUID and timestamp whenever mailbox is renamed or deleted. So: - If mailbox has different names on two sides, its "last renamed" timestamp is looked up from the mailbox list index. The side with the most recent timestamp is assumed to contain the newer name and the other side's mailbox is renamed to it. - If neither side has a "last renamed" timestamp, one side is picked. This shouldn't happen, except when mailbox log is deleted for some reason or if the renaming is done outside Dovecot. - If mailbox exists only on one side, the other side checks if mailbox log contains a delete record for its GUID. If there is one, the mailbox is deleted from the other side. If there's not, the mailbox is created and synced. - Subscriptions and unsubscriptions are synced in a similar way. But because it's possible to be subscribed to nonexistent mailboxes, mailbox log can't contain mailbox GUIDs for them. Instead the first 128 bits of SHA1 of mailbox name are used. Collisions for mailbox names are highly unlikely, but even if one happens, the worst that can happen is that user gets unsubscribed from wrong mailbox. dsync writes timestamps to changelog using the original timestamps, so that dsync's changes won't override changes done by user during sync. ## Mailbox When saving new mails, dsync preserves all of their immutable state: - GUID - Received date - Save date - Message contents It also attempts preserve IMAP UID. This works as long as the other side hasn't already used the UID for another mail. If it has, dsync doesn't attempt to preserve the UID, because an IMAP client might have already seen the UID and cached another mail's contents for it. IMAP requires that message's contents must never change, so UIDs can't be reused. So whenever an UID conflict happens, dsync gives messages in both sides a new UID, because it can't know which message the client had seen, or perhaps user used two clients and both saw a different message. The mutable metadata that dsync preserves is: - Message flags and keywords - Modification sequences (modseqs) Flags and keywords are synced based on modseqs. Whichever side has a higher modseq for the message, its flags and keywords are synced to the other side. Currently there's no per-flag or per-keyword synchronization, so that if one side had added \\Seen flag and other side had added \\Answered flag, one of them would be dropped. ## Finding What to Sync dsync can run in full mode or fast mode. Full mode means it goes through all messages in all mailboxes, making sure everything is fully synchronized. In fast mode it relies on uidvalidity, uid-next and highest-modseq values to find out changes. If any of the values changed, the mailbox is included in sync. WARNING A superfast mode should still be implemented, where once a mailbox is selected for syncing, it should sync only mails whose modseq is higher than a given one. This would improve performance and network traffic with large mailboxes. ## Copy Optimizations Before dsync actually starts syncing anything, it first fetched a list of all to-be-synced messages and adds them to a GUID -> message hash table. Whenever dsync needs to sync a new message to the other side, it first checks if the message's GUID already exists on the other side. If it does, it starts a message copy operation instead of a full save. It's possible that this copy operation fails if the message just gets expunged from the other side, so there needs to be fallback handling for this. If the message exists in multiple mailboxes, a copy from the next mailbox is attempted. If all of them fail, dsync fallbacks to saving the message. WARNING This optimization currently works only in full sync mode. If this were to work in fast sync mode, the full mailbox list would have to be looked up from local side. And this would slow it down.. # Events Design See [Events Design](/main/core/admin/events.html) first for an overview of the design. ## Sending Events Events are sent by logging it. Any `e_debug()`, `e_info()`, `e_warning()` or `e_error()` call will also send the event, which may be redirected to the stats process. Often events that are intended for statistics are sent using the `e_debug()` call. The event can be sent to statistics even if it's not actually logged. Avoid sending events excessively. WARNING An `e_debug()` call every time connection reads or writes something will likely result in a huge amount of unnecessary debug logging. ## Event Names Events that are expected to be used in statistics should have a name. Be consistent when naming the events. The name's prefix should be the subsystem that is logging the event. Usually this would be the primary category of the event. Example: IMAP related events should begin with `imap_` and mailbox related events begin with `mailbox_`. The name should consist of only `[a-z]`, `[0-9]` and `_` characters. Current naming conventions for name suffixes: - `_connected` (for connections) - `_disconnected` (for connections) - `_finished` (when some operation finishes, e.g. IMAP command or HTTP request) - e.g. `http_request_finished`, `dns_request_finished`, `imap_command_finished` - This should be used regardless of whether the operation succeeded or failed. The details would be in fields. - `_retried` (if an operation is internally retried one or more times before it's finished) ## Category Naming The same category name must not be duplicated within the process. This is because event handling is optimized and performs category checking by comparing the categories' pointers, not names' strings. (Then again, if the struct `event_category` variable names were consistent, you'd get duplicate symbol errors from linker as well.) Be careful naming events that go through client and server boundaries. Example: if both `lib-dns` and dns service use `dns` as their category and also have identically named `dns_lookup` event, there's no easy way to differentiate in event filters between these two. So a statistics filter could end up counting each DNS lookup twice. Since it's more difficult to remember to check for event naming conflicts, it would be safer to use different category names entirely. The category name should consist of only `[a-z]`, `[0-9]` and `-` characters. `:` is also used as a special case in `service:`, but it shouldn't be used for naming new categories. ### Current Naming Conventions - The name should consist of only `[a-z]`, `[0-9]` and `_` characters. - Timestamps should have `_time` suffix - Durations should have `_usecs` suffix and be in microseconds. - Try to avoid adding extra duration fields for most events. There's the automatic `duration` field already that contains how long the event has existed. So usually the event lifetime should be the same as the wanted duration field. - Incoming TCP/IP connections should have `remote_ip`, `remote_port`, `local_ip`, and `local_port` fields - Outgoing TCP/IP connections should have `ip` and `port` for the remote side. - For local side (bind address) `client_ip` and `client_port` may optionally be used WARNING These are all different from incoming connection's IP/port fields. This is because often everything starts from an incoming connection, which will be used as the root event. So we may want to filter e.g. outgoing HTTP events going to port 80 which were initiated from IMAP clients that connected to `port 993` `(port=80 local_port=993)` - Connection reads/writes should be counted in `net_in_bytes` and `net_out_bytes` fields - These fields are usually easiest updated with `event_add_int(event, net_in_bytes, istream->v_offset)` and `event_add_int(event, net_out_bytes, ostream->offset)`. If iostreams aren't used, `event_inc_int()` maybe be easier. - (Local) disk reads should have `disk_read` and `disk_write` fields - With remote filesystems like NFS it may be difficult to differentiate between disk IO and network IO. Generally the `disk_read/write` should be used for `POSIX read()` and `write()` calls from filesystem. - Counting only `read()s` and `write()s` doesn't necessarily translate to actual disk IO since it may only be accessing the kernel page cache. Still, this may be useful. - There is a lot of disk IO performed all over the code, so Dovecot will likely never include events for all disk reads/writes. - `error=`: The operation failed. The `` may be simply `y` or contain more details. This field shouldn't exist at all for successful operations. - `error_code=`: Machine-readable error code for a failed operation. If set, the `error` field must also be set. TIP Events shouldn't be sent every time when receiving/sending network traffic. Instead, the `net_in/out_bytes` fields should be updated internally so that whenever the next event is sent it will have an updated traffic number. Generally it's not useful for events to be counting operations. Rather each operation should be a separate event, and the statistics code should be the one counting them. This way statistics can only be counting, e.g., operations with `duration > 1 sec`. If the statistics code was seeing only bulk operation counts this wouldn't be possible. The `net_in/out_bytes` and such fields are more of an exception, because it would be too inefficient to send individual events each time those were updated. TIP Even though internally updating a field for an event's parent will be immediately visible to its children, the update won't be automatically sent to the stats process. We may need to fix this if it becomes a problem. Field inheritance may become problematic also when multiple nested ioloops are used. For example an outgoing imapc connection could receive a reply, which synchronously triggers an outgoing quota SQL connection. The quota SQL connection's parent event likely shouldn't be the imapc connection's event, because otherwise they could be mixing the `IP/port fields` and perhaps others. This isn't necessarily a problem though, but this is why when connection.c performs outgoing UNIX socket connection it clears the IP/port fields to make sure they don't exist for the connection event due to inheritance from a parent event. ## Passthrough Events Passthrough events' main purpose is to make it easier to create temporary events as part of the event parameter in `e_error()`, `e_warning()`, `e_info()`, or `e_debug()`. These passthrough events are automatically freed when the `e_*()` call is finished. Because this makes the freeing less obvious, it should be avoided outside `e_*()'s` event parameter. A passthrough event's creation timestamp is the same as the parent event's timestamp, because its intention is to only complement it with additional fields. This way the generated event `duration` field is preserved properly. The passthrough events also change the API to be more convenient towards being used in a parameter. Instead of having to use: ``` event_add_str(event_set_name(event_create(parent), "name"), "key", "value") ``` The event\_passthrough API can be a bit more readable as: ``` event_create_passthrough(parent)->set_name("name")->add_str("key", "value")->event(). ``` The passthrough event is converted to a normal event at the end with the event() call. TIP This API works by modifying the last created passthrough event, so it's not possible to have multiple passthrough events created in parallel. ## Log Prefixes Events allow replacing the current log prefix or appending to it. This way for example opening a mailbox can add a `Mailbox : prefix` and then `use e_debug(box->event, ...)` without having to specify the mailbox name in every log message. ## Global Events Sometimes there's not really any specific event that a log message would belong to, or it would be difficult to transfer the event there. In these cases the old `i_debug()`, `i_info()`, `i_error()`, etc. logging calls can still be used. These will be using the global event and its logging prefix. The global events are pushed/popped in a stack. For example with IMAP the initial global event is the user's event. During IMAP command execution the global event is the IMAP command event. # Mail Index Cache ![Index Cache](/main/assets/mail-index-cache.CxshI-h3.png) INFO See Also: - [Mail Indexes](./index_format.html) for an overview of what the cache does. ## Client Types IMAP clients can work in many different ways. There are basically 2 types: 1. Online clients that ask for the same information multiple times (e.g. webmails, Pine) 2. Offline clients that usually download first some of the interesting message headers and only after that the message bodies (possibly automatically, or possibly only when the user opens the mail). Most non-webmail IMAP clients behave like this. Cache file is extremely helpful with the type 1 clients. The first time that client requests message headers or some other metadata they're stored into the cache file. The second time they ask for the same information Dovecot can now get it quickly from the cache file instead of opening the message and parsing the headers. For type 2 clients the cache file is also somewhat helpful if client fetches any initial metadata. Some of the information is helpful in any case, for example it's required to know the message's virtual size when downloading the message with IMAP. Without the virtual size being in cache Dovecot first has to read the whole message first to calculate it, which increases CPU usage. Only the specified fields that client(s) have asked for earlier are stored into cache file. This allows Dovecot to be adaptive to different clients' needs and still not waste disk space (and cause extra disk I/O!) for fields that client never needs. Although this behavior is configurable with `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)`\], `[mail_always_cache_fields](/main/core/summaries/settings.html#mail_always_cache_fields)`, and `[mail_never_cache_fields](/main/core/summaries/settings.html#mail_never_cache_fields)` settings. Dovecot can cache fields either permanently or temporarily. Temporarily cached fields are dropped from the cache file after about a week. Dovecot uses two rules to determine when data should be cached permanently instead of temporarily: 1. Client accessed messages in non-sequential order within this session. This most likely means it doesn't have a local cache. 2. Client accessed a message older than one week. These rules might not always work optimally, so Dovecot also re-evaluates the caching decisions once in a while: - When caching decision is YES (permanently cache the field), the field's last\_used is updated only when the caching decision has been verified to be correct. - When caching decision is TEMP, the last\_used is updated whenever the field is accessed. - When last\_used becomes 30 days old (or `[mail_cache_unaccessed_field_drop](/main/core/summaries/settings.html#mail_cache_unaccessed_field_drop)`) a YES caching decision is changed to TEMP. - When last\_used becomes 60 days old (or 2 \* `[mail_cache_unaccessed_field_drop](/main/core/summaries/settings.html#mail_cache_unaccessed_field_drop)`) a TEMP caching decision is changed to NO. ## File Format The cache file format is: - Base header (`struct mail_cache_header`). - List of `struct mail_cache_header_fields` or `struct mail_cache_record`. After the base header it can't be assumed what the rest of the file contains. Everything after it must be accessed via provided file offsets. The list of cached fields exists in `struct mail_cache_header_fields`. The initial offset to it is in `mail_cache_header.field_header_offset`. The following updates are written to `mail_cache_header_fields.next_offset`, so the header reading must follow this linked list to the end. The cache records (`struct mail_cache_record`) are accessed via offsets in the "cache" extension of main index. The offset points to the newest cache record that was written to the mail. There can be multiple "continuation records", which can be accessed via `mail_cache_record.prev_offset`. When searching for a cache record for a mail, search through this whole linked list. If a wanted field is found, it's not necessary to search for other instances of it. If there are any other instances, they're just identical duplicates. The cache record has a size describing its full size, followed by a list of (field type, field specific data) until the cache record size is reached. Cache file was designed to be storing only immutable data. The current implementation doesn't support modifying existing data, although in theory this could be possible. If this is really required, it's possible to drop cache for a specific mail entirely and then re-add it. See `lib-index/mail-cache-private.h` in the source code for details about these structs. ## Field Types Specified in `enum mail_cache_field_type`. ### `MAIL_CACHE_FIELD_FIXED_SIZE` Fixed size cache field. The size is specified only in the cache field header, not separately for each record. ### `MAIL_CACHE_FIELD_VARIABLE_SIZE` Variable sized binary data. ### `MAIL_CACHE_FIELD_STRING` Variable sized string. There is no difference internally to how `MAIL_CACHE_FIELD_VARIABLE_SIZE` is handled, but it helps at least `[doveadm dump](/main/core/summaries/doveadm.html#dump)` to know whether to hex-encode the output. ### `MAIL_CACHE_FIELD_BITMASK` A fixed size bitmask field. It's possible to add new bits by updating this field. All the added fields are ORed together. ### `MAIL_CACHE_FIELD_HEADER` Variable sized message header. The data begins with a 0-terminated `uint32_t line_numbers[]`. The line number exists only for each header, header continuation lines in multiline headers don't get listed. After the line numbers comes the list of headers, including the "header-name: " prefix for each line, LFs and the TABs or spaces for continued lines. See `global_cache_fields[]` in `lib-storage/index/index-mail.c` for the list of all fields stored in the cache file. ## Reading and Writing Because cache file is typically used in potentially long-running operations, such as with IMAP command `FETCH 1:* (BODY.PEEK[] ENVELOPE BODYSTRUCTURE)` it's important that updating the cache file doesn't block out any other readers. Also because the readers are often also writers (if something isn't cached, it's added there), it's important that they don't block writers either. The simplest solution for this is that reading requires no locking, and write locks are also very short-lived. The cache writing is currently done by first gathering all the cache changes into a buffer in memory. Once the buffer grows large enough, the changes are written to the cache file. There is currently nothing to prevent two processes from concurrently writing the same cached data twice to dovecot.index.cache. Because the data written to the cache file are really just cached data, the fields' contents are identical. Having the data exist twice (or even more times) means wasting some disk space, but otherwise it isn't a problem. The duplicates are dropped the next time the file is purged (recreated). Details of writing to cache file: - Most of the data is only appended to it. - Header is overwritten to update fields: - Number of messages - Number of already expunged messages that have cache content - Number of cache continuation records - Cache file is recreated once there are too many expunged messages or cache continuation records. - List of cache fields is written as a separate "cache fields" header. Each time a new field is added, a new cache fields header is appended to the file. The previous cache fields header's next\_offset is updated to point to the new header's offset. - The cache fields header can also be updated directly to update cache decisions and "last used" timestamps. Writing to an existing `dovecot.index.cache` file is done by simply locking it. Purging (= recreating) the cache requires also having the `dovecot.index.log` locked first. There are some issues with lockless reading: - Because header can be rewritten, the fields can't be fully trusted. It's possible that reading can read only a partially updated header. This is unlikely though, and the important fields aren't modified anyway. The worst that can happen is that a cache file becomes purged earlier than intended. - The `mail_cache_header_fields.next_offset` field can become updated, but this is written using [Lockless Integers](./index_format.html#lockless-integers) which guarantees that the offset can be trusted to be either fully updated or nonexistent. - However, whenever writing to these cache headers, they need to be re-read after locking to make sure broken data won't be written back. ## Cache Decisions Dovecot tries to be smart about what it keeps in the cache file. If the client never fetches the cached data, it's just waste of disk space and disk I/O. Normally Dovecot changes the decisions based on what fields are fetched and for what messages. A specific decision can be forced by ORing it with `MAIL_CACHE_DECISION_FORCED`. ### `MAIL_CACHE_DECISION_NO` This field isn't cached currently. ### `MAIL_CACHE_DECISION_TEMP` This field is cached for new mails. ### `MAIL_CACHE_DECISION_YES` This field is cached for all mails. # Main Index The main index can be used to quickly look up messages' UIDs, flags, keywords and extension-specific data, such as cache file or mbox file offsets. INFO See Also: - [Mail Indexes](./index_format.html) for a more detailed overview. ## File Format The main index is only updated by recreating the whole file. An existing file is never modified. All the changes to the index go through the [Transaction Log](./index_log.html). The main index file format is: - Base header (`struct mail_index_header`). - Extension headers (`struct mail_index_ext_header`) - Message records (`struct mail_index_record`) including extension records. See `lib-index/mail-index.h` and `lib-index/mail-index-private.h` in the source code for details about these structs. ## Extension Headers Extensions allow adding extra data into index header and/or message records. For example cache file offsets are stored as extensions. See [Transaction Log](./index_log.html) for more details about extensions. The extensions are listed in the main index file after the base header. The first extension begins from `mail_index_header.base_header_size` offset. The second begins after the first one's `data[]` and so on. The extensions are always 64bit aligned, so the reader may need to skip a few bytes to get to the proper alignment offset. Read the extensions as long as the offset is smaller than `mail_index_header.header_size`. ## Message Records There are `mail_index_header.messages_count` records in the file. The size of each record is `mail_index_header.record_size`. Each record contains at least two fields: Record UID and flags. The UID is always increasing for the records, so it's possible to find a record by its UID with binary search. The flags are a combination of `enum mail_flags` and `enum mail_index_mail_flags`: - 0x01 - `MAIL_ANSWERED` - `\Answered` IMAP system flag - 0x02 - `MAIL_FLAGGED` - `\Flagged` IMAP system flag - 0x04 - `MAIL_DELETED` - `\Deleted` IMAP system flag - 0x08 - `MAIL_SEEN` - `\Seen` IMAP system flag - 0x10 - `MAIL_DRAFT` - `\Draft` IMAP system flag - 0x20 - `MAIL_INDEX_MAIL_FLAG_UNUSED` - This used to contain the `\Recent` IMAP system flag, but is always unset in current index files. This could be reused for other purposes. - 0x40 - `MAIL_INDEX_MAIL_FLAG_BACKEND` - For private use by backend. - 0x80 - `MAIL_INDEX_MAIL_FLAG_DIRTY` - Message flags haven't been successfully written to backend mailbox. This is used for example with mbox and `mbox_lazy_writes=yes`. It also allows having modifiable flags for read-only mailboxes. Alternatively, with some mailbox formats this flag can be used in a backend-specific way. The rest of the data is stored in record extensions. ## Keywords The keywords are stored in "keywords" named extension, where the keyword names are listed in the extension header and stored as bitmask in the extension records. So the nth bit in the bitfield points to the nth keyword listed in the header. For better performance and lower disk space usage in transaction logs, the keywords extension is more tightly integrated to the index file code than other extensions. The list of keywords is stored in "keywords" extension header: c ``` struct mail_index_keyword_header { uint32_t keywords_count; /* struct mail_index_keyword_header_rec[] */ /* char name[][] */ }; struct mail_index_keyword_header_rec { uint32_t unused; /* for backwards compatibility */ uint32_t name_offset; /* relative to beginning of name[] */ }; ``` The `mail_index_keyword_header_rec` records (and `keywords_count`) are actually unnecessary. They were used to optimize reading only newly added keywords since the last sync, but that happens rarely. Also nowadays this would be noticed by reading the change from `dovecot.index.log` instead of re-reading the `dovecot.index` header. So at some point the "keywords" extension should be replaced with a new "keywords" extension that no longer has these records. It's not currently possible to safely remove existing keywords. ## Extension Records The extensions only specify their wanted size and alignment in the mail records. The index file syncing code is free to assign any offset inside the message record to them. The extensions may be reordered at any time. Dovecot's current extension ordering code works pretty well, but it's not perfect. If the extension size isn't the same as its alignment, it may create larger message records than necessary. To guarantee aligned access to an extension, the message record size is always divisible by the maximum alignment requirement. See [Transaction Log](./index_log.html) for more details about extensions. # Mail Index File Format ![Mail Index Log](/main/assets/mail-index-log.DWbr84QZ.png) Mail index files consist of: - [Main Index File](./index_format_main.html) (`dovecot.index`) - [Transaction Log](./index_log.html) (`dovecot.index.log` and `dovecot.index.log.2`) - [Cache File](./index_cache.html) (`dovecot.index.cache`) See also [Mail Index API](./mail_index_api.html). The mail index files are used in a few different places: - Mailbox indexes (`dovecot.index*`) - [`mailbox_list_index`](#mailbox-list-index) (`dovecot.list.index*`) - mdbox map index (`dovecot.map.index*`) The mailbox index is optional for some mailbox formats (maildir, mbox), but required for all high performance mailbox formats (sdbox, mdbox). The index files were implemented to optimize Dovecot, so the file formats attempt to be efficient. The index files are often mmap()ed into memory and accessed directly via structs. This means that the data is stored using the CPU endianness, and all structs that end up in index files have to be careful with data alignment to avoid crashes with CPUs that require the alignment. At times there have been thoughts about changing index handling so it wouldn't care about CPU endianness or alignment, but this would be a huge change and the end result would almost certainly be worse performance. This is mostly a theoretical problem anyway: It's very unlikely that index files are moving between little and big endian CPUs, and if that is actually wanted the mails can be migrated with dsync. ## Main Index The main index contains fixed size records, which contain at least: - IMAP UID - Message flags (IMAP system flags) There are also optional extensions, which increase the record size: - Message keywords (custom flags) - Offset to cache file - Message sorting optimization records - Mailbox format-specific extensions The index file's header also contains some summary information, such as how many messages exist, how many of them are unseen and how many are marked with `\Deleted` flag. This allows efficiently answering the IMAP STATUS commands. The `dovecot.index` file is lazily updated by recreating it once in a while. An existing `dovecot.index` file is never written to. The transaction log file contains updates that need to be applied on top of the main index file to get to the latest state of the mailbox. See [Main Index](./index_format_main.html) for more details. ## Transaction Log Transaction log contains all the changes going to the main index (no `dovecot.index.cache` contents). It is the only file that is always required to exist for a folder. (Newly created folder indexes don't contain `dovecot.index` immediately.) New transactions are usually appended to the log file. Once the log becomes large enough, it's rotated into `dovecot.index.log.2` and a new empty log file is created. The `.log.2` file becomes deleted on the next log rotation, or earlier if the `.log.2` becomes old enough. There are several advantages to having a transaction log: - It provides atomic transactions: The transaction either succeeds, or it doesn't. For example if a transaction sets a flag to one message and removes it from another, it's guaranteed that both changes happen. - It allows another process to quickly see what changes have been made to the mailbox by other processes. For example IMAP protocol needs to send the IMAP client a list of all mailbox changes after each IMAP command. - This is also important when storing the index files in NFS or in a clustered filesystem. Instead of re-reading the whole index file after each external change, Dovecot can simply read the new changes from the transaction log and apply them to the in-memory copy of the main index. - They're also used for quickly getting changes (flag changes and expunges especially) since a specific point in time: - QRESYNC extension - doveadm sync (dsync) See [Transaction Log](./index_log.html) for more details. ## Cache File The cache file can have all kinds of cached email data, such as cached email headers. The cached data can't be changed. To prevent abuse, excessively large cache records aren't added to the cache file. Each mailbox can have its own different caching decisions. New cache fields are dynamically added as they become used. For example a user may start using a new IMAP client, which fetches some new message headers that old clients didn't want. This triggers Dovecot to start caching the newly requested header for any new mail deliveries. Similarly if some cache field isn't accessed for a while, it's dropped entirely. Fields can be cached either permanently or temporarily. The temporary fields may be dropped for mails that were saved more than 7 days ago. The idea for temporary fields is that some IMAP clients cache all the data locally, so they benefit from Dovecot's caching only once. 7 days should be long enough that the user has accessed the mailbox with all their locally caching clients. After this the cache fields are just wasting disk space unnecessarily. See [Cache File](./index_cache.html) for more details. ## Mailbox List Index `dovecot.list.index*` files are used for mailbox list indexes. It uses the same mail index format, although some fields are slightly abused to make it work. - The index header contains ID => name mapping. The name isn't the full mailbox name, but rather each hierarchy level has its own ID and name. For example a mailbox name "foo/bar" (with '/' as separator) would have separate IDs for "foo" and "bar" names. - The mail record's UID field contains is a semi-permanent identifier for the mailbox. The mailbox\_list\_index\_record extension includes name\_id and parent\_uid fields, which allow building the whole mailbox tree. parent\_uid=0 means root, otherwise it's the parent node's uid. - Message flags are used for list-specific flags (e.g. `\NoSelect`). - The mailbox\_list\_index\_record extension also contains GUID and UIDVALIDITY for selectable mailboxes. If a mailbox is recreated using the same name, its GUID also changes. Note however that the UID in `dovecot.list.index` doesn't change, because the UID refers to the mailbox name, not to the mailbox itself. - The records may contain also extensions for allowing mailbox\_get\_status() to return values directly from the mailbox list index. - Storage backends may also add their own extensions to figure out if a record is up to date. ## Lockless Integers Dovecot uses several different techniques to allow reading files without locking them. One of them uses fields in a "lockless integer" format. Initially these fields have "unset" value. They can be set to a wanted value in range 0..228 (with 32bit fields) once, but they cannot be changed. It would be possible to set them back to "unset", but setting them the second time isn't safe anymore, so Dovecot never does this. The lockless integers work by allocating one bit from each byte of the value to "this value is set" flag. The reader then verifies that the flag is set for the value's all bytes. If all of them aren't set, the value is still "unset". Dovecot uses the highest bit for this flag. So for example: - `0x00000000`: The value is unset - `0xFFFF7FFF`: The value is unset, because one of the bytes didn't have the highest bit set - `0xFFFFFFFF`: The value is (228)−1 - `0x80808080`: The value is `0` - `0x80808180`: The value is `0x80` Dovecot contains `mail_index_uint32_to_offset()` and `mail_index_offset_to_uint32()` functions to translate values between integers and lockless integers. The "unset" value is returned as 0, so it's not possible to differentiate between "unset" and "set" 0 values. # Transaction Log INFO See Also: - [Mail Indexes](./index_format.html) for an overview of the transaction log is and what its benefits are. ## File Format The transaction log is usually appended to. Once in a while the log gets rotated into .log.2 and a new .log is created. During this rotation the main index is also recreated. The log begins with a header (`struct mail_transaction_log_header`) followed by transaction records. Each transaction record begins with a header (`struct mail_transaction_header`) followed by transaction type specific content. The transaction types are described in `enum mail_transaction_type`. A single transaction record may contain multiple changes of the same type, although some types don't allow this. Because the size of the transaction record for each type is known (or can be determined from the type-specific record contents), the `mail_transaction_header.size` field can be used to figure out how many changes there are. For example a transaction record that appends two mails: c ``` struct mail_transaction_header { .type = MAIL_TRANSACTION_APPEND, .size = sizeof(struct mail_index_record) * 2 } struct mail_index_record { .uid = 1, .flags = 0 } struct mail_index_record { .uid = 2, .flags = 0 } ``` For supporting multi-record transactions, there's a `MAIL_TRANSACTION_BOUNDARY` type. These transactions are always processed at the same time, or if the write broke before finishing them the log file truncates the partial transaction away. However, there's no further guarantee that the subsequent transaction processing will all either succeed or fail. Generally they should, but the mailbox syncing could fail partially for example due to storage problems. See `lib-index/mail-transaction-log.h` in the source code for details. ## Internal vs. External Transactions are either internal or external. The difference is that external transactions describe changes that were already made to the mailbox, while internal transactions are commands to do something to the mailbox. This is especially relevant with mailbox formats that support changes to them done outside Dovecot, like mbox or Maildir. When beginning to synchronize such a mailbox with index files, the index file is first updated with all the external changes, and the uncommitted internal transactions are applied on top of them. When synchronizing the mailbox, using the synchronization transaction writes only external transactions. Also if the index file is updated when saving new mails to the mailbox, the append transactions must be external. This is because the changes are already in the mailbox at the time the transaction is read. ## Reading and Writing Appending to an existing `dovecot-index.log` file locks it exclusively using the index files' default lock method (`[lock_method](/main/core/summaries/settings.html#lock_method)`. The transaction log files are opened with `O_APPEND` flag, which usually makes the writes appear as atomic (although this doesn't seem to be actually guaranteed, practically this seems to happen). Reading transaction logs doesn't require any locking at all. Due to the `O_APPEND` behavior the reads typically don't even see partially written transactions (although they are also handled properly). A new log is created by first creating a `dovecot.index.log.newlock` dotlock file, which guarantees that the process itself is the only one creating a new log. After this, verify whether another process had just recreated the `dovecot.index.log`. If it had, there's no need to recreate it again. If not, finish writing the log header to the newlock file and finally `rename()` it to `dovecot.index.log`. Transaction logs are always only appended to, with one exception: If a partially written transaction is found, it'll be truncated away and new transaction is overwritten on top of the old truncated data. WARNING This should be avoided, rather rotate the transaction log instead. ## UIDs Many record types contain `uint32_t uid1, uid2` fields. This means that the changes apply to all the messages in uid1..uid2 range. Dovecot used to optimize these so that if for example the first 3 messages in a mailbox were 1,100,1000, these could have been referred to with `{ uid1=1, uid2=1000 }`. However, this is no longer done for a few reasons: - Another session might still have the same mailbox open without having synced away the expunged messages (IMAP sessions can do this by not sending any commands that allow syncing expunges). For example it might still see that the 2nd mail is uid=50. It would be confusing that the already expunged mail gets flag updates. - It makes it more difficult to debug problems when it's not clear which messages exactly were intended to be referred to. - There are likely also some issues related to dsync. ## Expunges Because expunges actually destroy messages, they deserve some extra protection to make it less likely to accidentally expunge wrong messages in case of for example file corruption. The expunge transactions must have `MAIL_TRANSACTION_EXPUNGE_PROT` ORed to the transaction type field. If an expunge type is found without it, assume a corrupted transaction log. `MAIL_TRANSACTION_EXPUNGE_GUID` is preferred to be used for expunging messages over `MAIL_TRANSACTION_EXPUNGE` that just lists the UIDs. The mailbox syncing looks up the actual GUID for the referred mail, and verifies that it matches the GUID in the expunge request. If they don't match, something's corrupted and the mail won't be expunged. The expunge GUID records also improve dsync behavior. ## Flags and Keywords IMAP protocol supports changing both flags and keywords with the same STORE command, but in Dovecot index fields they are handled separately. Flags can be added/removed with `MAIL_TRANSACTION_FLAG_UPDATE` while keywords can be added/remove with `MAIL_TRANSACTION_KEYWORD_UPDATE`. Keywords can be completely cleared out with `MAIL_TRANSACTION_KEYWORD_RESET`. To completely replace all flags and keywords with wanted ones, set: - `MAIL_TRANSACTION_FLAG_UPDATE`: Set `mail_transaction_flag_update.add_flags` to the wanted system flags and `.remove_flags = 0xff`. - `MAIL_TRANSACTION_KEYWORD_RESET` to remove all keywords. - `MAIL_TRANSACTION_KEYWORD_UPDATE` to set back the wanted keywords. ## Extensions Extension records allow creating and updating extension-specific header and message record data. For example messages' offsets to cache file or mbox file are stored in extensions. Whenever using an extension, you'll need to first write `MAIL_TRANSACTION_EXT_INTRO` record. This is a bit kludgy and hopefully will be replaced by something better in future. The intro contains: c ``` struct mail_transaction_ext_intro { uint32_t ext_id; uint32_t reset_id; uint32_t hdr_size; uint16_t record_size; uint16_t record_align; uint16_t flags; uint16_t name_size; /* unsigned char name[]; */ }; ``` If the extension already exists in the index file, `ext_id` can be set to it directly (extensions can't be removed from an existing index). For adding a new extension, specify the extension name instead and use `ext_id=(uint32_t)-1`. It's always possible to just give the name if you don't know the existing extension ID, but this uses more disk space. `reset_id` contains kind of a "transaction validity" field. It's updated with `MAIL_TRANSACTION_EXT_RESET` record, which (optionally) causes the extension records' contents to be zeroed. If an introduction's `reset_id` doesn't match the last EXT\_RESET, it means that the extension changes are stale and they must be ignored. For example: - `dovecot.index.cache` file's `file_seq` header is used as a `reset_id`. Initially it's 1. - Process A: Begins a cache transaction, updating some fields in it. - Process B: Decides to compress the cache file, and issues a `reset_id = 2` change. - Process A: Commits the transaction with `reset_id = 1`, but the cache file offsets point to the old file, so the changes must be ignored. `hdr_size` specifies the number of bytes the extension wants to have in the index file's header. `record_size` specifies the number of bytes it wants to use for each record. The sizes may grow or shrink at any time. `record_align` contains the required alignment for the field. For example if the extension contains a 32bit integer, the alignment should be 32bit so that the process won't crash in CPUs which require proper alignment. Of course, if the field is accessed only as 4 individual bytes, the alignment can be 1. Extension record updates typically are message-specific, so the changes must be done for each message separately rather than an UID range. For example: c ``` struct mail_transaction_ext_rec_update { uint32_t uid; // instead of uid1, uid2 /* unsigned char data[]; */ }; ``` # Mail Index API ![Index API](/main/assets/lib-index.CbsGHgnR.png) INFO See Also: - [Mail Indexes](./index_format.html) for an overview of what the cache does. `lib-index/mail-index.h` contains the functions to access the index files. `mail-cache.h` contains the functions to access the cache file. The purpose of the main structures are: - `struct mail_index`: Global state of the index. - `struct mail_index_view`: View into the mailbox state, which contains the mail sequence <-> UID mapping. The views see new messages come and expunged messages go only when it's being explicitly synchronized. However, the record data (flags, keywords, extensions) can change without syncing. This is normally the wanted behavior anyway (e.g. there's no point accessing obsolete cache file offsets). An index can have many views. In lib-storage each `struct mailbox` has a single view. - `struct mail_index_map`: Index file is read via maps. A view has its primary map, but it can also reference other newer maps to return latest record data. Maps can be shared by different views. Maps can contain either mmap()ed memory areas pointing to the index file, or an in-memory copy of it. - `struct mail_index_transaction`: In-memory list of changes to be written to the transaction log. The writing is done only when the transaction is committed. - `struct mail_transaction_log`: Global state of the transaction log. - `struct mail_transaction_log_file`: State of an opened transaction log file. - `struct mail_cache`: Global state of the cache file. - `struct mail_cache_view`: View for reading the cache file. The cache view references an index view. - `struct mail_cache_transaction`: Transaction for modifying the cache file. A transaction references a cache view and index transaction. See `lib-index/*.h` in the source code for details about these structs. ## Views and Maps ![Mail Index Map](/main/assets/mail-index-map.DS3nV0lT.png) The mail index API provides access to messages primarily using their message sequence number (MSN). This is the same as the IMAP MSNs. The first message has MSN=1, the second message 2 and so on. When a message is expunged, the MSNs change for all the messages following it. This means that the MSNs are highly stateful and can't be used as any kind of a permanent pointer to the message. IMAP UIDs provide such a permanent pointer instead (until UIDVALIDITY changes). MSNs are always attached to a specific "view". The MSNs within the view don't change until an explicit "view sync" is performed. There can be multiple views to the same folder with different MSN mappings. This means that a view may include messages that have already been expunged. Their index record data is still available though, and their `dovecot.index.cache` data may also be still available. There are APIs to convert between MSNs and UIDs. Converting UID to MSN requires doing a binary search through all the message records. It's fast, but still better avoided if not needed. This is one reason why all the API functions use MSNs instead of UIDs. Another reason is that IMAP protocol requires access by MSN in any case, so supporting MSNs directly makes them more efficient. The main downside to using MSNs is that whenever a message is expunged, we memmove() all the message data after it over the expunged message. This is typically not a big problem, because users usually expunge only new mails so the memmove()d size is rather small. If multiple mails are expunged at once, we'll also optimize so that we don't memmove() the same data multiple times. (The first Dovecot 0.99.x versions attempted to avoid this memmove() by tracking the mails in a binary tree, but this was optimizing a rarely problematic access pattern at the expense of a normal access pattern.) ### Maps A view points to a map, which points to a rec\_map (`struct mail_index_record_map`). The rec\_map can be thought of as a mmap() of the index file. So the rec\_map is the one that actually contains all the mail records. Multiple views can point to the same shared map, and multiple maps can point to the same shared rec\_map. When messages are appended or expunged, the shared maps are copied into private maps. Maps are updated whenever index file is refreshed, which can happen any time, not just when syncing index/view. The map is first built by reading or mmap()ing the `dovecot.index` file. The index header contains the file seq/offset to `dovecot.index.log` to indicate at which position it was created. All changes after this offset need to be read from the log and applied on top of the initial index. Later on when a map is being refreshed, only new changes from the `dovecot.index.log` file are read and applied. ### View Syncing Views are always synced up to a specific `dovecot.index.log` file seq/offset. IMAP protocol doesn't allow EXPUNGE notifications to be sent to the client in all situations, so a view sync can optionally sync the expunges or leave them to be synced later. This is why there are both "expunge" and "head" seq/offsets. The "expunge" points how far expunges have been synced, and the "head" how far everything else has. View syncing returns a list of flag changes and modseq changes that happened during the sync. This is used to send change notifications to the IMAP sessions and for syncing virtual mailboxes. It's possible that if a view hasn't been synced for a long time, it can point to a log file that has been rotated a few times already and no longer exists. In this situation Dovecot compares the original view's map and the latest map to find out the changes. ### View APIs For accessing messages with their UIDs, you'll first need to convert them to sequences with either `mail_index_lookup_uid()` or `mail_index_lookup_uid_range()`. `mail_index_lookup()` can be used to look up a single record's UID and flags. The returned record points to the latest map, so that it contains the latest flag and extension changes. If the message was already expunged from the latest map, it returns the old contents from the view's map. It's possible to find out whether a mail has been expunged from the latest map with `mail_index_is_expunged()`. `mail_index_lookup_full()` and `mail_index_lookup_ext_full()` can be used to return also the map where the message was found. This can be important with index extensions. If the extension record's state depends on the extension header or its reset\_id, they must be looked up from the same map. For this reason there exists `mail_index_map_get_header_ext()` and other functions which take the map as parameter. For example a cache file offset that was looked up must be verified to be compatible with the current cache file by comparing the reset\_id in the map vs cache file header. # Input Streams `lib/istream.h` describes Dovecot's input streams. Input streams can be stacked on top of each others as many times as wanted. Input streams actually reading data: - file: Read data from fd using `pread()` for files and `read()` for non-files. - unix: Read data from UNIX socket. Similar to file, but supports receiving file descriptors. - data: Read data from memory. Input stream filters: - concat: Concatenate multiple input streams together - chain: Chain multiple input streams together. Similar to istream-concat, but more istreams can be added after initialization and EOF needs to be explicitly added. - seekable: Make a number of (possibly non-seekable) input streams into a single seekable input stream. If all of the input streams are already seekable, a concat stream is created instead. - Usually the only non-seekable input streams are non-file fds, such as pipes or sockets. - crlf: Change all newlines to either LFs or CRLFs, by adding or removing CRs as necessary. - limit: Limit input stream's length, so after reading a given number of bytes it returns EOF. - sized: Require istream's length to be exactly the given size, or the last read returns error. - timeout: Fail the read when given timeout is reached. - try: Read from the first input stream that doesn't fail with EINVAL. - tee: Fork an input stream to multiple streams that can be read independently. - multiplex: Multiplex-iostreams support multiple iostream channels inside a single parent istream. - callback: Build an input stream by calling callback functions that return the data. - base64-encoder, base64-decoder: Encode/decode base64. - failure-at: Insert a failure at the specified offset. This can be useful for testing. - hash: Calculate hash of the istream while it's being read. - `lib-compression/*`: Read zlib/bzlib/lz4/zstd compressed data. There are also various other less generic istreams. Especially lib-mail has many mail-related istreams. ## Reading `i_stream_read()` tries to read more data into the stream's buffer. It returns: - \-2: Nothing was read, because the buffer is full. - \-1: Either input reached EOF, or read failed and stream\_errno was set. - 0: Input stream is non-blocking, and no more input is available now. - \>0: Number of new bytes read. Reading from a stream doesn't actually go forward in the stream, that needs to be done manually with `i_stream_skip()`. This makes it easy to read full data records into the stream directly instead of creating separate buffers. For example when reading line-based input you can keep reading input into the stream until you find LF and then just access the string directly from the input buffer. There are actually helper functions for this: `i_stream_next_line()` attempts to return the next line if available, `i_stream_read_next_line()` does the same but does a read to try to get the data. Because more and more data can be read into the buffer, the buffer size is typically limited, and once this limit is reached read returns -2. The buffer size is given as parameter to the `i_stream_create_*()`, but filters often use their parent stream's buffer size. The buffer size can be also changed with `i_stream_set_max_buffer_size()`. Figuring out what the buffer size should be depends on the situation. It should be large enough to contain all valid input, but small enough that users can't cause a DoS by sending a too large record and having Dovecot eat up all the memory. If there's no specific buffer size requirement, `IO_BLOCK_SIZE` is a good value to use. Once read returns -1, the stream has reached EOF. `stream->eof=TRUE` is also set. In this situation it's important to remember that there may still be data available in the buffer. If `i_stream_have_bytes_left()` returns FALSE, there really isn't anything left to read. Also at EOF it's important to check `stream->stream_errno` to see if the read failed. Whenever `i_stream_read()` returns >0, all the existing pointers are potentially invalidated. When i\_stream\_read() returns <= 0, the data previously returned by `i_stream_get_data()` are still valid, preserved in "snapshots". Example: c ``` /* Read line-based data from file_fd. The buffer size has no limits. */ struct istream *input = i_stream_create_fd(file_fd, SIZE_MAX, FALSE); const char *line; /* Return the last line also even if it doesn't end with LF. This is generally a good idea when reading files (but not a good idea when reading commands from e.g. socket). */ i_stream_set_return_partial_line(input, TRUE); while ((line = i_stream_read_next_line(input)) != NULL) { /* handle line */ } i_stream_destroy(&input); ``` ## Internals `lib/istream-private.h` describes the internal API that input streams need to implement. The methods that need to be implemented are: - `read()` is the most important function. It can also be tricky to get it completely bug-free. See the existing unit tests for other istreams and try to test the edge cases as well (such as ability to read one byte at a time and also with max buffer size of 1). When it needs to read from parent streams, try to use `i_stream_read_memarea(parent)` if possible so a new snapshot isn't unnecessarily created (see the snapshot discussion below). - `seek(v_offset, mark)` seeks to given offset. The `mark` parameter is necessary only when it's difficult to seek backwards in the stream, such as when reading compressed input. - `sync()` removes everything from internal buffers, so that if the underlying file has changed the changes get noticed immediately after sync. - `get_size(exact)` returns the size of the input stream, if it's known. If `exact=TRUE`, the returned size must be the same how many bytes can be read from the input. If `exact=FALSE`, the size is mainly used to compare against another stat to see if the underlying input had changed. For example with compressed input the size could be the compressed size. - `stat(exact)` stats the file, filling as much of the fields as makes sense. `st_size` field is filled the same way as with `get_size()`, or set to -1 if it's unknown. - `switch_ioloop_to` If there are any I/O loop items associated with the stream, move all of them to the provided/current ioloop. - `snapshot(prev_snapshot)` creates a snapshot of the data that is currently available via i\_stream\_get\_data(), merges it with prev\_snapshot (if any) and returns the merged snapshot (see below for more details). There are some fields available. Below is a list of the most important ones. For a complete overview please see `istream-private.h`. - `fd` file descriptor being read by the stream. - `buffer` contains pointer to the data. - `parent` parent istream - for filter streams. - First `skip` bytes of the buffer are already skipped over (with `i_stream_skip()` or seeking). - Data up to `pos` bytes (beginning after `skip`) in the buffer are available with `i_stream_get_data()`. If pos=skip, it means there is no available data in the buffer. If your input stream needs a write buffer, you can use some of the common helper functions and variables: - `w_buffer` contain the pointer where you can write data. It should be kept in sync with `buffer`. - `buffer_size` specifies the buffer's size, and `max_buffer_size` the max. size the buffer can be grown to. - `i_stream_try_alloc(wanted_size, size_r)` can be used when you want to store `wanted_bytes` into `w_buffer`. If the buffer isn't large enough for it, it's grown if possible. The buffer isn't grown above the stream's max buffer size. The returned `size_r` specifies how many bytes are actually available for writing at `stream->w_buffer + stream->pos`. - `i_stream_alloc(size)` is like `i_stream_try_alloc()`, except it always succeeds allocating `size` bytes, even if it has to grow the buffer larger then the stream's max buffer size. - Lower-level memory allocation functions: - `i_stream_grow_buffer(bytes)` grows the `w_buffer` by the given number of bytes, if possible. It won't reach the stream's current max buffer size. The caller must verify from `buffer_size` how large the buffer became as a result of this call. - `i_stream_compress()` attempts to compress the current `w_buffer` by removing already-skipped data with `memmove()`. If `skip` is 0, it does nothing. Note that this function must not be called if `memarea` has refcount > 1. Otherwise that could be modifying a snapshotted memarea. The snapshots have made implementing istreams slightly more complicated than earlier. There are a few different ways to implement istreams: - Always point `buffer=w_buffer` and use `i_stream_try_alloc()` and/or `i_stream_alloc()` to allocate the `w_buffer`. The generic code will handle all the snapshotting. Use `i_stream_read_memarea()` to read data from parent stream so multiple snapshots aren't unnecessarily created. - Guarantee that if `read()` returns <=0, the existing `buffer` will stay valid. Use `ISTREAM_CREATE_FLAG_NOOP_SNAPSHOT` flag in `i_stream_create()` so your filter stream isn't unnecessarily snapshotted (or causing a panic due to missing `snapshot()` implementation). - One way of doing this with filter streams is to read from the parent stream via `i_stream_read(parent)` and always use `buffer=i_stream_get_data(parent)`. The parent's snapshotting guarantees that the buffer will stay valid. - Implement the `snapshot()` yourself in the stream. You'll need to create a new memarea of the current data available via `i_stream_get_data()` and it must not change, i.e. most likely you'll need to duplicate the allocated memory. Create a new `struct istream_snapshot` and assign the allocated memarea to its `old_memarea`. Fill `prev_snapshot` field and return your new snapshot. The snapshot will be freed by the generic istream code either when the next `read()` returns >0 or when the istream is destroyed. - See `src/lib-mail/istream-header-filter.c` or `src/lib-dcrypt/istream-decrypt.c` for examples of how to do this. - Filter streams that only pass through parent stream's contents without changes can just point to the parent stream. The default snapshotting causes the parent to be snapshotted, so the filter stream can simply use `i_stream_read_memarea()` and point to the parent's buffer. When Dovecot is configured with `--enable-devel-checks`, `i_stream_read()` will verify that the first and the last two bytes of the buffer didn't unexpectedly change due to a `read()`. While developing istream changes you should use this to make sure the istream is working properly. Running the istream unit test also via valgrind can also be used to verify that the buffer wasn't freed. # Mail Namespace `src/lib-storage/mail-namespace.h` describes mail namespaces. See [Namespaces](/main/core/config/namespaces.html) for more information about what they are actually about. ## Hierarchy Separators and Namespace Prefixes A namespace and [mailbox\_list](./mailbox_list.html) has 1:1 relationship. A namespace is mainly about dealing with hierarchy separators and namespace prefixes, which mailbox list doesn't know or care much about. Mailbox lists have their native hierarchy separators. For example with FS layout the separator is '/', because child mailboxes are physically in subdirectories and '/' is the separator for directories. With Maildir++ layout the separator is hardcoded to '.' in the maildir directory name, so that's its native hierarchy separator. Dovecot allows namespace separators to be configurable, so namespaces have two separators: - `mail_namespace_get_sep()` returns the namespace's configured separator. If unset, this uses the mailbox\_list's default separator. - `mailbox_list_get_hierarchy_sep()` returns the mailbox\_list's native separator. All `list=yes` namespaces must use the same configured hierarchy separator. This is returned by `mail_namespaces_get_root_sep()`. Namespaces also have prefixes. The prefixes are visible for users/clients and they appear to be part of the actual mailbox name. One commonly used namespace prefix is "INBOX.", so all mailboxes (other than INBOX itself) appear to be children of the INBOX. If all configured namespaces have a non-empty prefix, Dovecot internally creates a namespace with empty prefix. This way functions that find a namespace for a given mailbox name will always be able to return a valid namespace, so callers don't have to handle "nonexistent namespace" error. See [mailbox names](./mailbox_list.html#mailbox-names) for how mailbox names behave with namespace configuration. ## Users and Owners When accessing other users' shared mailboxes, there's a difference between a namespace's user and owner: - `ns->user` points to the mail user actually accessing the mailbox (i.e. the IMAP connection's mail user). - `ns->owner` points to the mail user who shared the mailbox. The distinction can be important sometimes. For example if user adds or removes messages from the shared mailbox, the owner's quota must be updated instead of the user's. ## Functions Functions about finding namespaces: - `mail_namespace_find()` returns namespace for given virtual name. It also has a few variations: - `mail_namespace_find_unalias()` changes the behavior for namespaces that are aliases to another namespace (`[namespace_alias_for](/main/core/summaries/settings.html#namespace_alias_for)`). Then it returns the unaliased namespace and modifies the mailbox name. - `mail_namespace_find_visible()` skips `hidden=yes` namespaces. - `mail_namespace_find_subscribable()` skips `subscriptions=no` namespaces. - `mail_namespace_find_unsubscribable()` skips `subscriptions=yes` namespaces. - `mail_namespace_find_inbox()` returns the namespace with `inbox=yes`. There is always exactly one such namespace. - `mail_namespace_find_prefix()` returns the namespace that has the given prefix. - `mail_namespace_find_prefix_nosep()` does the same, but ignores the trailing separator in prefix (e.g. "foo" would find namespace with prefix=foo/). A single namespace can in theory point to multiple storages. The `ns->storage` points to the default storage, while `ns->all_storages` is an array of all storages that the namespace can access. Currently there is no actual code that adds more than one storage to a namespace, but this might change some day so try to avoid preventing that. When creating new mailboxes, `mail_namespace_get_default_storage()` returns the storage that should be used. For other purposes you should find the storage via [mailbox list](./mailbox_list.html) functions. # Mail Plugins Typically plugins add hooks in their init() function by calling `mail_storage_hooks_add()`, and remove the hooks at deinit() with `mail_storage_hooks_remove()`. ## Supported Hooks ### `mail_user_created` A new mail user was created. It doesn't yet have any namespaces. ### `mail_storage_created` A new mail storage was created. It's not connected to any namespaces/mailbox lists yet. ### `mailbox_list_created` A new mailbox list was created. It's not connected to any storages yet. Because of this, some internal virtual methods haven't been overridden by the storage yet, so plugins rarely want to use this hook. Instead they should use `mail_namespace_storage_added`. ### `mail_namespace_storage_added` Storage was connected to its first mailbox list. This hook should usually be used if plugin wants to override mailbox\_list's methods. ### `mail_namespaces_created` User's all namespaces have been created. This hook is called only per user at startup. More internal namespaces may be created later when using shared mailboxes. ### `mail_namespaces_added` More namespaces were added to the user's namespaces. At initialization this is called before `mail_namespaces_created`. Afterwards it's mainly called when shared mailboxes are accessed, which trigger shared namespace creation. ### `mailbox_allocated` `mailbox_alloc()` was called. ### `mailbox_opened` Mailbox (and its index) was actually opened, either explicitly with `mailbox_open()` or implicitly by some other function. ### `mail_allocated` Mail was allocated with `mail_alloc()`. ## Overriding Methods When the hook gets called, you usually want to override some method of the created object. This is the easy part, for example: c ``` static void plugin_mailbox_allocated(struct mailbox *box) /* ... */ box->v.transaction_begin = plugin_transaction_begin; ``` The problem is that once `plugin_transaction_begin()` is called, it should call the original `transaction_begin()`. There may also be multiple plugins that want to override the same method, so the idea is to just have each plugin call the previous `transaction_begin()`. The next problem is where do you save the previous value? Most objects have a `module_contexts` array for storing per-plugin pointers for this purpose. There are several helper functions to make setting and accessing them in a quite safe way. Easiest way to set up the module context is to just copy&paste code from an existing plugin that sets the same context. Here's some documentation about it anyway: First you start by creating a register for the plugin. There are different registers for different types of objects: - `mail_user_module_register`: For `struct mail_user`. - `mailbox_list_module_register`: For `struct mailbox_list`. - `mail_storage_module_register`: For `struct mail_storage`, `struct mailbox`, `struct mailbox_transaction_context` and `struct mail_search_context`. - `mail_module_register`: For `struct mail`. We'll assume you want to use `mail_storage_module_register`: c ``` static MODULE_CONTEXT_DEFINE_INIT(plugin_storage_module, &mail_storage_module_register); ``` If you need to make it external, use: c ``` extern MODULE_CONTEXT_DEFINE(plugin_storage_module, &mail_storage_module_register); struct plugin_storage_module plugin_storage_module = MODULE_CONTEXT_INIT(&mail_storage_module_register); ``` Next you'll need to allocate memory for the structure you want to place in the context. If you only want to override some methods, you can use: c ``` union mailbox_module_context *mbox; struct mailbox_vfuncs *v = box->vlast; mbox = p_new(box->pool, union mailbox_module_context, 1); mbox->super = *v; box->vlast = &mbox->super; v->transaction_begin = plugin_transaction_begin; MODULE_CONTEXT_SET_SELF(box, plugin_storage_module, mbox); ``` If you want to store some more plugin-specific data to the object instead of just the super methods, you can do: c ``` struct plugin_mailbox { /* must be called module_ctx */ union mailbox_module_context module_ctx; }; /* ... */ struct plugin_mailbox *mbox; struct mailbox_vfuncs *v = box->vlast; mbox = p_new(box->pool, struct plugin_mailbox, 1); mbox->module_ctx.super = *v; box->vlast = &mbox->super; v->transaction_begin = plugin_transaction_begin; MODULE_CONTEXT_SET(box, plugin_storage_module, mbox); ``` Note that when using union directly you use `MODULE_CONTEXT_SET_SELF()`, while when it's inside a struct you use `MODULE_CONTEXT_SET()`. Once all this initialization is done, you can look up the module context with: c ``` #define PLUGIN_CONTEXT(obj) MODULE_CONTEXT(obj, plugin_storage_module) /* ... */ struct plugin_mailbox *mbox = PLUGIN_CONTEXT(box); ``` # Mail Storage `src/lib-storage/mail-storage.h` and `mail-storage-private.h` describes mail storage. Mail storage is mainly about being a common container for its mailboxes. For example with [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox), each storage has one directory where all the message bodies are written to, while the per-mailbox directories only contain index files. With other mailbox formats the mail storage doesn't do much else than allow allocating [mailboxes](/main/developers/design/mailbox.html). ## Public Functions ### `mail_storage_purge()` Frees disk space used by expunged messages. Currently the only mailbox format that uses this is multi-dbox. ### `mail_storage_get_settings()` Returns mail storage settings. ### `mail_storage_set_callbacks()` Can be used to specify "OK" and "NO" callbacks, which are called when a long running operation wants to send a status update. For example "OK Stale mailbox lock file detected, will override in n seconds" or "NO Mailbox is locked, will abort in n seconds". ## Required Methods Methods that mail storage drivers need to implement are: ### `get_setting_parser_info()` Returns storage-specific settings parser information. ### `alloc()` Allocate memory for a storage and set its virtual functions. ### `create(ns)` Initialize the storage based on given namespace settings. The same storage can be used by other namespaces, but they don't call `create()` again. This function typically shouldn't fail, except when storage can't handle the wanted namespace settings. ### `destroy()` Destroys the storage. ### `add_list(list)` Called every time the storage is attached to a new namespace / mailbox list. ### `get_list_settings(ns, set)` Used to get storage's default settings. ### `autodetect(ns, set)` Returns TRUE if based on the given settings it looks like this storage should we handling the namespace. This is done when `[mail_driver](/main/core/summaries/settings.html#mail_driver)` isn't explicitly set. ### `mailbox_alloc()` Allocate memory for [mailbox](/main/developers/design/mailbox.html). ### `purge()` If storage supports purging. # Mail User `src/lib-storage/mail-user.h` describes mail user. The struct contains all kinds of useful information about the user that can be accessed directly. Some of the most useful things you can do with a user are: - `user->username` gives you the actual username string (e.g. `user@domain.org`). - `user->set` gives you access to user's settings. - `user->namespaces` points to a linked list of user's namespaces. - `mail_user_get_home()` returns user's home directory, if there's one. - `mail_user_home_expand()` expands `~/` at the beginning of given path to user's actual home directory. Typically each new IMAP/POP3/etc. connection creates a single mail user. If the same process handles multiple connections for the same user, they don't share the same mail\_user (especially since each mail\_user has a unique session ID). If a user has shared mailboxes from other users (not public namespaces), a mail user is also created whenever necessary to list/access the user's mailboxes. Again there is no attempt to share the created mail user with other connections. # Mail Mail is first allocated with `mail_alloc()`. Mails always belong to a [transaction](./mailbox_transaction.html). Even if mail is treated read-only, Dovecot might write data to cache file, so whenever possible, mail transactions should be committed. When mail is allocated, you can specify a list of fields and headers that you're (most likely) going to need. This allows Dovecot to optimize the later `mail_get_*()` lookups so that it doesn't need to parse the message multiple times. These fields are also added to cache file, so you shouldn't list fields unless you're fairly certain you need them. Usually the mails are created and returned by `mailbox_search_*()` functions. This is preferable even if you're doing a simple search, such as "all mails". Using the search API allows the internal implementation to prefetch mails, which can reduce latency. Alternatively you can use `mail_set_seq()` and `mail_set_uid()` to jump between mails, but this prevents prefetching. ## Getting Data ### Mail Fields Some of the mail fields can be accessed directly: #### `box` Mail's mailbox, same as the transaction's. #### `transaction` Mail's transaction, the same that was given to `mail_alloc()`. #### `seq` Currently selected message's sequence number. #### `uid` Currently selected message's UID. #### `expunged` We already detected that the message is expunged and can't be accessed. This may also be set (and looked up) later when some `mail_get_*()` function fails. #### `has_nuls` #### `has_no_nuls` Message body is known to (not) have NUL characters. ### `lookup_abort` The final field is `lookup_abort`, which is write-only. Normally when doing a `mail_get_*()` operation for a field that isn't in a cache, the field is generated and added to cache. If you don't want this, but instead have figured out some better optimized way to do non-cached lookups, you can change this field so that `mail_get_*()` lookups fail instead with `MAIL_ERROR_LOOKUP_ABORTED`. This is primarily used by searching code internally. #### Lookup Abort Flags ##### `MAIL_LOOKUP_ABORT_NEVER` The default - do whatever it takes to return the value. ##### `MAIL_LOOKUP_ABORT_READ_MAIL` If returning the value would require reading message headers/body, abort. ##### `MAIL_LOOKUP_ABORT_NOT_IN_CACHE` If the value isn't already in cache, abort. For example if looking up message's physical size would require `stat()`\\ ing the file this wouldn't be done. ## Mail Get Functions Most of the `mail_get_*()` should be fairly obvious in how they work. Only functions returning int can fail, others don't. - Keywords can be looked up either by getting an array of keyword strings or keyword indexes. The index lookups are slightly faster. Keyword indexes can be converted to strings by using the `mailbox_status.keywords` array returned by `mailbox_get_status()`. - `mail_get_first_header()` returns 0 if header wasn't found, 1 if it was. - `mail_get_special()` can return various special fields. If a special isn't implemented by some driver, the call returns success and sets the value to empty string. - `mail_get_stream()` returns an input stream that can be used to access the mail. If this function is called multiple times, each call seeks the stream to beginning. Don't unreference the stream, it's destroyed automatically. - Typically the input contains the raw data in disk, lines may end with LF or CRLF depending on how they're on disk. - mbox drops Dovecot's internal headers from the stream (X-UID:, Status:, etc.). - Plugins (e.g. [mail-compress plugin](/main/core/plugins/mail_compress.html)) can also hook into this call and modify the input stream. Sometimes you might notice that some looked up field is actually corrupted. For example you might notice that input stream returns EOF earlier than previous `mail_get_physical_size()` said. This might have been caused by various different things, but in any case all you can really do then is to just call `mail_set_cache_corrupted()` and try again. ## Changing Metadata Some of the messages' metadata can be updated: - `mail_update_flags()` and `mail_update_keywords()` changes flags/keywords. - Usually you should set `modify_type` parameter to `MODIFY_ADD` or `MODIFY_REMOVE`, instead of replacing all the flags. That allows concurrent flag updates not to overwrite each others changes. - `mail_expunge()` expunges a message. Other functions are mainly intended for mailbox replication or restoring an existing mailbox (e.g. dsync): - `mail_update_modseq()` and `mail_update_pvt_modseq()` can be used to increase the message's shared/private modseq. - `mail_update_pop3_uidl()` can be used to give a specific POP3 UIDL for the message. This is used internally when `pop3_save_uidl=yes`. Other metadata can't be changed. IMAP protocol requires that messages are immutable, so it's not possible to change a message's received date, headers or body. If you wish to modify any of them, you need to create a new message and expunge the old one. # Mailbox List `src/lib-storage/mailbox-list.h` and `mailbox-list-private.h` describes mailbox list. The purpose of mailbox list is to manage mailbox storage name <-> physical directory path mapping. Its most important functions are: - listing existing mailboxes, - creating directories for new mailboxes (but not the mailboxes themselves, that's storage's job), - deleting mailboxes, - renaming mailboxes and - managing mailbox subscriptions. Mailbox list code also internally creates and updates mailbox changelog (in `dovecot.mailbox.log` file), which keeps track of mailbox deletions, renames and subscription changes. This is primarily useful when running the `doveadm sync` command. ## Mailbox Names The mailbox names are configured in [Mail Namespace](./mail_namespace.html). The same mailbox name can be visible in three different forms: - Virtual name (commonly called "vname") uses the namespace's configured separator and namespace prefix. For example `INBOX/foo/bar`. - Storage name (commonly called just "name") uses the native separator and doesn't have a namespace prefix. For example `foo.bar`. - Physical directory name on disk can be different again. For example with Maildir++ it could be `.../Maildir/.foo.bar` (note the leading dot before `foo`). With `[mailbox_list_layout = index](/main/core/summaries/settings.html#mailbox_list_layout)` the directory name is the mailbox GUID (e.g. `.../mailboxes/d3b07384d113edec49eaa6238ad5ff00`). The mailbox virtual/storage names can be converted with functions: - `mailbox_list_get_storage_name()` - Virtual name -> storage name - `mailbox_list_get_vname()` - Storage name -> virtual name ## Listing Mailboxes First the list operation is initialized with one of the init functions: ### Init Functions #### `mailbox_list_iter_init()` List mailboxes that match the given pattern. #### `mailbox_list_iter_init_multiple()` List mailboxes that match any of the given patterns list. #### `mailbox_list_iter_init_namespaces()` List matching mailboxes from all namespaces. - `MAILBOX_LIST_ITER_SKIP_ALIASES` flag skips namespaces that have `alias_for` set. You usually want to set this flag to avoid processing the same mailbox multiple times. ### List Patterns The patterns are IMAP-style patterns with '%' and '\*' wildcards as described by [RFC 3501](https://datatracker.ietf.org/doc/html/rfc3501): '%' matches only up to next hierarchy separator, while '\*' matches the rest of the string. ### Mailbox Return Flags These flags control what mailboxes are returned: #### `MAILBOX_LIST_ITER_NO_AUTO_BOXES` Don't list INBOX or other autocreated mailboxes unless they physically exists (i.e. they have been opened once). #### `MAILBOX_LIST_ITER_SKIP_ALIASES` Skip namespaces that are aliases to other namespaces (`[namespace_alias_for](/main/core/summaries/settings.html#namespace_alias_for)`). #### `MAILBOX_LIST_ITER_STAR_WITHIN_NS` Change `*` in patterns to not cross namespace boundaries. For example `*o` returns all mailboxes that end with the `o` letter in the root namespace, but not in any other namespaces. #### `MAILBOX_LIST_ITER_SELECT_SUBSCRIBED` List only subscribed mailboxes. #### `MAILBOX_LIST_ITER_SELECT_RECURSIVEMATCH` Currently only useful when combined with `_SELECT_SUBSCRIBED` flag. Then it adds `MAILBOX_CHILD_SUBSCRIBED` flags for mailboxes whose children are subscribed. It also lists mailboxes that aren't themselves subscribed, but have children that do. #### `MAILBOX_LIST_ITER_SELECT_SPECIALUSE` List only mailboxes marked with \\Special-use flags. ### Mailbox Matching Return Flags These flags control what is returned for matching mailboxes: #### `MAILBOX_LIST_ITER_RETURN_NO_FLAGS` This can be set when you don't care about mailbox flags. They're then set only if it can be done without any additional disk I/O. #### `MAILBOX_LIST_ITER_RETURN_SUBSCRIBED` Return mailbox's subscription state. #### `MAILBOX_LIST_ITER_RETURN_CHILDREN` Add "has child mailboxes" or "doesn't have child mailboxes" flag. #### `MAILBOX_LIST_ITER_RETURN_SPECIALUSE` Return mailbox's \\Special-use flags. ### Other Flags #### `MAILBOX_LIST_ITER_RAW_LIST` This should usually be avoided. It ignores ACLs and just returns everything. ### Return Struct Once listing is initialized, `mailbox_list_iter_next()` can be called until it returns NULL. The returned mailbox\_info struct contains: #### `vname` Mailbox's virtual name. #### `special_use` Mailbox's \\Special-use flags. #### `ns`: Mailbox's namespace. This is mainly useful when mailboxes are listed using `mailbox_list_iter_init_namespaces()`. #### `flags` Mailbox flags: ##### `MAILBOX_NOSELECT` Mailbox exists, but can't be selected. It's possible that it can be created and then it becomes selectable. For example with mbox and FS layout the directories aren't selectable mailboxes. ##### `MAILBOX_NONEXISTENT` Mailbox doesn't exist. It's listed only because it has child mailboxes that do exist but don't match the pattern. Example: `foo/bar` exists, but `foo` doesn't. `%`, `foo` or `*o` pattern would list `foo`, because it matches the pattern but its child doesn't. Then again `*`, `*bar` or `%/%` wouldn't list `foo`, because `foo/bar` matches the pattern (and is also listed). Something like `*asd*` wouldn't match either `foo` or `foo/bar` so neither is returned. ##### `MAILBOX_CHILDREN` ##### `MAILBOX_NOCHILDREN` Mailbox has or doesn't have children. If neither of these flags are set, it's not known if mailbox has children. ##### `MAILBOX_NOINFERIORS` Mailbox doesn't have children and none can ever be created. For example with mbox and FS layout the mailboxes have this flag set, because files can't be created under files. ##### `MAILBOX_MARKED` ##### `MAILBOX_UNMARKED` Mailbox has or doesn't have messages with \\Recent flags. If neither is set, the state is unknown. Because this check is done in a very cheap way, having `MAILBOX_MARKED` doesn't always mean that there are \\Recent flags. However, if `MAILBOX_UNMARKED` is returned it is guaranteed to be correct. (False positives are ok, false negatives are not ok.) ##### `MAILBOX_SUBSCRIBED` Mailbox is subscribed. ##### `MAILBOX_CHILD_SUBSCRIBED` Mailbox has a child that is subscribed (and `_SELECT_RECURSIVEMATCH` flag was set). ##### `MAILBOX_SPECIALUSE_*` These are for internal use only. Don't use them. ### Deinitialization Finally the listing is deinitialized with `mailbox_list_iter_deinit()`. If it returns -1, it means that some mailboxes perhaps weren't listed due to some internal error. If you wish to get mailbox\_info flags only for a single mailbox, you can use `mailbox_list_mailbox()`. ## Directory Permissions `mailbox_list_get_permissions()` and `mailbox_list_get_dir_permissions()` can be used to get wanted permissions for newly created files and directories. - For global files, give NULL as the mailbox name. The permissions are then based on the root\_dir. If root\_dir doesn't exist, it returns 0700/0600 mode. - For per-mailbox files, give the mailbox name. The permissions are then based on the mailbox's directory. If changing the group fails with EPERM, `eperm_error_get_chgrp()` can be used to log a nice and understandable error message. ### Returned Permissions #### `mode` Creation mode, like 0600. #### `gid` Group that should be set, unless it's `(gid_t)-1`. There are 3 reasons why it could be that: - directory has g+s bit set, so the wanted group is set automatically - group is the same as process's effective GID, so it gets set automatically - mode's group permissions are the same as world permissions, so group doesn't matter. #### `gid_origin` This string points to the directory where the group (and permissions in general) was based on, or "defaults" for internal defaults. # Mailbox Saving Both saving and copying messages begins by calling `mailbox_save_alloc()`. ## Message Metadata Fields When copying, most of the metadata fields are automatically copied from the source message. The only exception is message's flags and keywords. If you want to preserve them, the easiest way is to call `mailbox_save_copy_flags()`. ### `mailbox_save_set_flags()` Set flags and keywords. ### `mailbox_save_set_received_date()` Set message's received date (IMAP INTERNALDATE). It also supports specifying timezone, but most backends don't support saving it. ### `mailbox_save_set_from_envelope()` Set the envelope sender. Currently this is only used by mbox format to specify the address in From-line. ### `mailbox_save_set_min_modseq()` Set message's modseq to at least the specified modseq. If the modseqs are already higher in the mailbox, the resulting modseq is what it would have been without this call. INFO Mainly useful when you're restoring an existing mailbox and want to preserve metadata. ### `mailbox_save_set_uid()` Set message's UID. If mailbox's next\_uid is already higher than the specified UID, the UID request is ignored. INFO Mainly useful when you're restoring an existing mailbox and want to preserve metadata. ### `mailbox_save_set_guid()` Set message's globally unique ID. A new GUID is generated by default, and if there already exists a message with the same GUID a different one may or may not be given. For example with maildir the GUID is same as the base filename, while dbox has an explicit GUID metadata field. INFO Mainly useful when you're restoring an existing mailbox and want to preserve metadata. ### `mailbox_save_set_pop3_uidl()` Set POP3 UIDL value. Not all backends support this. INFO Mainly useful when you're restoring an existing mailbox and want to preserve metadata. ### `mailbox_save_set_save_date()` Set "message saved" date, i.e. the date/time when the message was saved to this mailbox. The default is the current time. INFO Mainly useful when you're restoring an existing mailbox and want to preserve metadata. ## Saving Once you're done with setting the metadata fields, you can either copy an existing mail with `mailbox_copy()` or provide message body with: ### `mailbox_save_begin()` Start saving from given input stream. ### `mailbox_save_continue()` Save all data from input stream. If input stream is blocking, typically a single call to this function should be enough. If input stream is non-blocking, you need to call this function until you're done. In any case call this until `i_stream_is_eof()` returns TRUE. ### `mailbox_save_finish()` Finish saving the mail, or: ### `mailbox_save_cancel()` Abort saving the mail. ### `mailbox_save_get_dest_mail()` Give access to the mail whose saving/copying was just finished (but not committed). You should be able to do pretty much anything with the mail, but its UID is usually 0 at this point. # Mailbox Searching `mailbox_search_*()` functions should be used always when you're iterating through multiple messages. The search queries can be complex or as simple as "all messages". Without searching there's also a way to directly switch to a specific message by its sequence number or UID, but this should be avoided usually since they prevent mail prefetching (which can reduce latency). ## Initializing Search is initialized with `mailbox_search_init()` on top of a [transaction](./mailbox_transaction.html). Although it would appear that a search is a read-only operation, it can actually write data to Dovecot's cache file. For example if you search for a specific header not yet in cache file, the results are saved to cache file so the next search will be fast. This is why you should always commit search transactions, even if the rest of your operation fails (you should use separate transactions for search and for updates if necessary). You'll need a search query. `src/lib-storage/mail-search.h` and `mail-search-build.h` contain all the functions and structures related to it. Usually you should start with `mail_search_build_init()` and then start adding the search parameters, either manually or with one of the existing `mail_search_build_add_*()` helper functions. The same search query structures can be saved and used for other searches later, but because search state is stored inside the structs you need to be careful that multiple searches aren't using them at the same time. This is usually more trouble than worth, so avoid doing it. Search results can be sorted by giving a sort program. Dovecot optimizes this by keeping sort indexes in the index files. ## Reading Search Results While `mailbox_search_next()` returns TRUE, a new search result is found and it changes the given [mail](./mail.html) to point to the search result. The mail's "wanted fields/headers" parameters don't need to include anything needed by the search query, Dovecot optimizes them internally. If the search needs to parse message bodies and the mailbox is large, this call can take a long time. If you want to do other things while searching, you can use `mailbox_search_next_nonblock()` that does only a bit of work and then returns either with a result or "try again later" status. Dovecot attempts to keep each non-matching call to this function between 200 and 250 milliseconds, although the upper bound can't be guaranteed. It's possible that messages are being expunged while Dovecot is searching them, so it can't determine if they would have matched the search rule or not. In this case it skips over them, but if you want to know if this has happened, you can see if `mailbox_search_seen_lost_data()` returns TRUE. ## Deinitializing `mailbox_search_deinit()` finishes the search. If it returns -1, some error occurred and some search results might not have been returned. ## Example Iterating through all messages in a mailbox goes like: c ``` /* build search query */ search_args = mail_search_build_init(); mail_search_build_add_all(search_args); search_ctx = mailbox_search_init(trans, search_args, NULL, 0, NULL); /* search context keeps args referenced */ mail_search_args_unref(&search_args); while (mailbox_search_next(ctx, &mail)) { printf("matched uid %{user}\n", mail->uid); } if (mailbox_search_deinit(&search_ctx) < 0) i_error("search failed"); ``` ## Saving Search Results Search results can be saved for future use by calling `mailbox_search_result_save()` just after initializing the search. The results as returned as UIDs with `mailbox_search_result_get()` and may contain UIDs that are already expunged. Once you're done with the saved result, free it with `mailbox_search_result_free()`. The search result can also be automatically updated whenever mailbox is synced if `MAILBOX_SEARCH_RESULT_FLAG_UPDATE` is set. The update is optimized, so Dovecot doesn't do a full re-search, but matches only new and changed messages. If `MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC` is also set, search result additions and removals are also tracked and can be retrieved with `mailbox_search_result_sync()`, i.e. with this you can implement "what changed in search results since last time I checked". # Mailbox Synchronization The idea behind synchronization is to find out what changes other sessions have done to the mailbox and to finalize our own changes to the mailbox. For example if you expunge a message in a transaction and commit it, the commit will only write a "please expunge UID n" record to Dovecot's transaction log file. The message still exists on the disk. The next time Dovecot syncs the mailbox (either the session that wrote the record or another one), it goes through all the non-synchronized records in transaction log and applies the requested changes to the backend mailbox. Syncing can be a bit heavyweight operation, so it's possible to commit multiple transactions and perform a single sync for all of them. Dovecot attempts to do this with IMAP protocol when pipelining commands. The other important job of syncing is to refresh mailbox's state: - Finding out about external modifications to mailbox (e.g. a new mail delivered to `Maildir/new/`). - Updating in-memory view of what messages exist, what their flags are, etc. When a mailbox is opened, its state starts with what index files contain at the time. Since the backend mailbox may have already changed, and syncing an up-to-date mailbox is usually really cheap, there isn't much point in not syncing mailbox immediately after opening. The mailbox state stays the same until you synchronize the mailbox again. Without syncing no new messages show up and no messages get expunged. Typically you would sync the mailbox - after committing a transaction that modifies backend mailbox in any way (instead of just internal index data), such as after changing message flags or expunging a message. - whenever you want to find out if there are any changes. With IMAP protocol this is done every time after running a command. ## Initializing `mailbox_sync_init()` initializes syncing. ### Sync Flags: Effort There are some flags that control how much effort is spent on syncing: #### `MAILBOX_SYNC_FLAG_FAST` This can be given when you're ready for mailbox to be refreshed, but don't care much if it actually is or not. When this flag is set, Dovecot still notices all internal changes, but external changes are checked only once every few seconds or so. #### `MAILBOX_SYNC_FLAG_FULL_READ` This is mainly useful with mboxes. If `mbox_dirty_syncs=yes` and a new mail gets appended to mbox by an external program, Dovecot assumes that the only change was the added mail, even though the program may have also modified existing messages' flags by rewriting Status: headers. If `mbox_very_dirty_syncs=no`, these changes are noticed after the next time mailbox is opened. So when this flag is enabled, it means Dovecot should try harder to find out if there were any external unexpected changes. It's currently used only with IMAP SELECT and CHECK commands and POP3 startup. Probably unnecessary elsewhere. #### `MAILBOX_SYNC_FLAG_FULL_WRITE` This is again mainly useful with mboxes. If `mbox_lazy_writes=no`, Dovecot delays writing flag changes to mbox file until mailbox is closed or IMAP CHECK command is issued. Using this elsewhere is probably unnecessary, except as an optimization if mailbox is in any case synced just before closing it, you might as well give this flag to it to avoid double-syncing with mbox. #### `MAILBOX_SYNC_FLAG_FORCE_RESYNC` This is used to force resyncing indexes. The only time this should be done is when manually triggered by administrator. ### Sync Flags: Other Then there are also other syncing flags: #### `MAILBOX_SYNC_FLAG_NO_EXPUNGES` No expunged messages are removed from the in-memory mailbox view. Their removal is delayed until syncing is done without this flag. Attempting to access the expunged messages may or may not work, depending on what information is accessed and what storage backend is used. #### `MAILBOX_SYNC_FLAG_FIX_INCONSISTENT` Normally when the internal mailbox state can't be consistently updated (typically due to index file corruption), the syncing fails. When this flag is set, it means that the caller doesn't care about mailbox's previous state and just wants to get it accessible again. Typically this is used when the mailbox is being opened, but not afterwards. #### `MAILBOX_SYNC_FLAG_EXPUNGE` This is mainly intended for virtual plugin with IMAP protocol. You probably shouldn't use it. ## Reading Changes While `mailbox_sync_next()` returns TRUE, it fills out sync record: | Record | Description | | --- | --- | | `seq1` `seq2` | Message sequence numbers that were affected. | | `type` | Expunge, flag change or modseq change. | Expunge records don't immediately change the view's sequence numbers. After seeing an expunge record you can still fetch the expunged messages' flags and possibly other information. Only after syncing is deinitialized, the sequences change. Message flag change records don't actually show what the changes were. You can find the new flags by fetching them (`mail_get_flags()`, etc.). They're available immediately. You'll need to create a [mailbox transaction](/main/developers/design/mailbox_transaction.html) and a [mail](/main/developers/design/mail.html) for that. For example: c ``` sync_ctx = mailbox_sync_init(box, flags); trans = mailbox_transaction_begin(box, 0); mail = mail_alloc(trans, MAIL_FETCH_FLAGS, 0); ``` If you don't actually care about sync records, you don't necessarily have to even call `mailbox_sync_next()`. In that case it's actually easiest to perform the whole sync using a one-step `mailbox_sync()` function. This function also sets `MAILBOX_SYNC_FLAG_FIX_INCONSISTENT` flag automatically. ## Deinitializing `mailbox_sync_deinit()` finalizes the syncing. If any errors occurred during sync, it'll return -1. If `MAILBOX_SYNC_FLAG_NO_EXPUNGES` was used and some expunges were actually delayed, `status_r->sync_delayed_expunges` is set to TRUE. ## Implementing Sync for a Storage Backend WARNING Talk about `mail_index_sync_*()` and how to change stuff and how to update internal state. # Mailbox Transactions Before you can read any mails or do any changes to mails, you need to create a transaction with `mailbox_transaction_begin()`. ## Mailbox Transaction Flags ### `MAILBOX_TRANSACTION_FLAG_HIDE` Mark the changes in a way that when later [syncing](./mailbox_syncing.html) the mailbox in this session, `mailbox_sync_next()` won't return sync records for the changes done by this transaction. This is primarily meant for flag and keyword changes; you can't hide expunges. For example IMAP's `STORE FLAGS.SILENT` command is implemented by setting this flag for the transaction. ### `MAILBOX_TRANSACTION_FLAG_EXTERNAL` Changes done by this transaction should be marked as external changes. Internal changes can be thought of as "change requests" that syncing later finishes, while external changes are done immediately and syncing ignores them. Normally you would use this flag when you want to save or copy messages. ### `MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS` Require assigning UIDs to saved/copied messages immediately. Normally this is done only when it's easy (maildir: if dovecot-uidlist can be locked without waiting, mbox: if mbox is already fully synced). ### `MAILBOX_TRANSACTION_FLAG_REFRESH` Do a quick index refresh, so any recent flag/modseq changes done by other Dovecot sessions will be visible. You shouldn't usually need this, because usually you should have recently done a mailbox sync. ### `MAILBOX_TRANSACTION_FLAG_NO_CACHE_DEC` Don't update caching decisions no matter what we do in this transaction (useful for e.g. precaching). ### `MAILBOX_TRANSACTION_FLAG_SYNC` Sync transaction describes changes to mailbox that already happened to another mailbox with whom we're syncing with (dsync). ### `MAILBOX_TRANSACTION_FLAG_NO_NOTIFY` Don't trigger any notifications for this transaction. This especially means the notify plugin. This would normally be used only with `MAILBOX_TRANSACTION_FLAG_SYNC`. ## Committing Changes for a transaction are kept in memory until the transaction is committed. If you want to cancel the changes, you can call `mailbox_transaction_rollback()`. Transaction can be committed with `mailbox_transaction_commit()`. If you want to know a bit more about the results of the transaction, use `mailbox_transaction_commit_get_changes()` instead. It returns a change structure: ### `uid_validity` UIDVALIDITY used by returned UIDs ### `saved_uids` UIDs assigned to saved/copied mails. Typically they're in an ascending order, unless you explicitly requested some specific UIDs for mails while saving them (e.g. dsync does this). ### `ignored_modseq_changes` Number of modseqs that couldn't be changed by `mail_update_modseq()` calls, because they would have lowered the modseq. ### `changes_mask` Bitmask of types of changes that occurred within this transaction. ### `no_read_perm` User doesn't have read ACL for the mailbox, so don't show the `uid_validity` / `saved_uids`. ## Deinitialization Once you're done with reading the change structure, be sure to free the memory used by it with `pool_unref(&changes->pool)`. ## Atomic Changes `mailbox_transaction_set_max_modseq()` can be used to implement atomic conditional flag changes. If message's modseq is higher than the given max\_modseq while transaction is being committed, the change isn't done and the message's sequence number is added to the given array. # Mailbox `src/lib-storage/mail-storage.h` and `mail-storage-private.h` describes mailbox API, among others. Mailbox life cycle often goes like: - `mailbox_alloc()` allocates memory for the mailbox and initializes some internal settings, but doesn't actually try to open it. - `mailbox_open()` opens the mailbox. Instead of opening a mailbox, you can also create it with `mailbox_create()`. - If you're immediately syncing the mailbox, you don't need to open it, because it's done implicitly. This reduces your code and error handling a bit. - `mailbox_close()` closes the mailbox, so that it needs to be opened again if it's wanted to be accessed. This is rarely needed. - `mailbox_free()` closes and frees the mailbox. There are a lot of functions to deal with mailboxes. The most important ones are: - `mailbox_get_status()` to get a summary of mailbox, such as number of messages in it. - `mailbox_get_metadata()` to various kinds of metadata of a mailbox, such as the sum of the message sizes inside the mailbox. - [Mailbox Sync](./mailbox_syncing.html): `mailbox_sync_*()` to synchronize changes from the backend to memory. - [Mailbox Transactions](./mailbox_transaction.html): `mailbox_transaction_*()` for transaction handling. All message reads and writes are done in a transaction. - [Mailbox Searching](./mailbox_searching.html): `mailbox_search_*()` is used for searching messages. Even simple operations like "get all messages" go through this API by creating a "search all" query. - [Mailbox Saving](./mailbox_saving.html): `mailbox_save_*()` and `mailbox_copy()` is used for saving/copying new messages to mailbox. # Memory Allocations C language requires explicitly allocating and freeing memory. The main two problems with this are: 1. A lot of allocations and frees cause memory fragmentation. The longer a process runs, the more it could have leaked memory because there are tiny unused free spots all around in heap. 2. Freeing memory is easy to forget, causing memory leaks. Sometimes it can be accidentally done multiple times, causing a potential security hole. A lot of `free()` calls all around in the code also makes the code more difficult to read and write. The second problem could be solved with Boehm garbage collector, which Dovecot used to support, but it wasn't very efficient. It also doesn't help with the first problem. To reduce the problems caused by these issues, Dovecot has several ways to do memory management. ## Common Design Decisions All memory allocations (with some exceptions in data stack) return memory filled with NULs. This is also true for new memory when growing an allocated memory with realloc. The zeroing reduces accidental use of uninitialized memory and makes the code simpler since there is no need to explicitly set all fields in allocated structs to zero/NULL. Note the C standard doesn't require that NULL is zero, but this is practically true everywhere and appears to be a (future) requirement for POSIX as well. In out-of-memory situations memory allocation functions die internally by calling `i_fatal_status(FATAL_OUTOFMEM, ..)`. There are several reasons for this: - Trying to handle malloc() failures explicitly would add a lot of error handling code paths and make the code much more complex than necessary. - In most systems malloc() rarely actually fails because the system has run out of memory. Instead the kernel will just start killing processes. - Typically when malloc() does fail, it's because the process's address space limit is reached. Dovecot enforces these limits by default. Reaching it could mean that the process was leaking memory and it should be killed. It could also mean that the process is doing more work than anticipated and that the limit should probably be increased. - Even with perfect out-of-memory handling, the result isn't much better anyway than the process dying. User isn't any happier by seeing "out of memory" error than "server disconnected". When freeing memory, most functions usually also change the pointer to NULL. This is also the reason why most APIs' deinit functions take pointer-to-pointer parameter, so that when they're done they can change the original pointer to NULL. ## malloc() Replacements `lib/imem.h` has replacements for all the common memory allocation functions: - `malloc`, `calloc` -> `i_malloc()` - `realloc()` -> `i_realloc()` - `strdup()` -> `i_strdup()` - `free()` -> `i_free` All memory allocation functions that begin with `i_` prefix require that the memory is later freed with `i_free()`. This is a macro that is guaranteed to set the freed pointer to NULL afterwards. ## Memory Pools `lib/mempool.h` defines API for allocating memory through memory pools. All memory allocations actually go through memory pools. Even the `i_*()` functions get called through `default_pool`, which by default is `system_pool` but can be changed to another pool if wanted. All memory allocation functions that begin with `p_` prefix have a memory pool parameter, which it uses to allocate the memory. Dovecot has many APIs that require you to specify a memory pool. Usually (but not always) they don't bother freeing any memory from the pool. Instead, they assume that more memory can be just allocated from the pool and the whole pool is freed later. These pools are usually alloconly-pools, but can also be data stack pools. See below. ## Alloc-only Pools `pool_alloconly_create()` creates an allocate-only memory pool with a given initial size. As the name says, alloconly-pools only support allocating more memory. As a special case its last allocation can be freed. `p_realloc()` also tries to grow the existing allocation only if it's the last allocation, otherwise it'll just allocates new memory area and copies the data there. Initial memory pool sizes are often optimized in Dovecot to be set large enough that in most situations the pool doesn't need to be grown. To make this easier, when Dovecot is configured with `--enable-devel-checks`, it logs a warning each time a memory pool is grown. The initial pool size shouldn't of course be made too large, so usually it's best to just pick some small initial guessed size and if there are too many "growing memory pool" warnings start growing the pool sizes. Sometimes there's just no good way to set the initial pool size and avoid the warnings, in that situation you can prefix the pool's name with `MEMPOOL_GROWING` which prevents logging warnings about the pool. Alloconly-pools are commonly used for an object that builds its state from many memory allocations, but doesn't modify (much of) its state. It's a lot easier when you can do a lot of small memory allocations and in object destroy you simply free the memory pool. ## Data Stack `lib/data-stack.h` describes the low-level data stack functions. Data stack works a bit like C's control stack. `alloca()` is quite near to what it does, but there's one major difference: In data stack the stack frames are explicitly defined, so functions can return values allocated from data stack. `t_strdup_printf()` call is an excellent example of why this is useful. Rather than creating some arbitrary sized buffer and using `snprintf()`, which might truncate the value, you can just use `t_strdup_printf()` without worrying about buffer sizes being large enough. Try to keep the allocations from data stack small, since the data stack's highest memory usage size is kept for the rest of the process's lifetime. The initial data stack size is 32kB, which should be enough in normal use. If Dovecot is configured with `--enable-devel-checks`, it logs a warning each time the data stack needs to be grown. Stack frames are preferably created using a `T_BEGIN`/`T_END` block, for example: c ``` T_BEGIN { string_t *str1 = t_str_new(256); string_t *str2 = t_str_new(256); /* ... */ } T_END; ``` In the above example the two strings are allocated from data stack. They get freed once the code goes past `T_END`. That's why the variables are preferably declared inside the `T_BEGIN`/`T_END` block so they won't accidentally be used after they're freed. `T_BEGIN` and `T_END` expand to `t_push()` and `t_pop()` calls and they must be synchronized. Returning from the block without going past `T_END` is going to cause Dovecot to panic in next `T_END` call with "Leaked t\_pop() call" error. Data stack allocations have similar disadvantages to alloc-only memory pools. Allocations can't be grown, so with the above example if str1 grows past 256 characters, it needs to be reallocated, which will cause it to forget about the original 256 bytes and allocate 512 bytes more. However, as with alloc-only pools, the last allocation can be grown. Memory allocations from data stack often begin with `t_` prefix, meaning "temporary". There are however many other functions that allocate memory from data stack without mentioning it. Memory allocated from data stack is usually returned as a const pointer, so that the caller doesn't try to free it (which would cause a compiler warning). When should `T_BEGIN`/`T_END` used and when not? This is kind of black magic. In general they shouldn't be used unless it's really necessary, because they make the code more complex. But if the code is going through loops with many iterations, where each iteration is allocating memory from data stack, running each iteration inside its own stack frame would be a good idea to avoid excessive memory usage. It's also difficult to guess how public APIs are being used, so it's often good for such API functions use their own private stack frames. Dovecot's ioloop code also wraps all I/O callbacks and timeout callbacks into their own stack frames, so you don't need to worry about them. It's actually a good idea for any callback to be called with its own data stack frame. You can create memory pools from data stack too. Usually you should be calling `pool_datastack_create()` to generate a new pool, which also tries to track that it's not being used unsafely across different stack frames. Some low-level functions can also use the slightly more efficient `unsafe_data_stack_pool` as the pool, which doesn't do such tracking. Data stack's advantages over malloc(): - FAST, most of the time allocating memory means only updating a couple of pointers and integers. Freeing memory all at once also is a fast operation. - No need to `free()` each allocation resulting in prettier code - No memory leaks - No memory fragmentation It also has some disadvantages: - Allocating memory inside loops can accidentally allocate a lot of memory - Memory allocated from data stack can be accidentally stored into a permanent location and accessed after it's already been freed. - Debugging invalid memory usage may be difficult using existing tools # Output Streams `lib/ostream.h` describes Dovecot's output streams. Output streams can be stacked on top of each others as many times as wanted. Output streams actually writing data: - file: Write to given fd using `pwrite()` for files and `write()` for non-files. - unix: Write to given UNIX socket. Similar to file, but supports sending file descriptors. - buffer: Write to [buffer](./buffers.html). Output stream filters: - hash: Calculate hash of the ostream while it's being written. - escaped: Write output escaped via callback. Built-in support for HEX and JSON escaping. - multiplex: Multiplex-iostreams support multiple iostream channels inside a single parent istream. - null: All the output is discarded. - failure-at: Insert a failure at the specified offset. This can be useful for testing. - lib-dcrypt/encrypt: Write encrypted data. - wrapper: Can be used to implement other ostreams where data can be coming from any form of activity. - `lib-compression/*`: Write zlib/bzlib/lz4/zstd compressed data. There are also various other less generic ostreams. A typical life cycle for an ostream can look like: - `o_stream_create()` - `o_stream_cork()` - `o_stream_nsend*()` one or more times - `o_stream_uncork()` - If necessary, check errors with `o_stream_flush()` - `o_stream_cork()` - `o_stream_nsend*()` one or more times - `o_stream_uncork()` - finalize the ostream with `o_stream_finish()` - optionally close the ostream with `o_stream_close()` - unref or destroy Once the ostream is finished, it can't be written to anymore. The `o_stream_finish()` call writes any potential trailer that the ostream may have (e.g. ostream-gz, ostream-encrypt, ostream-dot) while still allowing the caller to check if the trailer write failed. After `o_stream_finish()` is called, any further write will panic. The ostreams that require a trailer will panic if `o_stream_finish()` hasn't been called before the stream is destroyed, but other ostreams don't currently require this. Still, it's not always easy to know whether there might be ostreams that require the trailer, so if there's any doubt, it's preferred to call `o_stream_finish()` just before destroying the ostream. Usually calling `o_stream_finish()` will also finish its parent ostream. This may or may not be wanted depending on the situation. For example ostream-dot must be finished to write the last "." line, but ostream-dot is always a sub-stream of something else that must not be finished yet. This is why ostream-dot by default has called `o_stream_set_finish_also_parent(FALSE)`, so finishing the ostream-dot won't finish the parent stream. Similarly `connection.c` API sets `o_stream_set_finish_via_child(FALSE)` so none of the socket connections created via it will be finished even though one of their sub-streams is finished. These functions may need to be called explicitly in other situations. When doing a lot of writes, you can simplify the error handling by delaying the error checking. Use the `o_stream_nsend*()` functions and afterwards check the error with `o_stream_flush()` or `o_stream_finish()`. If you forgot to do this check before the ostream is destroyed, it will panic with: `output stream %s is missing error handling` regardless of whether there is an error or not. If you don't care about errors for the ostream (e.g. because it's a client socket and there's nothing you can do about the write errors), you can use `o_stream_set_no_error_handling()` to fully disable error checks. You can also use `o_stream_ignore_last_errors()` to ignore the errors so far, but not for future writes. Writes are non-buffered by default. To add buffering, use `o_stream_cork()` to start buffering and `o_stream_uncork()` to stop/flush. When output buffer gets full, it's automatically flushed even while the stream is corked. The term "cork" is used because with TCP connections the call actually sets/removes TCP cork option. It's quite easy to forget to enable the corking with files, making the performance worse. The corking/uncorking is done automatically while running a flush callback (set via `o_stream_set_flush_callback()`). Using `o_stream_uncork()` will trigger an automatic `o_stream_flush()` but the error is ignored. This is why it acts similarly to `o_stream_nsend*()`, i.e. it requires another explicit `o_stream_flush()`, `o_stream_finish()` or error ignoring before the ostream is destroyed. If output buffer's size isn't unlimited, the writes can also fail or be partial because there's no more space in the buffer and `write()` syscall is returning `EAGAIN`. This of course doesn't happen with blocking fds (e.g. files), but you need to handle this in some way with non-blocking network sockets. A common way in Dovecot to handle this is to just use unlimited buffer sizes and after each write check if the buffer size becomes too large, and when it does it stops writing until more space is available. # Overview ## Dovecot Design - [Overview of Dovecot processes](./processes.html) - [Design of Index Files](./indexes/index_format.html) - [API for Accessing the Index Files](./indexes/mail_index_api.html) - [Design of Authentication Process](./auth_process.html) - [Authentication Protocol](./auth_protocol.html) - [Doveadm Server Protocol](./doveadm_protocol.html) and [Doveadm HTTP API](/main/core/admin/doveadm.html#http-api) - [Doveadm Synchronization](./dsync.html) - [Dovecot Lua Support](/main/core/admin/lua/index.html) - [Dovecot Dict Protocol](./dict_protocol.html) ## Protocol Extensions - [Forwarding Parameters in IMAP/POP3/LMTP/SMTP Proxying](/main/core/config/proxy/forwarding.html) ## Code APIs - [Coding Style Guide](/main/developers/coding_style.html) - explanations how and why the coding style is the way it is. Look at the \*.h files for the actual API documentation. The documentation below doesn't attempt to list full API documentation. liblib: - [Memory Allocations](./memory.html) - [Static/Dynamic Buffers](./buffers.html) - [Dynamic Arrays](./arrays.html) - [String Handling](./strings.html) - [Input Streams](./istreams.html) - [Output Streams](./ostreams.html) - [Events](./events.html) - [Plugins](./plugins.html) lib-dcrypt: - [lib-dcrypt Data Formats](./dcrypt.html) lib-storage: - [Mail User](./mail_user.html) contains everything related to a single user. - [Mail Namespace](./mail_namespace.html) A single user can contain multiple namespaces. - [Mailbox List](./mailbox_list.html) is used to list/manage a list of mailboxes for a single namespace (1:1 relationship). - [Mail Storage](./mail_storage.html) is used to access mails in a specific location with a specific mailbox format. Multiple namespaces can point to the same storage. A single namespace may in future (but not currently) point to multiple storages (e.g. a mixed mbox and Maildir directory). - [Mailbox](./mailbox.html) is used to access a specific mailbox in a storage. - [Mail](./mail.html) is used to access a specific mail in a mailbox. - [Error Handling](./storage_errors.html) - [Plugins](./mail_plugins.html) - how to hook into lib-storage functions. # Plugins Plugins in Dovecot are really simple. They basically have two functions: - `_init(module)` is called when `module_dir_init()` is called. - `_deinit()` is called when `module_dir_deinit()` or `module_dir_unload()` is called. The `` is the short version of the plugin name, based on the filename. For example if the filename is `lib11_imap_quota_plugin.so`, the `` is `imap_quota` and the init function to be called is `imap_quota_plugin_init()`. ## Versioning Since different Dovecot versions can have different APIs, your plugin should usually also define `_version`, like: c ``` const char *imap_quota_plugin_version = DOVECOT_ABI_VERSION; ``` If the version string in plugin doesn't match the version of the running binary, the plugin loading fails. The `DOVECOT_ABI_VERSION` is defined in Dovecot's `config.h`, which you're typically including. It's possible to check the Dovecot version number with a macro. This allows either supporting different Dovecot APIs or giving a clear error message if the API is too old to support your plugin. For example: ``` #if ! DOVECOT_PREREQ(2, 3, 18) # error Must have at least v2.3.18 #endif ``` ## Dependencies Some plugins depend on another one. In some systems (but not all) it's possible to handle this by giving a nicer error message than "symbol xyz not found". There are two steps for this: First create `_dependencies` array listing plugin names that the plugin depends on, like: c ``` const char *imap_quota_plugin_dependencies[] = { "quota", NULL }; ``` Then you'll also have to make the plugin .so binary link to the other plugins: ``` if PLUGIN_DEPS lib11_imap_quota_plugin_la_LIBADD = \ ../quota/lib10_quota_plugin.la endif ``` `PLUGIN_DEPS` is set only if plugin dependencies are actually supported. Otherwise the build might fail or plugin loading might fail. Once all this is done, trying to load imap\_quota plugin without quota plugin gives a nice error message: ``` Error: Can't load plugin imap_quota_plugin: Plugin quota must be loaded also ``` ## Hooks Different kinds of plugins can also hook into various things: - imap: `imap_client_created_hook_set()` - pop3: `pop3_client_created_hook_set()` - submission: `submission_client_created_hook_set()` - lmtp: `hook_client_created` - lib-storage: `mail_storage_hooks_add()` # Dovecot processes Dovecot is split into multiple processes where each process does only one thing. This is partially because it makes the code cleaner, but also because it allows setting up different privileges for each process. The most important processes are: - Master process (dovecot) - Log process (log) - Config process (config) - Authentication process (auth) - Login processes (imap-login, pop3-login) - Mail processes (imap, pop3, lmtp) ![Auth](/main/assets/auth.BZPaFxgP.png) ## Master Process This process keeps all the other processes running. If a child process dies, another one is restarted automatically if necessary. The master process runs as root, so its functionality is attempted to be kept minimal. The master process is the only process that opens all inet, unix socket and fifo listeners. These listener fds are then passed to child processes that it forks. Each service has a pipe that is used by the service's processes to send status updates to master about how many client connections it can still accept. If the master process sees that the service is completely full and can't accept any more client connections, it logs a warning and eventually starts rejecting the client connections for the service. The login processes are special however: When master process notices that all login processes are full, it instead notifies them about it. The login processes then start closing their oldest connections in order to make space for more client connections. This prevent DoSing the login services by simply opening many idling connections. ## Log Process Most of the logging is done via the log process. Only the master process and processes that start up standalone (e.g. `dovecot-lda`) bypass the log process. The Dovecot master process sets up a separate pipe for each service, which is shared by all the processes of that service. The write side of the pipe becomes the processes' stderr fd, while the read side is read by the log process. Log process has a few benefits over direct logging: - Log process can start throttling a service that logs too rapidly. - All stderr output logged by processes will be caught by the log process, so any errors printed by libraries will not be lost. These errors will also always have a log prefix showing which service caused the error. - Avoids duplicate log lines about crashes. If a panic is logged before a process crashes, there's no unnecessary line logged about process dying with signal 6. ## Config Process The config processes parse the configuration file and feed the parsed output in a simplified format to all the other processes via UNIX socket connections. The master process reads its configuration in a different way: It first executes the `doveconf` binary, which reads the configuration into environment variables and then executes back the dovecot master binary. Standalone tools like `dovecot-lda` and `doveadm` first try to read the configuration via UNIX socket connection, but if that fails they do it by executing `doveconf`. ## Authentication Process The auth process handles everything related to the actual authentication: SASL authentication mechanisms, looking up and verifying the passwords and looking up user information. There is only a single auth master process, which accepts all incoming connections. This means that the auth process needs to be very efficient in what it does and must not block for long or it will cause all the authentications to hang. To handle potentially long-running blocking operations there are auth worker processes. These are often used for passdb and userdb lookups. The auth worker processes can also be used for verifying password hashes, which may be necessary if strong hashing algorithms are used. ## Login Processes The login processes implement the required minimum of the IMAP, POP3, ManageSieve or Submission protocols before a user logs in successfully. Each protocol is handled by a separate process (and binary). These processes are run with least possible privileges. Unfortunately the default UNIX security model still allows them to do much more than they would have to: Accept new connections on a socket, connect to new UNIX sockets and read and write to existing file descriptors. Still, the login process is by default run under a user account that has no special access to anything, and runs inside a non-writable chroot where only a couple of UNIX sockets exist. Doing any damage inside there to the server itself should be difficult. It could of course still create connections to other services that would normally be unavailable from external IP addresses. When a new connection comes, one of the login processes accept()s it. After that the client typically does nothing more than ask the server's capability list and then log in. The client may also start TLS session before logging in. Authentication is done by talking to the authentication process. The login process is untrusted by the authentication process, so even if an attacker is able to execute arbitrary code inside a login process, they won't be able to log in without a valid username and password. After receiving a successful authentication reply from the authentication process, the login process connects to the mail process via UNIX socket and sends the file descriptor it. The mail process verifies from the auth process that the authentication really was successful. By default each login process will handle only a single connection and afterwards kill itself (but see SSL proxying below). This way attacker can't see other people's connections. This can however be disabled, in which case the security of the design suffers greatly. The login processes handle SSL/TLS connections themselves completely. They keep proxying the connection to mail processes for the entire lifetime of the connection. This way if a security hole is found from the SSL library, an authenticated user still can't execute code outside the login process. See [login processes](/main/core/config/login_processes.html) for more information about different settings related to login processes. ## Mail Processes These processes handle the actual post-login mail handling using the privileges of the logged in user. ## Other Processes There are also various other processes commonly used: - anvil: Keep track of which mail processes handle which users - dict: Proxy process for dict lookups - dns-client: Asynchronous DNS lookups - imap-hibernate: IDLEing imap connections can be moved to hibernation processes - indexer and indexer-worker: For full text search indexing - stats: Tracking statistics # Lib-storage Error Handling `src/lib-storage/mail-error.h` describes different types of errors and has some other error-related functions and macros. Only functions returning "int" can actually return a failure. - Functions that return a pointer will never use NULL as a failure. Only "find" type of functions can return NULL, which means "not found". - Iterators usually work by the init() returning iterator pointer and next() returning a boolean. If there were any errors in either init() or next(), deinit() finally returns a failure. ## Getting lib-storage Errors There are two types of errors: Errors that can safely be sent to clients, and internal errors which may contain sensitive information. Try to name the client-safe error variables clearly, preferably `client_error`. - `mailbox_list_*()` functions set their errors to the given mailbox\_list structure. You can get these errors with `mailbox_list_get_last_error()` (client-safe error) or `mailbox_list_get_last_internal_error()`. - All other functions that have some way of accessing mail\_storage (mailbox, mail, transactions, etc.) set their errors to the storage. You can get these errors with `mail_storage_get_last_error()` (client-safe error) or `mail_storage_get_last_internal_error()`. There are also `mailbox_get_last_error()` and `mailbox_get_last_internal_error()`, which are simply convenience wrappers to the `mail_storage_get_last_*error()` functions. - Mail user and namespace functions have their own error handling, typically by returning error strings as parameters. - Both `*_get_last_error()` functions should be called soon after the error is noticed, before other failing lib-storage calls overwrite the error. - If there are multiple errors, it's best to always log at least the first one. It's usually the first error that is the most relevant while the other errors are caused by the first one. ## Setting lib-storage Errors Errors can be set with two calls: - `mailbox_list_set_error()` and `mail_storage_set_error()` should be used when the error is user's fault in some way. For example invalid mailbox name, out of quota, etc. The error string will be shown to user. It won't be written to a log file. - `mailbox_list_set_critical()`, `mail_storage_set_critical()`, `mailbox_set_critical()` and `mail_set_critical()` should be used when the error is a problem in the system and sysadmin should be notified. Try to use the most specific object (mail, mailbox, storage) to get the most accurate event and log prefix. The critical errors could be for example out of disk space or just in general an unexpected syscall failure. The client-visible error string is "Internal error occurred" followed by a timestamp, which can be used to try to find the error from the log files. 1. Only log errors that sysadmin can do something about. 2. Never show user anything even potentially sensitive about the system, such as path names. There are also a few other calls that aren't used as often, but can be helpful: - `mail_storage_set_internal_error()` and `mailbox_list_set_internal_error()` simply set the user-visible error message to "Internal error occurred". These can be used if the actual error was already logged. - `mail_storage_set_error_from_errno()` and `mailbox_list_set_error_from_errno()` set the user-visible error message based on some common `errno` values. Currently: - `EACCESS`, `EPERM`, `EROFS`: Permission denied - `ENOSPC`, `EDQUOT`: Not enough disk space - `ENOENT`, `ENOTDIR`: Not found - `ELOOP`: Directory structure is broken - `mail_storage_copy_list_error()` copies the error from `mailbox_list` into `mail_storage`. - `mail_storage_copy_error()` copies the error from one storage to another. - `mailbox_set_index_error()` and `mail_storage_set_index_error()` copies the internal error from `mail_index`. # Dynamic Strings `lib/str.h` describes Dovecot's dynamically growing strings. Strings are actually only a simple wrapper on top of [buffers](./buffers.html). Even the `string_t` type is only a typedef of `buffer_t`, so it's possible to use `buffer_*()` functions with strings (although it's ugly so it should be avoided). The decision of whether to use a `string_t` or a `buffer_t` is mainly for human readability: If the buffer's contents are (ASCII/UTF8) text use `string_t`, otherwise for binary data use `buffer_t`. Once you've finished modifying a string with `str_*()` functions, you can get it out as a NUL-terminated string with `str_c()` or `str_c_modifiable()`. These pointers shouldn't be accessed after modifying the string again, since they could have moved elsewhere in memory and they're no longer guaranteed to be NUL-terminated. Example: c ``` T_BEGIN { string_t *str = t_str_new(64); str_append(str, "hello world"); str_printfa(str, "\nand %zu", str_len(str)); printf("%s\n", str_c(str)); } T_END; ``` ## String Handling Functions `lib/strfuncs.h` contains a lot of functions intended to make string handling easier. They use C's NUL-terminated strings instead of Dovecot's dynamic strings. - `[ipt]_strdup_printf()` and `[ipt]_strconcat()` are the most commonly used functions. `*_strconcat` is slightly faster than `*_strdup_printf()`, so use it if you simply need to concatenate strings. - Various functions for doing a `strdup()` from wanted input. - `i_snprintf()` is a wrapper to `snprintf()` that makes it easier to check if result was truncated. It also adds a few other safety checks. This should be avoided in general, except in situations where you just don't want to use data stack and there's no way for the result to get truncated. - `i_strocpy()` is similar to `strlcpy()`, but makes it easier to check if result was truncated. This has the same problems as `i_snprintf()`. - Functions for uppercasing and lowercasing strings. - Functions you can pass to `bsearch()` and `qsort()` for handling string arrays. - `[pt]_strsplit()` is an easy way to split a string into an array of strings from given separator. - `t_strarray_join()` reverses this. - There are also some other functions to handle an array of strings, like getting its length or finding a given string. - `dec2str()` can be used to convert a number to a string allocated from data stack. This can be useful if you don't know the correct type and don't want to add casting (that could potentially truncate the string). For example: `print("pid = %s\n", dec2str(getpid()));` ## String Escaping `lib/strescape.h` contains functions to escape and unescape `"`, `'` and `\` characters in strings using the `\` character. Dovecot's internal protocols are often line-based with TAB as the field separator. This file also contains functions to escape and unescape such data. # Variable expansion design Dovecot comes with powerful variable expansion system, which allows constructing reusable text templates. This has been upgraded since v2.3 to a more flexible system. ## Syntax ``` ::= ::= ::= , "{" "}", "%" ::= "|" , ::= ::= ::= , ::= "-" , ::= "+", "-", "*", "/" ::= ::= "(" ")" ::= "," ::= , , , "=" , "=" , "=" NAME = string VALUE = "string" or 'string' NUMBER = [0-9]+ ``` ## Design Internally, everything is stored in a binary-safe string container. There is no other data type internally. This buffer can be set and unset, and the content can be tagged by filters to be binary or string. The system uses programs to perform the actual expansion. The given input is always first parsed into a list of programs. Input that consists from multiple expansions separated by non-expansion strings is split into multiple programs. Once program is compiled, it can be executed multiple times with different parameters. Program parameters consists from variable table(s), provider(s) and escape function. A program can be executed with different parameters. ## Parameters Parameters are provided via `struct var_expand_params`. Variable mappings are provided via `struct var_expand_table` array, which is `VAR_EXPAND_TABLE_END` terminated list of key-value mappings. Key's value can also be provided by a function. Providers are used to handle scoped variables, such as passdb, ldap etc. There are also global providers which are always available. Providers can be provide with `struct var_expand_provider` array which contains prefix and provider function, and is `VAR_EXPAND_TABLE_END` terminated. It is also possible to provide escape function, which is applied to each %{pipeline} output. Key functions and providers use the same context. Key-value tables and providers can also be provided as arrays of arrays, which must be NULL terminated. Contexts for these must be provided in an array that is `VAR_EXPAND_CONTEXTS_END` terminated. When these arrays are used, first match wins. ## Filters Filters are functions that accept input from left side and emit output to right side. They can accept positional and named parameters. Some filters can start expressions, namely ones that do not require any input. ## Variables Variables are always considered to be strings. NULL value is considered same as empty value. Variable names are unique, and if table contains multiple variables with same name, the first is always used. Variable can exist as parameter to filters or as the first token in expression. ## Output handling If a program ends up with binary tagged output, the output is automatically hex-encoded. If there is no key in table or provider, error will occur. This error can be negated with `default(value)` filter, which clears error. # Development Tips ## Clang Patching Dovecot is developed with some extra warning features developed for clang. Here's how to build such a patched clang. ### Prerequisites To build llvm/clang, you'll need cmake version 3.4.3. ### Source Repos There are github mirrors, but you can clone directly from [llvm.org](https://llvm.org/). sh ``` git clone https://llvm.org/git/llvm cd tools git clone https://llvm.org/git/clang ``` Here are some hints on how to build/hack clang: - If you want to debug clang with gdb, you need to have a debug build. This used to be the default, but apparently not nowadays. Note that Debug build takes ~15 GB of space vs non-debug build 5 GB. `cmake -DCMAKE_BUILD_TYPE=Debug` - With debug build you still want it to be optimized or the resulting compiler will be horribly slow. `cmake -DLLVM_OPTIMIZED_TABLEGEN=ON` - Even then it seems to be very slow. It's possibly enough to use `-DCMAKE_BUILD_TYPE=RelWithDebInfo` sh ``` mkdir build cd build cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ \ -DLLVM_OPTIMIZED_TABLEGEN=ON -DCMAKE_BUILD_TYPE=Debug -G "Unix Makefiles" ../ ``` If you want to debug clang, you need to debug "clang -cc1" because otherwise it forks a new process and you can't do much with it. See [https://clang.llvm.org/docs/FAQ.html#id2](https://clang.llvm.org/docs/FAQ.html#id2) ### Dovecot Patches Dovecot has some clang patches in [https://dovecot.org/patches/clang/](https://dovecot.org/patches/clang/). These patches aid with sanity checking the dovecot build, e.g. static code analysis: - `clang-strict-bool.diff` - This is the most important one that implements `-Wstrict-bool`. - `clang-default-nonnull.diff` - Add support for automatically marking pointer parameters as non-null. This was never fully implemented to work nicely. ## Code Coverage Testing Compile using `--coverage` flags: sh ``` EXTRA_CFLAGS=--coverage EXTRA_LDFLAGS=--coverage ./configure ``` Build and run unit tests: sh ``` make check NOVALGRIND=1 ``` Get coverage as HTML: sh ``` lcov -q --directory . --capture -o cov.info summary="`lcov --summary cov.info 2>&1 \| sed -e 's/$/
/'`" genhtml -q -k --legend -o cov cov.info ``` The HTML content is now in the "cov" directory. Note that the lcov command can generate some warnings about missing \*.gcda files, like: ``` geninfo: WARNING: gcov did not create any files for .../src/lib-otp/.libs/otp-parity.gcda! geninfo: WARNING: gcov did not create any files for .../src/lib/.libs/ioloop-notify-kqueue.gcda! geninfo: WARNING: gcov did not create any files for .../src/lib/.libs/ioloop-notify-none.gcda! ``` ### clang If using clang, create llvm-gcov.sh script: bash ``` #!/bin/bash exec llvm-cov gcov "$@" ``` Then add `--gcov-tool llvm-gcov.sh` parameter: sh ``` lcov -q --gcov-tool llvm-gcov.sh --directory . --capture -o cov.info ``` ### Issues Code coverage output isn't written if `_exit()` is used. # Dovecot Developer Resources These pages are targeted for developers working on Dovecot. They are meant to help understand Dovecot internals as well as get the reader in touch with internal APIs. # How To's This directory contains a collection of documents that involve how to configure and extend Dovecot for various use cases. This information is provided as a benefit to the community, but this information may be outdated or incorrect as it is not core Dovecot documentation. # Authentication Against Active Directory When connecting to AD, you may need to use port 3268. Then again, not all LDAP fields are available in port 3268. Use whatever works. See: [https://technet.microsoft.com/en-us/library/cc978012.aspx](https://technet.microsoft.com/en-us/library/cc978012.aspx). # LDAP Driver Configuration dovecot.conf doveconf ``` passdb ldap { ... } ``` This enables LDAP to be used as passdb. The most important settings are: - Configure how the LDAP server is reached. (Active directory allows binding with username@domain): dovecot.conf doveconf ``` ldap_uris = ldap://ldap.example.com ldap_auth_dn = cn=admin,dc=example,dc=com ldap_auth_dn_password = secret ldap_base = dc=example,dc=com ``` - Use LDAP authentication binding for verifying users' passwords: dovecot.conf doveconf ``` passdb ldap { bind_userdn = %{user} bind = yes } ``` - Use auth worker processes to perform LDAP lookups in order to use multiple concurrent LDAP connections. Otherwise only a single LDAP connection is used. dovecot.conf doveconf ``` (passdb|userdb) ... { use_worker = yes } ``` - Normalize the username to exactly the `mailRoutingAddress` field's value regardless of how the `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)` found the user: dovecot.conf doveconf ``` passdb ldap { fields { user = %{ldap:mailRoutingAddress} password = %{ldap:userPassword} proxy = y proxy_timeout = 10 } } ``` - Returns userdb fields when prefetch userdb wasn't used (LMTP & doveadm). The username is again normalized in case `user_filter` found it via some other means: dovecot.conf doveconf ``` passdb ldap { fields { user = %{ldap:mailRoutingAddress} quota_storage_size = %{ldap:messageQuotaHard}B } } ``` - How to find the user for passdb lookup (this can be set specifically to distinct values inside each `[passdb](/main/core/summaries/settings.html#passdb)` / `[userdb](/main/core/summaries/settings.html#userdb)` section): dovecot.conf doveconf ``` passdb ldap { filter = (mailRoutingAddress=%{user}) } ``` - How to iterate through all the valid usernames: dovecot.conf doveconf ``` userdb ldap { filter = (mailRoutingAddress=%{user}) iterate_filter = (objectClass=messageStoreRecipient) iterate_fields { user = %{ldap:mailRoutingAddress} } } ``` # Configuring Autoforward Sender Address Customer would like to have auto forwarding feature as described below: - Scenario: `(User A -> User B (Auto forwarder) -> User C)` 1. `B@example2.com` is a user at customer. 2. An email is sent from `A@example1.com` to `B@example2.com` 3. `B@example2.com` set an auto-forward rule so emails are being forwarded to `C@example3.com` automatically. 4. `C@example3.com` received the forwarded mail. - Current behavior : The mail received by `C@example3.com` has `mail from` header `A@example1.com` (i.e. the `mail from` is unchanged by client when forwarding to user C) - Requested behavior : The mail received by `C@example3.com` has `mail from` header `B@example2.com` (i.e. the `mail from` has been changed by user B when forwarding to user C, so the mail looks like as if it was originally sent by user B) - Reason for the request : Currently, the mails being auto forwarded by user B are occasionally get failed to pass SPF check somewhere in the path, which will result that the mail cannot be forwarded to user C. ## Solution The following is an example of what the Sieve config and rules could look like: dovecot.confSieve Rule doveconf ``` # Use editheader sieve_extensions { editheader = yes } ``` ``` require "editheader"; require "variables"; require "envelope"; # ... Any other rules # Obtain the user's full email address somehow # It can be obtained from the recipient address (without full name) # An alternative is to put the primary address into the script as a literal. if envelope :matches "to" "*" { set "user_email" "${1}"; } # This part of the script MUST be the final rule, otherwise other rules are # affected since the message is modified. # Drop the original "From:" header deleteheader "from"; # Add a new "From:" header addheader "From" "${user_email}"; redirect "forward@example.com"; ``` INFO It is very important to make sure that the deleteheader, addheader, redirect commands are the last rule in the sieve script, as this would affect other actions as well. # Converting Password Schemes ## Introduction Through the years computers are being faster and faster, and so with it the encryption of passwords have to more secure. In this example we convert passwords stored in MySQL with basic CRYPT-encryption to SSHA256-encryption (Salted SHA256). See [Password Schemes](/main/core/config/auth/schemes.html) for a list of supported password schemes. We used php to generate the new passwords, but you can use any language you want Alternative example can be found at: [https://kaworu.ch/blog/2016/04/20/strong-crypt-scheme-with-dovecot-postfixadmin-and-roundcube](https://kaworu.ch/blog/2016/04/20/strong-crypt-scheme-with-dovecot-postfixadmin-and-roundcube). ## Example - Copy the CRYPT-passwords to a new field (newpassword) but with the prefix `{CRYPT}`. This might start you off in the right direction for mysql: `UPDATE 'your_table' SET field_name = CONCAT('{CRYPT}', field_name)` - Change `dovecot.conf`, so it will look at the new fields Details doveconf ``` passdb sql { # Comment default_pass_scheme so dovecot will look at the prefix # default_pass_scheme = CRYPT # update your sql query so it will look at the new field # AND add a %{password} field in the query so we have the plain password in # our Enviroment ($PLAIN_PASS) query = SELECT id as user, newpassword as password, home as userdb_home, uid as userdb_uid, gid as userdb_gid, '%{password}' as userdb_plain_pass \ FROM users \ WHERE id = '%{user}' # Alternatively, here is another config that worked for me with # SHA512-CRYPT (note: uncomment the lines relevant for your setup): # # driver = mysql # connect = host=127.0.0.1 user=mailauth password=secret dbname=postfixadmin # default_pass_scheme = SHA512-CRYPT # query = SELECT username AS user, password, CONCAT('/var/mail/vdomains/', maildir) as userdb_home, 'vmail' as userdb_uid, 'vmail' as userdb_gid, '%{password}' as userdb_plain_pass \ # FROM mailbox \ # WHERE username = '%{user}' } userdb sql { # query = SELECT CONCAT('/var/mail/vdomains/', maildir) AS home, 'vmail' AS uid, 'vmail' AS gid, password \ # FROM mailbox \ # WHERE username = '%{user}' AND active = 1 ``` - `dovecot.conf`: doveconf ``` userdb prefetch { } ``` - Now reload dovecot, and see everything is still working. - Make the postlogin-script (which is executed after login) and save it as `/usr/local/etc/popafter.sh`: sh ``` #!/bin/sh /usr/local/etc/convertpw.php $USER $PLAIN_PASS exec "$@" ``` - Make the php-script which updates the password and save it as `/usr/local/etc/convertpw.php`: Details php ``` #!/usr/local/bin/php ``` - Update your `dovecot.conf` so it will use the scripts we just made: doveconf ``` # insert these lines so dovecot uses our scripts service pop3 { executable = pop3 pop3-postlogin } service pop3-postlogin { executable = script-login /usr/local/etc/popafter.sh user = $SET:default_internal_user unix_listener pop3-postlogin { } } ``` - Reload Dovecot. As of now, each user which connects through POP will convert their password to SSHA256. If you look at the database you will see, for example, `{SSHA256.hex}fb0e7f39c88c1d7017169f7f6b9cd6977d1e3291149382b90da4a390a31e81bab3cdced8` instead of `{CRYPT}$1$.gvrgDqc$Slvoapz5zkpVmmJAxi.0k1`. If you are using IMAP, you will need to add the same kind of commands (i.e. imap-postlogin) to your config, too. When every record is updated, you can update `dovecot.conf` (remove the extra lines). ## SHA512-CRYPT To use SHA512-CRYPT passwords use `/usr/local/etc/popafter.sh`: sh ``` #!/bin/sh DOVECOTPW=$(doveadm pw -s SHA512-CRYPT -p $PLAIN_PASS) /usr/local/etc/convertpw.php $USER $DOVECOTPW exec "$@" ``` A variant that does not leak the password to the process list: Details ScriptAlternate Version/usr/local/etc/convertpw.php sh ``` #!/bin/sh NEWPASSWORD=$(doveadm pw -s SHA512-CRYPT <> /tmp/log # echo "PLAIN-PASS: $PLAIN_PASS" >> /tmp/log DOVECOTPW=$(/usr/local/bin/doveadm pw -s SHA512-CRYPT -p "$PLAIN_PASS") # echo $DOVECOTPW >> /tmp/log /usr/local/etc/convertpw.php $USER $DOVECOTPW exec "$@" # note: if enabled, some of the lines above will log passwords to /tmp/log. # Create the file first, and delete it when no longer needed - # this while approach is a security risk and should *never* be done in a # production system. I had to use it for troubleshooting for a very # limited period of time. ``` ``` #!/usr/bin/php ``` # selinux sh ``` chcon -u system_u /usr/local/etc/convertpw.php chcon -t bin_t /usr/local/etc/convertpw.php chcon -u system_u /usr/local/etc/popafter.sh chcon -t bin_t /usr/local/etc/popafter.sh ``` ## Example for SHA512-Crypt with passwd-files This example has been tested on Dovecot 2.2.19 in a virtual user setup. Create a new service for the postlogin script and reference it in the `imap` service section. `/etc/dovecot/conf.d/10-master.conf`: doveconf ``` service imap { executable = imap imap-postlogin unix_listener imap-master { user = dovecot } } service imap-postlogin { executable = script-login /var/vmail/conf.d/scripts/postlogin.sh user = vmail unix_listener imap-postlogin { } } ``` Enable the `plain_pass` variable in the auth-passwdfile configuration. doveconf ``` passdb passwd-file { passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } userdb passwd-file { passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd fields { plain_pass = %{password} } } ``` This script will act on all users for a particular domain specified via the `MIGRATE_DOMAIN` variable. `/var/vmail/conf.d/scripts/postlogin.sh`: sh ``` #!/bin/sh # Split out domain part from $USER user@domain MAIL_ALIAS=${USER%@*} MAIL_DOMAIN=${USER#*@} MIGRATE_DOMAIN="domain.tld" case "$MAIL_DOMAIN" in $MIGRATE_DOMAIN) DOVECOTPW=$(/usr/bin/doveadm pw -s SHA512-CRYPT -p "$PLAIN_PASS") echo "user: $USER" >> /tmp/log echo $DOVECOTPW >> /tmp/log ;; esac exec "$@" ``` Exemplary directory permissions (Setup is using `vmail` context for the users): sh ``` ls -l /var/vmail/conf.d/scripts/ ``` ``` -r-x------ 1 vmail vmail 322 Nov 23 09:58 postlogin.sh ``` sh ``` ls -l /tmp/log ``` ``` -rw------- 1 vmail root 1160 Nov 23 10:27 /tmp/log ``` # Using Fail2ban with Dovecot You can use syslogging by setting `[log_path](/main/core/summaries/settings.html#log_path)` to empty value. See [https://github.com/fail2ban/fail2ban/blob/master/config/filter.d/dovecot.conf](https://github.com/fail2ban/fail2ban/blob/master/config/filter.d/dovecot.conf) for fail2ban provided filter for Dovecot. # Dovecot imapc Proxy Using Dovecot as a secure IMAP Proxy in front of Exchange, using Exchange Authentication and IMAPC. This is based on already having Dovecot already compiled and installed. 1. Create an unprivileged, non-system account user and group for the proxy, with a home directory. This needs to have a writable home directory, but no other privileges. sh ``` useradd imapproxy ``` 2. Verify that the user cannot login: sh ``` grep imapproxy /etc/shadow ``` You should see something like: ``` imapproxy:!!:nnnn:0:nn:n::: ``` The important part is the "!!". This indicates that the account is locked. If you don't see this, lockout the account (check man passwd) 3. Create `dovecot.conf`: doveconf ``` ## Dovecot configuration file mail_uid = imapproxy mail_gid = imapproxy protocols = imap listen = *, :: mail_driver = imapc mail_path = ~/imapc # Change the line below to reflect the IP address of your Exchange Server. imapc_host = 10.1.2.3 imapc_port = 143 passdb imap { # You can optionally override the IP here. # imapc_host = 10.1.2.5 fields { userdb_imapc_user = %{user} userdb_imapc_password = %{password} } } userdb prefetch { } # /home/imapproxy is the home directory for the imapproxy user, and # %{user} is a subdir that will be automatically created for each IMAP user # when they connect mail_home = /home/imapproxy/%{user} auth_mechanisms = plain login # This is the auth service used by Postfix to do dovecot auth. service auth { unix_listener auth-userdb { } inet_listener auth { port = 12345 } } ## SSL settings # These will need to ba adjusted to point to *your* certificates # The ssl_server_ca_file line refers to the intermediate certificate bundle which # may or may not be required by your SSL provider ssl_server_cert_file = /etc/pki/tls/certs/machine.example.org.crt ssl_server_key_file = /etc/pki/tls/private/machine.example.org.key ssl_server_ca_file = /etc/pki/tls/certs/gd_bundle.crt ``` Start dovecot and test it with openssl as: sh ``` openssl s_client -connect machine.example.org:143 -starttls imap ``` You should see a whole bunch of SSL information, and the last line should say: ``` . OK Pre-login capabilities listed, post-login capabilities have more. ``` Next, type: ``` 01 LOGIN username badpassword ``` You should then see: ``` 01 NO [AUTHENTICATIONFAILED] Authentication failed ``` Next, type: ``` 02 LOGIN username password ``` And should see a list similar to this: ``` * CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS FUZZY 02 OK Logged in ``` If you get this far, the proxy is working and is authenticating against your exchange server. ## Postfix Configuration My configuration is for a closed server that will never allow inbound SMTP from unauthenticated clients, and authenticates inbound SMTP TLS connections against the above Dovecot auth service, which in turn authenticates against Exchange, which authenticates against Active Directory. This means that disabling an account in Active Directory, also disables inbound and outbound mail access. If this is what you want, add the following to your `/etc/postfix/main.cf` file: ``` smtpd_sasl_type = dovecot smtpd_sasl_path = inet:127.0.0.1:12345 smtpd_sasl_auth_enable = yes smtpd_client_restrictions = permit_sasl_authenticated, reject ## Don't relay for anybody from or to anywhere, unless they authenticated smtpd_recipient_restrictions = permit_sasl_authenticated reject broken_sasl_auth_clients = yes # Talk to outlook <= 2003 and O Express <=6 smtpd_tls_security_level = encrypt smtpd_tls_received_header = yes smtpd_tls_cert_file = /etc/pki/tls/certs/machine.example.org.crt smtpd_tls_key_file = /etc/pki/tls/private/machine.example.org.key smtpd_tls_CAfile = /etc/pki/tls/certs/gd_bundle.crt # If your Certification Authority requires intermediate certificates, # the bundle goes here. tls_random_source = dev:/dev/urandom smtpd_tls_auth_only = yes # only allow auth if it's encrypted ``` # Dovecot LDA Examples ## Exim ### System Users Change the localuser router to use dovecot\_delivery transport: ``` localuser: driver = accept check_local_user # local_part_suffix = +* : -* # local_part_suffix_optional transport = dovecot_delivery ``` `check_local_user` is required. It makes Exim execute the transport with the user's UID and GID and it also sets HOME environment. Next create a new transport for dovecot-lda: ``` dovecot_delivery: driver = pipe # Use /usr/lib/dovecot/dovecot-lda if using Debian's package. # You may or may not want to add -d $local_part@$domain depending on if # you need a userdb lookup done. command = /usr/local/libexec/dovecot/dovecot-lda -f $sender_address message_prefix = message_suffix = log_output delivery_date_add envelope_to_add return_path_add #group = mail #mode = 0660 temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78 ``` LDA is now running using the local user's UID and GID. The mail is delivered to the location specified by [Mail Location Setting](/main/core/config/mail_location.html#mail-location-setting). Note that the above configuration doesn't do any [userdb](/main/core/config/auth/userdb.html) lookups, so you can't have any per-user configuration. If you want that, see the virtual user setup below. ### Virtual Users Make sure that `check_local_user` isn't set in the router. #### Single UID Configure the transport to run as the user you want, for example vmail: ``` dovecot_virtual_delivery: driver = pipe command = /usr/local/libexec/dovecot/dovecot-lda -d $local_part@$domain -f $sender_address # v1.1+: command = /usr/local/libexec/dovecot/dovecot-lda -d $local_part@$domain -f $sender_address -a $original_local_part@$original_domain message_prefix = message_suffix = delivery_date_add envelope_to_add return_path_add log_output user = vmail temp_errors = 64 : 69 : 70: 71 : 72 : 73 : 74 : 75 : 78 ``` You'll also need to have a master authentication socket and give vmail user access to it. See [LDA](/main/core/config/delivery/lda.html) for more information. List of temp\_errors can be found in `/usr/include/sysexits.h`. #### Multiple UIDs If you need multiple uids/gids you'll need to set dovecot-lda setuid root. See [LDA](/main/core/config/delivery/lda.html) for how to do this securely. You could alternatively set `user = root`, but this requires that you built Exim without root being in FIXED\_NEVER\_USERS list. ##### Multiple UIDs, without running dovecot-lda as root In this mode, dovecot-lda won't be querying Dovecot's master socket, instead trusting Exim to setup its execution environment. This means you must set up Exim to get the UID, GID, Home directory from LDAP/SQL/whatever. Here, we're setting them in the router and the transport automatically inherits them. ###### Router Configuration Insert the following router after your external delivery routers and before your local system delivery routers. This assumes you're using macros set elsewhere to handle your external queries, as they can quickly become unwieldy to manage. Make sure you adjust it to suit your installation first! ``` ldap_local_user: debug_print = "R: ldap_local_user for $local_part@$domain" driver = accept domains = +ldap_local_domains condition = LDAP_VIRT_COND router_home_directory = LDAP_VIRT_HOME user = LDAP_VIRT_UID group = LDAP_VIRT_GID #local_part_suffix = +* : -* #local_part_suffix_optional transport = dovecot_lda ``` ###### Transport Configuration This transport has been tested with Exim 4.69-9 and Dovecot 1:1.2.5-2 (backported) on Debian Lenny. You also have to set: ``` dovecot_lda: debug_print = "T: dovecot_lda for $local_part@$domain" driver = pipe # Uncomment the following line and comment the one after it if you want dovecot-lda to try # to deliver subaddresses into INBOX.{subaddress}. If you do this, uncomment the # local_part_suffix* lines in the router as well. Make sure you also change the separator # to suit your local setup. #command = /usr/lib/dovecot/dovecot-lda -e -k -m "INBOX|${substr_1:$local_part_suffix}" \ command = /usr/lib/dovecot/dovecot-lda -e -k \ -f "$sender_address" -a "$original_local_part@$original_domain" environment = USER=$local_part@$domain home_directory = /var/mail/home/$domain/$local_part umask = 002 message_prefix = message_suffix = delivery_date_add envelope_to_add return_path_add log_output log_defer_output return_fail_output freeze_exec_fail #temp_errors = * temp_errors = 64 : 69 : 70 : 71 : 72 : 73 : 74 : 75 : 78 ``` You need to have [Home Directories for Virtual Users](/main/core/config/auth/users/virtual.html#home-directories) set to have duplicate database enabled, among other reasons. ## Postfix This page contains only information specific to using LDA with Postfix, see [LDA](/main/core/config/delivery/lda.html) for more information about using the LDA itself. ### System Users If you wish you use `dovecot-lda` for all system users on a single domain mail host you can do it by editing `mailbox_command` parameter in `/etc/postfix/main.cf` ([`postconf(5)`](http://www.postfix.org/postconf.5.html)): ``` mailbox_command = /usr/local/libexec/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT" # or mailbox_command = /usr/libexec/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT" # or mailbox_command = /usr/lib/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT" # or wherever it was installed in your system. ``` Then run `postfix reload`. - This command doesn't do a [userdb](/main/core/config/auth/userdb.html) lookup. If you want that (e.g., for per-user quota lookups) you need to add `-d "$USER"` parameter. - Postfix runs `mailbox_command` with both the uid and gid of the destination user. This may not allow `dovecot-lda` to write a lock file in `/var/mail`. When this directory is writable by a privileged group (say `main`, see the option `[mail_privileged_group](/main/core/summaries/settings.html#mail_privileged_group)`), we can use the setgid permission bit on the `dovecot-lda` executable: sh ``` chgrp mail /usr/lib/dovecot/dovecot-lda chmod 2755 /usr/lib/dovecot/dovecot-lda ``` Alas, these permission will disappear if you update Dovecot. A more robust way to do so is to compile a relay program `/etc/postfix/dovecot-lda-relay` that has the setgid permission and execs the real `dovecot-lda`. sh ``` cd /etc/postfix cat >dovecot-lda-relay.c < char *pgm = "/usr/lib/dovecot/dovecot-lda"; /* wherever dovecot-lda is located */ int main(int argc, char**argv) { argv[0]=pgm; execv(pgm,argv); return 10; } EOF gcc -o dovecot-lda-relay dovecot-lda-relay.c chown root:mail dovecot-lda-relay chmod 2755 dovecot-lda-relay ``` Then, simply invoke `/etc/postfix/dovecot-lda-relay` instead of `dovecot-lda` in `mailbox_command`. - Postfix's `mailbox_size_limit` setting applies to all files that are written via dovecot-lda. The default is 50 MB, so dovecot-lda can't write **any** files larger than that, including mbox files or log files. This shows up only in Dovecot's logs: ``` dovecot-lda(user): write() failed with mbox file /home/user/mail/foo: File too large (process was started with ulimit -f limit) ``` - If you have trouble seeing anything in Dovecot's logs, see [LDA](/main/core/config/delivery/lda.html). ### Virtual Users Dovecot LDA is very easy to use on large scale installations with Postfix virtual domains support, just add a `dovecot` service in `/etc/postfix/master.cf` ([`master(5)`](http://www.postfix.org/master.5.html)): ``` dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/dovecot-lda -f ${sender} -d ${recipient} ``` An example using address extensions (i.e. [user+extension@domain.com](mailto:user+extension@domain.com) (don't forget to define the proper recipient\_delimiter in Postfix's main.cf)) to deliver to the folder 'extension' in your maildir (If you wish to preserve the case of `${extension}`, remove the `hu`[flags](http://www.postfix.org/pipe.8.html), and be sure to utilize `[Settings variables](/main/core/settings/variables.html)` in your `dovecot.conf` for mail locations and other configuration parameters that are expecting lower case): ``` dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/dovecot-lda -f ${sender} -d ${user}@${nexthop} -m ${extension} # or if you have a INBOX/ namespace prefix: dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/local/libexec/dovecot/dovecot-lda -f ${sender} -d ${user}@${nexthop} -m INBOX/${extension} ``` This example ignores address extensions (ie [user+extension@domain.com](mailto:user+extension@domain.com) delivers just like [user@domain.com](mailto:user@domain.com) ), but still shows the original address for Sieve: ``` dovecot unix - n n - - pipe flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/dovecot-lda -f ${sender} -a ${original_recipient} -d ${user}@${nexthop} ``` Replace `vmail` above with your virtual mail user account. Then set `virtual_transport` to `dovecot` in `/etc/postfix/main.cf`: ``` dovecot_destination_recipient_limit = 1 virtual_mailbox_domains = your.domain.here virtual_transport = dovecot ``` And remember to run `postfix reload`. #### Virtual Users with Multiple uids/gids If you need multiple uids/gids you'll need to set dovecot-lda setuid root or invoke it through sudo. See [LDA](/main/core/config/delivery/lda.html) for how to do this securely. ### Postfix with a NFS mail store If you are experiencing problems with dovecot-lda processes hanging when delivering to an NFS mail store, it's likely that the dovecot-lda process is hanging while waiting for free locks. The occurrence of this can be greatly reduced, if not eradicated, by forcing Postfix to only deliver to the same recipient one at a time. ``` dovecot_destination_concurrency_limit = 1 ``` ### Prevent Backscatter To prevent backscatter you should configure Postfix to reject mail for nonexistent recipients. This is the default behaviour (`smtpd_reject_unlisted_recipient = yes`) so there's no need to set "reject\_unlisted\_recipient" in any of your restriction. But: Postfix must know if a recipient exists. Depending on how you've configured Dovecot and Postfix this can be done several ways. #### System Users If you only use local system users this is no problem - all valid recipients can be found in the local password or alias database. #### Virtual users (static) When you use virtual users and domains you should maintain a list of valid recipients. The relevant settings are: **virtual\_alias\_maps, virtual\_mailbox\_maps** For static verification you can maintain the content of the files yourself. For every recipient or alias you need one entry. Example: **virtual\_alias\_maps** `name_recipient@example.com external@example.net` **virtual\_mailbox\_maps** ``` name@example.com OK recipient@example.com available ``` Don't forget to run "postmap" afterwards. INFO If you use the Dovecot LDA or LMTP it doesn't matter what you use behind the recipient address. Use "OK", the full name of the user or else. #### Virtual users (dynamic) Do you already use a database (MySQL, PostgreSQL) for Dovecot? Use the same source for Postfix. You only have to to define a valid sql query for Postfix. Example: ``` virtual_mailbox_maps = proxy:mysql:/etc/postfix/virtual_mailbox_maps.cf ``` **virtual\_mailbox\_maps.cf** ``` user = mysql-user password = mysql-password hosts = unix:/var/run/mysql/mysqld.sock dbname = mailserver query = SELECT name FROM mailbox WHERE email='%s' ``` This query will return the value of the filed "name" from table "mailbox" if the email address of the recipient matches the email from the field "email". This is enough for Postfix because Postfix must only know if the recipient exists. The value doesn't matter. When you use a database (or LDAP) there's no need to manually maintain a file with valid recipients. INFO If you use "relay\_domains" instead of "virtual\_mailbox\_domains" you have to use "relay\_recipient\_maps" instead of "virtual\_mailbox\_maps". ### Dynamic address verification with LMTP You can also use LMTP and the Postfix setting "reject\_unverified\_recipient" for dynamic address verification. It's really nice because Postfix doesn't need to query an external datasource (MySQL, LDAP...). Postfix maintain a local database with existing/non existing addresses (you can configure how long positive/negative results should be cached). To use LMTP and dynamic address verification you must first get Dovecot working. Then you can configure Postfix to use LMTP and set "reject\_unverified\_recipient" in the smtpd\_recipient\_restrictions. On every incoming email Postfix will probe if the recipient address exists. You will see similar entries in your logfile: ``` Recipient address rejected: undeliverable address: host tux.example.com[private/dovecot-lmtp] said: 550 5.1.1 < tzknvtr@example.com > User doesn't exist: tzknvtr@example.com (in reply to RCPT TO command); from=< cnrilrgfclra@spammer.org > to=< tzknvtr@example.com > ``` If the recipient address exists (status=deliverable) Postfix accepts the mail. INFO You cannot use "reject\_unverified\_recipient" with "pipe" so this doesn't work with the Dovecot LDA "deliver". ## qmail ### System Users The delivery command you need is sh ``` |/var/qmail/bin/preline -f /usr/local/libexec/dovecot/dovecot-lda ``` (You may need to adjust the paths to match your qmail and dovecot installations.) The `preline` command will add the `Return-Path:` and `Delivered-To:` lines, because `dovecot-lda` doesn't recognize qmail's environment variables. For site-wide usage, put that in `/var/qmail/control/defaultdelivery` (assuming you installed qmail according to [LWQ](http://www.lifewithqmail.org/lwq.html)). Or, save it as `.qmail` in selected users' home directories. ### Virtual Users Add the `-d` parameter to specify the destination username: sh ``` |/var/qmail/bin/preline -f /usr/local/libexec/dovecot/dovecot-lda -d $EXT@$USER ``` ## Sendmail The following describes how to configure Sendmail to use `dovecot-lda` where `root` permission is not granted and Dovecot runs under a single user ID. It may need some adjustment for more typical setups. Other assumptions are that Sendmail is configured for virtual hosting and that local-system mail delivery is not handled by `dovecot-lda`. Allowing that `sendmail.mc` has `MAILER(procmail)dnl` included, edit `sendmail.cf` adding these lines after the `Mprocmail` definition: ``` ######################*****############## ### DOVECOT Mailer specification ### ##################*****################## Mdovecot, P=/usr/local/libexec/dovecot/dovecot-lda, F=DFMPhnu9, S=EnvFromSMTP/HdrFromSMTP, R=EnvToSMTP/HdrFromSMTP, T=DNS/RFC822/X-Unix, A=/usr/local/libexec/dovecot/dovecot-lda -d $u ``` If you're using `sendmail.mc` then put the lines above into a new file `/usr/share/sendmail-cf/mailer/dovecot.m4` and put `MAILER(dovecot)` into your `sendmail.mc`. Another method of doing the above is by editing your `hostname.mc` with the following three lines: ``` FEATURE(`local_procmail', `/usr/local/libexec/dovecot/dovecot-lda',`/usr/local/libexec/dovecot/dovecot-lda -d $u') MODIFY_MAILER_FLAGS(`LOCAL', `-f') MAILER(procmail) ``` After editing `hostname.mc` with the above, be sure to remake your `hostname.cf` file. If `sendmail` runs under a different non-`root` UID via - `define('confRUN_AS_USER', 'sendmail')dnl` in `sendmail.mc`, then the `env_put(t_strconcat("RESTRICT\_` lines in `deliver.c` must be commented-out. Now add a `virtualdomain.example.com vmail:vmail` line for each virtual domain to `mailertable.cf` and run `makemap hash mailertable.db < mailertable.cf`. The `dovecot` (or some other random text) after the colon character is required, else `sendmail` will fail to pass command arguments to `dovecot-lda` correctly. Make sure all the virtual domains are in the `virtuserdomains` file. Summing up all previous experience, one may keep all virtual user accounts under one system account. The sendmail's "U=" mailer option with changing the owner of lda (to "keeper" here for instance): ``` -rwxr-xr-x. 1 keeper mail 14536 Dec 7 16:43 /usr/libexec/dovecot/dovecot-lda ``` allows to run virtual users under one system account without applying SUID. Sendmail can pass a user account to LDA with or without the domain. Passing a user name without the domain can be achieved with S=/R= rewriting rules of the local mailer. Finally, into `/usr/share/sendmail-cf/mailer/dovecot.m4` goes the block of lines: ``` Mdovecot, P=/usr/libexec/dovecot/dovecot-lda, F=l59DFMPhnuS, S=EnvFromL/HdrFromL, R=EnvToL/HdrToL, M=51200000, U=keeper:mail, T=DNS/RFC822/X-Unix, A=/usr/libexec/dovecot/dovecot-lda -d $u ``` Sendmail's `dovecot.m4` can be a bit more complex. # Dovecot LMTP and Exim Exim provides support for LMTP over UNIX sockets using the [LMTP transport](http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_lmtp_transport.html). Your distribution may not provide this; run `exim -bV` and check for 'lmtp' in 'Transports:'. Support for LMTP over TCP sockets is provided by the [SMTP transport](http://www.exim.org/exim-html-current/doc/html/spec_html/ch-the_smtp_transport.html). ## Using LMTP over UNIX Socket Use this configuration if Dovecot runs on the same host as exim. routertransport ``` local_user: debug_print = "R: local_user for $local_part@$domain" driver = accept domains = +local_domains check_local_user transport = dovecot_lmtp cannot_route_message = Unknown user ``` ``` dovecot_lmtp: driver = lmtp socket = /var/run/dovecot/lmtp #maximum number of deliveries per batch, default 1 batch_max = 200 #allow suffixes/prefixes (default unset) rcpt_include_affixes ``` ## Using LMTP over TCP Socket routertransport ``` local_user: transport = dovecot_lmtp domains = +local_domains driver = manualroute # Set IP appropriate to your setup. route_list = "* 192.168.1.0 byname" #if destination server is the local host enable this #self = send ``` ``` dovecot_lmtp: driver = smtp #allow suffixes/prefixes (default unset) rcpt_include_affixes protocol = lmtp # Set port appropriate to your setup. port = 2525 ``` ## Stripping Domain If you are using a userdb which does not have domain names, you may need to add a setting to `dovecot.conf`: ``` protocol lmtp { ... # use %{user | username} to strip away the domain part auth_username_format = %{user | username} } ``` Symptoms: - Exim says something like "LMTP error after RCPT ... 550 ... User doesn't exist user@domain" - Dovecot verbose log says something like "auth-worker(9048): passwd(user@domain): unknown user" ## Verifying Recipients Using LMTP You can use callout verification to avoid accepting mail for addresses which do not exist in Dovecot. Below is a config snippet which could be used in `acl_smtp_rcpt` to achieve this: ``` deny message = invalid recipient domains = +local_domains !verify = recipient/callout=no_cache ``` For more information on address verification, see [http://www.exim.org/exim-html-current/doc/html/spec\_html/ch-access\_control\_lists.html#SECTaddressverification](http://www.exim.org/exim-html-current/doc/html/spec_html/ch-access_control_lists.html#SECTaddressverification). ## Delivering Mails Case Insensitively ::: WARNING Just use this setup if all your login names contain only lower case characters! (On Linux see `/etc/adduser.conf` under NAME\_REGEX variable). ::: Exim retains the case of the local part. Dovecot's LMTP _may_ fail looking up an incorrect cased local part in your userdb. You can solve this problem by extending the _protocol lmtp_ section: doveconf ``` protocol lmtp { ... # strip away the domain part auth_username_format = %{user|lower} } ``` (If you don't mind allowing case insensitive logins for dovecot authentication, you may set `[auth_username_format](/main/core/summaries/settings.html#auth_username_format)` in the global configuration accordingly and renounce the above change). In case you prefer to configure exim to lower case the local part instead, add a router just before your local delivery router: ``` lowercase_local: debug_print = "R: lower case local_part for local delivery" driver = redirect redirect_router = local_user data = ${lc:${local_part}} ``` Make sure to reference the name you have chosen for your local delivery router within _redirect\_router_. # Postfix and Dovecot LMTP ## Basic Configuration The first step is to enable LMTP in `dovecot.conf`: doveconf ``` protocols = imap lmtp ``` ## Socket Configuration The LMTP service can be bound to both INET or Unix sockets. In this example, a Unix socket is placed inside the Postfix spool with appropriate permissions set: doveconf ``` service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { group = postfix mode = 0600 user = postfix } } ``` Note that the socket needs to be placed there because Postfix access is limited to this directory. ## Plugin Support Plugin support can be enabled at protocol level for [quota plugin](/main/core/plugins/quota.html) and [sieve plugin](/main/core/plugins/sieve.html). doveconf ``` protocol lmtp { # REQUIRED postmaster_address = postmaster@domainname mail_plugins { quota = yes sieve = yes } } ``` ## Postfix `main.cf` Configuration The final step is to tell Postfix to use this socket for final delivery, in this case of a virtual user scenario: ``` virtual_transport = lmtp:unix:private/dovecot-lmtp ``` For a non virtual user setup: ``` mailbox_transport = lmtp:unix:private/dovecot-lmtp ``` ## Dynamic Address Verification with LMTP You can use LMTP and the Postfix setting "reject\_unverified\_recipient" for dynamic address verification. It's useful as Postfix doesn't need to query an external datasource (MySQL, LDAP...). Postfix maintain a local database with existing/non-existing addresses (you can configure how long positive/negative results should be cached). See: [Postfix reject\_unverified\_recipient](https://www.postfix.org/ADDRESS_VERIFICATION_README.html). To use LMTP and dynamic address verification, you must first get Dovecot working. Then you can configure Postfix to use LMTP and set "reject\_unverified\_recipient" in the `smtpd_recipient_restrictions`. On every incoming email, Postfix will probe if the recipient address exists. You will see entries in your logfile: ``` Recipient address rejected: undeliverable address: host tux.example.com[private/dovecot-lmtp] said: 550 5.1.1 < tzknvtr@example.com > User doesn't exist: tzknvtr@example.com (in reply to RCPT TO command); from=< cnrilrgfclra@spammer.org > to=< tzknvtr@example.com > ``` If the recipient address exists (status=deliverable) Postfix accepts the mail. INFO You cannot use "reject\_unverified\_recipient" with "pipe", so this doesn't work with the Dovecot LDA "deliver". # Lua Director WARNING This is an unsupported method of running Dovecot. Caveat emptor. It is only intended for small-scale deployments - this is NOT designed to scale for more than a few servers. Small scale deployments using NFS or other shared storage need a way to ensure users end up in same backend. For this purpose, a simple [Lua script](/main/core/admin/lua/index.html) can be used. The example Lua scripts comes in two variants. - username <-> hostname mapping only - [https://github.com/dovecot/tools/blob/main/director.lua](https://github.com/dovecot/tools/blob/main/director.lua) - sharding (16-bit) - [https://github.com/dovecot/tools/blob/main/director-shard.lua](https://github.com/dovecot/tools/blob/main/director-shard.lua) The script can be run on a proxy directly, or one can have one dedicated proxy node acting as a Lua director node running the script. ## Prerequisites This script can use any SQL driver supported by [Lua DBI](https://github.com/mwild1/luadbi). If you have multiple Dovecot proxies, they all need the same data to route users to the correct backend. Therefore, if you use a database engine that supports data replication (such as MySQL or PostgreSQL), you can run the script on each of your proxies. For the shard version, you will also need to install [CRC32 library](https://github.com/hjelmeland/luacrc32). ## Schema username to hostname schemasharding schema sql ``` CREATE TABLE backends ( id int not null primary key, hostname varchar(255) not null unique, hostip varchar(255), state smallint not null default 0 ); CREATE TABLE user_backend ( backend_id int not null, user varchar(255), primary key(backend_id, user) ); ``` sql ``` CREATE TABLE backends ( id INT NOT NULL PRIMARY KEY, hostname VARCHAR(255) NOT NULL, hostip VARCHAR(255) ); CREATE TABLE user_backend ( backend_id INT NOT NULL, user_hash INT NOT NULL, PRIMARY KEY (backend_id, user_hash) ); ``` The user is routed to the backend in the `user_backend` table. If no entry exists, the script creates one. If you used Dovecot 2.x's `director_tag` functionality, you can create an entry yourself with the appropriate backend. ## Configuration There are few ways you can use this script. First, is drop-in replacement for director with no authentication. doveconf ``` passdb lua { lua_file = /etc/dovecot/director.lua fields { # or password = masterpass # you can include other keys here too, they will be # included in the response. nopassword = yes } } ``` Alternatively, if you wish to do authentication: doveconf ``` passdb pam { result_success = continue-ok } passdb lua { skip = unauthenticated lua_file = /etc/dovecot/director.lua fields { noauthenticate = yes } } ``` ## Operations There are no built-in tools in Dovecot to manage the database (such as adding backends, kicking users, monitoring backends, etc.). You need to build your own tooling. # How To: Restricting IMAP/POP3 access Below examples show how you can give POP3 access to everyone, but IMAP access only for some people. The exact solution you want depends on what passdb you use. The solutions can also be modified for other types of IMAP/POP3/SMTP/etc. access checks. ## PAM Set `[passdb_pam_service_name = %{protocol}](/main/core/summaries/settings.html#passdb_pam_service_name)`, i.e.: doveconf ``` passdb pam { service_name = %{protocol} } ``` That way PAM uses `/etc/pam.d/imap` for IMAP, and `/etc/pam.d/pop3` for POP3. In `/etc/pam.d/imap` you could then use, e.g., the pam\_listfile.so module: ``` # allow IMAP access only for users in /etc/imapusers file auth required pam_listfile.so item=user sense=allow file=/etc/imapusers onerr=fail ``` ## SQL You can use the `%{protocol}` variable which expands to `imap` or `pop3` in `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)`: doveconf ``` passdb sql { query = SELECT password \ FROM users \ WHERE userid = '%{user}' AND NOT (imap_allowed = false and '%{protocol}' = 'imap') } ``` ## LDAP Just like with SQL, you can use `%{protocol}` in `[passdb_ldap_filter](/main/core/summaries/settings.html#passdb_ldap_filter)`: doveconf ``` passdb_ldap_filter = (&(objectClass=posixAccount)(uid=%{user})(protocol=%{protocol})) ``` That would require setting both protocol=pop3 and protocol=imap attributes to the user objects. ## passwd-file You can create a deny passwd-file based on the service: doveconf ``` passdb passwd-file { passwd_file_path = /etc/dovecot/deny.%{protocol} deny = yes } ``` This makes Dovecot look for `/etc/dovecot/deny.imap` and `/etc/dovecot/deny.pop3` files. If the user exists in it, the access is denied. The files don't need to have anything else than one username per line. Note that this deny passdb must be before other passdbs. It also means that it can be used with any other passdb, not just with passwd-file passdbs. ## Restricting IP Access It's possible to allow a user to authenticate only from a specific IP or network. This is especially useful for master users. This can be done by returning `allow_nets` extra field from [passdb: Extra Fields](/main/core/config/auth/passdb.html#extra-fields). # Chasquid and Dovecot SASL [chasquid](https://blitiri.com.ar/p/chasquid) users can use Dovecot [SASL](/main/core/admin/sasl.html) instead of Cyrus SASL for authenticating SMTP clients. This is supported from version 0.04 and later, and uses the `PLAIN` mechanism only. ## Configuration Example dovecot.conf/etc/chasquid/chasquid.conf doveconf ``` auth_mechanisms = plain login service auth { ... # If chasquid is running under a different user, adjust the 'user =' # lines accordingly. unix_listener auth-chasquid-userdb { mode = 0660 user = chasquid } unix_listener auth-chasquid-client { mode = 0660 user = chasquid } ... } ``` ``` dovecot_auth: true ``` That should be it, because chasquid will "autodetect" the full path to the Dovecot sockets, by looking in the usual places (tested in Debian, Ubuntu, and CentOS). If chasquid can't find them, the paths can be set with the `dovecot_userdb_path` and `dovecot_client_path` options (see the chasquid configuration manual page for details). ## Additional Information - chasquid's [Dovecot integration documentation](https://blitiri.com.ar/p/chasquid/docs/dovecot/) - chasquid's [How-to](https://blitiri.com.ar/p/chasquid/docs/howto/) - chasquid's [Dovecot library source code](https://blitiri.com.ar/git/r/chasquid/b/master/t/internal/dovecot/f=dovecot.go.html) # Exim and Dovecot SASL Exim v4.64+ users can use Dovecot [SASL](/main/core/admin/sasl.html) instead of Cyrus SASL for authenticating SMTP clients. ## Configuration Example dovecot.confexim.conf doveconf ``` auth_mechanisms = plain login service auth { ... # SASL unix_listener auth-exim { # Exim requires legacy auth type until it gets updated type = auth-legacy mode = 0660 user = mail } ... } ``` ``` dovecot_login: driver = dovecot public_name = LOGIN server_socket = /var/run/dovecot/auth-exim # setting server_set_id might break several headers in mails sent by # authenticated smtp. So be careful. server_set_id = $auth1 dovecot_plain: driver = dovecot public_name = PLAIN server_socket = /var/run/dovecot/auth-exim server_set_id = $auth1 ``` If you are having problems with this not working, ensure that you are using version 4.72 or greater of exim. Previous versions of exim have trouble with the version of the protocol used in Dovecot v2.x. # Postfix and Dovecot SASL Since version 2.3, Postfix supports SMTP AUTH through Dovecot [SASL](/main/core/admin/sasl.html). If you intend to use GSSAPI, oauth2, external (certificate based) authentication, you should look at using [submission service](/main/core/config/submission.html) instead. If using Postfix obtained from a binary (such as a .rpm or .deb file), you can check if Postfix was compiled with support for Dovecot SASL by running the command: `postconf -a`. Once you have verified that your installation of Postfix supports Dovecot SASL, it's very simple to configure: ## Configuration dovecot.confpostfix: main.cf doveconf ``` service auth { ... unix_listener /var/spool/postfix/private/auth { mode = 0660 # Assuming the default Postfix user and group user = postfix group = postfix } ... } # Outlook and Windows Mail works only with LOGIN mechanism, not the # standard PLAIN auth_mechanisms = plain login ``` ``` smtpd_sasl_type = dovecot # Can be an absolute path, or relative to $queue_directory # Debian/Ubuntu users: Postfix is setup by default to run chrooted, so it # is best to leave it as-is below smtpd_sasl_path = private/auth # On Debian Wheezy path must be relative and queue_directory defined #queue_directory = /var/spool/postfix # and the common settings to enable SASL: smtpd_sasl_auth_enable = yes # With Postfix version before 2.10, use smtpd_recipient_restrictions smtpd_relay_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination ``` ## Using SASL with Postfix Submission Port When Dovecot is used as the authentication driver for Postfix, it is good practice to use a dedicated submission port for the MUAs (TCP 587). Not only can you specify individual parameters in `master.cf` overriding the global ones, but you will not run into internet mail rejection when the Dovecot Auth Mechanism is unavailable. In this example, Postfix is configured to accept TLS encrypted sessions only, along with several other sanity checks: - Verification of alias ownership via Login Maps - Domainname and recipient plausibility master.cfdovecot.confPostfix Config ``` submission inet n - n - - smtpd -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_sasl_security_options=noanonymous -o smtpd_sasl_local_domain=$myhostname -o smtpd_client_restrictions=permit_sasl_authenticated,reject -o smtpd_sender_login_maps=hash:/etc/postfix/virtual -o smtpd_sender_restrictions=reject_sender_login_mismatch -o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject ``` ## Dovecot Authentication via TCP If Postfix and Dovecot are running on separate servers, you can also authenticate via TCP. For Dovecot, set up an `inet_listener`: dovecot.confPostfix Config ``` service auth { inet_listener auth { port = 12345 } } ``` ``` smtpd_sasl_path = inet:dovecot.example.com:12345 smtpd_sasl_type = dovecot ``` ## See Also: [https://www.postfix.org/SASL\_README.html#server\_dovecot](https://www.postfix.org/SASL_README.html#server_dovecot) # Sieve Examples Below are some simple Sieve code examples, more can be found at [http://sieve.info/examplescripts](http://sieve.info/examplescripts). ## Mail Filtering by Various Headers ### Use if/elsif/else to store messages into various folders/subfolders ``` require ["fileinto", "envelope"]; if address :is "to" "dovecot@dovecot.org" { fileinto "Dovecot-list"; } elsif envelope :is "from" "owner-cipe-l@inka.de" { fileinto "lists.cipe"; } elsif anyof (header :contains "X-listname" "lugog@cip.rz.fh-offenburg.de", header :contains "List-Id" "Linux User Group Offenburg") { fileinto "ml.lugog"; } else { # The rest goes into INBOX # default is "implicit keep", we do it explicitly here keep; } ``` "anyof" means logical OR, "allof" is AND. ### Forward mails with "order" or "buy" in their subject to another address ``` if header :contains "subject" ["order", "buy"] { redirect "orders@company.dom"; } ``` Message-ID and recipient of forwarded message are stored in a `.dovecot.lda-dupes` at users home directory to prevent mail loops. ## Flagging or Highlighting your Mail Some mail readers use these flags: ``` require "imap4flags"; require "regex"; if anyof (exists "X-Cron-Env", header :regex ["subject"] [".* security run output", ".* monthly run output", ".* daily run output", ".* weekly run output"]) { addflag "$label1"; # ie 'Important'/red label within Thunderbird # Other flags: # addflag "$label1"; # Important: #ff0000 => red # addflag "$label2"; # Work: #ff9900 => orange # addflag "$label3"; # personal: #009900 => green # addflag "$label4"; # todo: #3333ff => blue # addflag "$label5"; # later: #993399 => violet # } ``` Local copy of your emails: ``` require ["envelope", "imap4flags"]; if envelope "from" "my_address@my_domain.com" { setflag "\\seen"; } ``` Useful, when you want sieve to manage your incoming **and** outgoing email (you must ask your mail reader to Bcc your mail to your dovecot in this case). ## Spam/Virus Rules Most spam and virus scanners add a special header to mail messages, so that users can apply filtering accordingly. Depending on how the Sieve interpreter is configured, filtering can either be performed by evaluating these headers directly, or using the spamtest and virustest extensions. ### Direct filtering using message header Evaluating the headers directly is always possible as long as the headers are actually added to the messages by the scanner software. For example, to file SpamAssassin-tagged mails into a folder called "Spam": ``` require "fileinto"; if header :contains "X-Spam-Flag" "YES" { fileinto "Spam"; } ``` The following example discards SpamAssassin-tagged mails with level higher than or equal to 10: ``` if header :contains "X-Spam-Level" "**********" { discard; stop; } ``` Some spam scanners only produce a numeric score in a header. Then, the test becomes more involved: ``` require ["comparator-i;ascii-numeric","relational"]; if allof ( not header :matches "x-spam-score" "-*", header :value "ge" :comparator "i;ascii-numeric" "x-spam-score" "10" ) { discard; stop; } ``` INFO Be very careful when matching against spam score headers using the relational extension and the i;ascii-numeric comparator. This comparator can only be used to match unsigned integers. Strings that do not begin with a digit character represent positive infinity and will therefore always be larger than any score mentioned in your rule! That is why the above example first checks the minus sign explicitly. ### Filtering using the spamtest and virustest extensions When the [Sieve spamtest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) and [Sieve virustest extension](/main/core/config/sieve/extensions/spamtest_virustest.html) extensions are configured on the server, users (and GUIs) can have a much easier way to filter spam and virus messages respectively. To filter spam, the spamtest extension can for example be used as follows: ``` require "spamtestplus"; require "fileinto"; require "relational"; require "comparator-i;ascii-numeric"; /* If the spamtest fails for some reason, e.g. spam header is missing, file * file it in a special folder. */ if spamtest :value "eq" :comparator "i;ascii-numeric" "0" { fileinto "Unclassified"; /* If the spamtest score (in the range 1-10) is larger than or equal to 3, * file it into the spam folder: */ } elsif spamtest :value "ge" :comparator "i;ascii-numeric" "3" { fileinto "Spam"; /* For more fine-grained score evaluation, the :percent tag can be used. The * following rule discards all messages with a percent score * (relative to maximum) of more than 85 %: */ } elsif spamtest :value "gt" :comparator "i;ascii-numeric" :percent "85" { discard; } /* Other messages get filed into INBOX */ ``` The virustest extension can be used in a similar manner: ``` require "virustest"; require "fileinto"; require "relational"; require "comparator-i;ascii-numeric"; /* Not scanned ? */ if virustest :value "eq" :comparator "i;ascii-numeric" "0" { fileinto "Unscanned"; /* Infected with high probability (value range in 1-5) */ } elsif virustest :value "eq" :comparator "i;ascii-numeric" "4" { /* Quarantine it in special folder (still somewhat dangerous) */ fileinto "Quarantine"; /* Definitely infected */ } elsif virustest :value "eq" :comparator "i;ascii-numeric" "5" { /* Just get rid of it */ discard; } ``` ## Plus Addressed Mail Filtering Using the subaddress ([RFC 5233](https://datatracker.ietf.org/doc/html/rfc5233)) extension, it is possible to match against the 'detail' part of an e-mail address, e.g. a `+tag` suffix to the local part of the address. This is for example useful when you don't want just any +tag to create a directory, but you want to use tagged addresses such as with amavisd-new. This example would place email addressed to [user+spam@example.com](mailto:user+spam@example.com) into user's Spam folder. ``` require ["fileinto", "envelope", "subaddress"]; if envelope :detail "to" "spam"{ fileinto "Spam"; } ``` The following more advanced example uses the subaddress extension to handle recipient addresses structured as `sales+@company.com` in a special way. The `` part is extracted from the address using [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) extension, transformed into a format with the first letter in upper case and subsequently used to create the folder name where the message is stored. The folder name is structured as `users/`. If the `+` detail is omitted from the recipient address, the message is filed in the `sales` folder. ``` require ["variables", "envelope", "fileinto", "subaddress"]; if envelope :is :user "to" "sales" { if envelope :matches :detail "to" "*" { /* Save name in ${name} in all lowercase except for the first letter. * Joe, joe, jOe thus all become 'Joe'. */ set :lower :upperfirst "name" "${1}"; } if string :is "${name}" "" { /* Default case if no detail is specified */ fileinto "sales"; } else { /* For sales+joe@ this will become users/Joe */ fileinto "users/${name}"; } } ``` To work with Postfix, this requires that the envelope "to" still contains the full address, so pass it with the -a flag. ``` dovecot unix - n n - - pipe flags=DRhu user=mail:mail argv=/usr/local/libexec/dovecot/dovecot-lda -f ${sender} -d ${user}@${nexthop} -a ${original_recipient} ``` or ``` mailbox_command = /usr/lib/dovecot/dovecot-lda -a "$RECIPIENT" ``` ## Vacation auto-reply Auto-responder functionality is implemented using the [Sieve vacation extension](/main/core/config/sieve/extensions/vacation.html) extension. The following script sends out-of-office replies when the message is not spam: ``` require ["fileinto", "vacation"]; # Move spam to spam folder if header :contains "X-Spam-Flag" "YES" { fileinto "spam"; # Stop here so that we do not reply on spams stop; } vacation # Reply at most once a day to a same sender :days 1 :subject "Out of office reply" # List of additional recipient addresses which are included in the auto replying. # If a mail's recipient is not the envelope recipient and it's not on this list, # no vacation reply is sent for it. :addresses ["j.doe@company.dom", "john.doe@company.dom"] "I'm out of office, please contact Joan Doe instead. Best regards John Doe"; ``` It's also possible to include the original subject using the [Sieve variables extension](/main/core/config/sieve/extensions/variables.html) extension: ``` require ["variables", "vacation"]; # Store old Subject line so it can be used in vacation message if header :matches "Subject" "*" { set "subjwas" ": ${1}"; } vacation :days 1 :subject "Out of office reply${subjwas}" :addresses ["j.doe@company.dom", "john.doe@company.dom"] "I'm out of office, please contact Joan Doe instead. Best regards John Doe"; ``` ## Include scripts It's possible to [include](/main/core/config/sieve/extensions/include.html) other Sieve scripts in your script: ``` require ["include"]; include :global "global-spam"; include :personal "my-own-spam"; ``` The lookup directories can be specified with: doveconf ``` sieve_script personal { type = personal # Directory where the sieve include plugin retrieves :personal scripts from. path = ~/sieve active_path = ~/.dovecot.sieve } # Storage for :global include scripts. If no type=global storage is configured, # any :global include fails. sieve_script global1 { type = global path = /etc/dovecot/sieve/ } ``` Settings for both [personal](/main/core/plugins/sieve.html#script-storage-type-personal) and [global](/main/core/plugins/sieve.html#script-storage-type-global) Sieve script storage types may also be overridden by [userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields). It's not currently possible to use subdirectories for the scripts. Having a '/' character in the script name always fails the include. This is just an extra check to avoid potential problems with including scripts within mail directories. ## Archiving a Mailinglist by Date You can archive messages from mailing lists in a date-structured folder tree as follows: ``` require ["variables","date","fileinto","mailbox"]; # Extract date info if currentdate :matches "year" "*" { set "year" "${1}"; } if currentdate :matches "month" "*" { set "month" "${1}"; } # Archive Dovecot mailing list items by year and month. # Create folder when it does not exist. if header :is "list-id" "dovecot.dovecot.org" { fileinto :create "INBOX.Lists.${year}.${month}.dovecot"; } ``` For example, in March 2013 this puts messages from the Dovecot mailing list in a folder called `INBOX.Lists.2013.03.dovecot`. It combines the date and variables extensions to extract the required date strings. Using the `:create` argument for the `fileinto` command, the indicated folder is created automatically if it doesn't exist. The `:create` argument is provided by the mailbox extension. ## Emulating `lmtp_save_to_detail_mailbox=yes` If you can't turn this option on, you can emulate the behaviour to some extent with following code. ``` require ["variables", "fileinto", "envelope", "subaddress", "mailbox"]; if envelope :matches :detail "to" "*" { # you can prefix with INBOX/ or INBOX. if necessary # remove :create if you want to permit only existing mailboxes fileinto :create "${1}"; } ``` ## Translation from Procmail There exists a script which attempts to translate simple Procmail rules into Sieve rules: [https://dovecot.org/tools/procmail2sieve.pl](https://dovecot.org/tools/procmail2sieve.pl). Here's the original post announcing it: [https://dovecot.org/list/dovecot/2007-March/020895.html](https://dovecot.org/list/dovecot/2007-March/020895.html). # SSL For configuring Dovecot to use SSL, see [SSL configuration](/main/core/config/ssl.html#configuration-overview). LetsEncrypt has a [good primer](https://www.eff.org/deeplinks/2019/01/encrypting-web-encrypting-net-primer-using-certbot-secure-your-mailserver) on mail server SSL certificates. SSL works pretty much the same universally, so for more information about SSL you can see [Apache's documentation](https://httpd.apache.org/docs/2.4/ssl/). ## SSL, TLS and STARTTLS confusion SSL and TLS terms are often used in confusing ways: - SSL (Secure Sockets Layer) is the original protocol implementation. Some clients use SSL to mean that they're going to connect to the imaps (993), pop3s (995) or smtps (465) port, although they're still going to use TLSv1 protocol. - TLS (Transport Layer Security) replaced the SSL protocol. TLSv1 protocol is used practically always nowadays. Some clients use TLS to mean that they're going to use STARTTLS command after connecting to the standard imap (143), pop3 (110) or smtp port (25/587). Nothing would prevent using SSLv3 protocol after STARTTLS command. Unfortunately there doesn't seem to be any clear and simple way to refer to these different meanings. SSL term is much more widely understood than TLS, so Dovecot configuration and this documentation only talks about SSL when in fact it means both SSL/TLS. Originally SSL support was added to protocols by giving them a separate `SSL port` (imaps, pop3s, etc.), where the SSL handshake starts immediately when client connects, and only after the session is encrypted the regular protocol handling begins. Using two separate ports for unencrypted and SSL connections was thought to be wasteful and adds complexity for clients which may wish to make use of SSL when it is advertised, so STARTTLS command was added and intended to deprecate the SSL ports. Clients using STARTTLS work by connecting to the regular unencrypted port and immediately issue a STARTTLS command, after which the session is encrypted. After SSL handshake there is no difference between SSL port initiated connections and STARTTLS initiated connections. SSL port deprecation never really happened, probably because of a few reasons: - Some admins don't even know about STARTTLS. - Some admins want to require SSL/TLS, but don't realize that this is also possible with STARTTLS (Dovecot has `[auth_allow_cleartext = no](/main/core/summaries/settings.html#auth_allow_cleartext)` and `[ssl = required](/main/core/summaries/settings.html#ssl)` settings). - Some admins understand everything, but still prefer to allow only SSL ports (maybe with a firewall). This could be because it makes it easier to ensure that no information is leaked, because SSL/TLS handshake happens immediately. Some clients unfortunately try to do cleartext authentication without STARTTLS, even when IMAP server has told the client that it won't work. Besides, it requires fewer round-trips if you begin SSL upon connection when you know you want it, instead of connect, negotiate capabilities, insist on TLS, then start all over again inside the encrypted session. - According to some [reports](https://it.slashdot.org/story/14/11/11/2349244/isps-removing-their-customers-email-encryption) STARTTLS cannot guarantee encrypted delivery of mail. # SSL Certificate Creation ## Self-signed SSL Certificates Self-signed SSL certificates are the easiest way to get your SSL server working. However unless you take some action to prevent it, **this is at the cost of security**: - The first time the client connects to the server, it sees the certificate and asks the user whether to trust it. The user, of course, doesn't really bother verifying the certificate's fingerprint, so a man-in-the-middle attack can easily bypass all the SSL security, steal the user's password and so on. - If the client was lucky enough not to get attacked the first time it connected, the following connections will be secure as long as the client had permanently saved the certificate. Some clients do this, while others have to be manually configured to accept the certificate. The only way to be fully secure is to import the SSL certificate to client's (or operating system's) list of trusted CA certificates prior to first connection. See [certificate importing](./cert_import.html) for how to do it for different clients. ### Self-signed Certificate Creation Dovecot includes a script to build self-signed SSL certificates using OpenSSL. In the source distribution this exists in [https://github.com/dovecot/core/blob/main/doc/mkcert.sh](https://github.com/dovecot/core/blob/main/doc/mkcert.sh). Binary installations usually create the certificate automatically when installing Dovecot and don't include the script. The SSL certificate's configuration is taken from [https://dovecot.org/doc/dovecot-openssl.cnf](https://dovecot.org/doc/dovecot-openssl.cnf) file. Modify the file before running mkcert.sh. Especially important field is the CN (Common Name) field, which should contain your server's host name. The clients will verify that the CN matches the connected host name, otherwise they'll say the certificate is invalid. It's also possible to use wildcards (eg. \*.domain.com) in the host name. They should work with most clients. By default the certificate is created to `/etc/ssl/certs/dovecot.pem` and the private key file is created to `/etc/ssl/private/dovecot.pem`. Also by default the certificate will expire in 365 days. If you wish to change any of these, modify the `mkcert.sh` script. ## Certificate Authorities The correct way to use SSL is to have each SSL certificate signed by a Certificate Authority (CA). The client has a list of trusted Certificate Authorities, so whenever it sees a new SSL certificate signed by a trusted CA, it will automatically trust the new certificate without asking the user any questions. There are two ways to get a CA signed certificate: get it from an external CA, or create your own CA. The clients have a built-in list of trusted CAs, so getting it from one of those CAs will have the advantage of the certificate working without any client configuration. There are already-trusted-CAs where you can buy the certificate, and there are already-trusted-CAs where you can get your certificate for free. On the other hand, if you create your own CA, you'll have to install the CA certificate to all the clients. So the two options end up being three: 1. Get it from an external CA (which already have the public keys installed on the clients, so the clients trust the CA) - Purchase the certificate. - Get a free certificate. 2. Create your own CA (in this case you'll have to add the CA public keys into the clients, as you are not trusted by default) If you choose "1.ii", there is [https://letsencrypt.org](https://letsencrypt.org) where you need to do some technical effort to demonstrate to a robot that you own the domains for which the certificate is issued. LetsEncrypt is an initiative of the Internet Security Research Group (ISRG), with board members from Cisco, Mozilla and the University of Michigan among others and technical advisors from Akamai, Google, Electronic Frontier Foundation, Internet Society and independents among others. LetsEncrypt CA is trusted by default by many clients. If you can control the entries in your DNS, you'll be able to demonstrate to the robot that "you are you" for domain-based identities. See [https://www.eff.org/deeplinks/2019/01/encrypting-web-encrypting-net-primer-using-certbot-secure-your-mailserver](https://www.eff.org/deeplinks/2019/01/encrypting-web-encrypting-net-primer-using-certbot-secure-your-mailserver). If you choose "2", there are multiple different tools for managing your own CA. The simplest way is to use a CA managing tool as [gnoMint](https://gnomint.sourceforge.net/) or [TinyCA](https://opsec.eu/src/tinyca/). However, if you need to tailor the properties of the CA, you always can use OpenSSL, very much customizable, but however a bit cumbersome. # SSL Certificate Importing to Clients You may import either the server's self-signed certificate or the CA certificate (see [certificate\_creation](./cert_create.html)). ## Windows 11 See [https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates](https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/working-with-certificates) ## Mac OS X - Doubleclick the certificate - Keychain should open - Add the certificate to X509 Anchors keychain Apple Mail uses the OS X's certificate store. ## Thunderbird Preferences -> Privacy -> Security -> View Certificates -> Authorities -> Import -> Trust this CA to identify email users. ## Opera Mail Preferences -> Advanced > Security > Certificates > Import certificate file. ## Evolution Preferences -> Certificates -> Authorities -> Import -> Trust this CA to identify email users. ## Mutt See [https://gitlab.com/muttmua/mutt/-/wikis/MuttGuide/UseIMAP](https://gitlab.com/muttmua/mutt/-/wikis/MuttGuide/UseIMAP) ## Pine [http://www.madboa.com/geek/pine-ssl/](http://www.madboa.com/geek/pine-ssl/) tells a story how to do this. Basically it seems to be: 1. Find out your OPENSSLDIR: `openssl version -d` 2. Get a hash of your certificate: `openssl x509 -in cert.pem -hash -noout` 3. Copy the certificate to `$OPENSSLDIR/certs/$hash.0` This probably works only for self-signed certificates. ## KMail See [https://docs.kde.org/stable5/en/kmail/kmail2/manual-configuration-quickstart.html](https://docs.kde.org/stable5/en/kmail/kmail2/manual-configuration-quickstart.html) ## Claws Mail Configuration -> Edit accounts (Choose here your's one and press 'Edit'-button) Account -> SSL -> Certificate for receiving->Browse # TLS SNI Client Support ## Works - Thunderbird (Linux) - K-9 on Android ([merged Sept 2015](https://github.com/k9mail/k-9/pull/718)) - [Apple Mail](https://forums.cpanel.net/threads/mail-ssl-sni.454592/) - [Mutt](https://gitlab.com/muttmua/mutt/-/blob/master/README.SSL) - NeoMutt ([since 2016-03-07](https://www.neomutt.org/feature/tls-sni)) ## Doesn't Work - K-9 on Droid X2 (maybe fixed in newer versions - see above) - Apple Mail (Mac OS X 10.10 and lower AND iOS 9.3 and lower) - [Outlook for Mac version 15](https://forums.cpanel.net/threads/mail-ssl-sni.454592/) - Kindle Fire HD 8 - Outlook 2013 # Virtual Users with Postfix WARNING This document has been taken out of the old wiki and has not yet been updated. This document describes how to user virtual users with [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html). ## Basic Configuration dovecot.conf dovecot ``` protocols = imap lmtp ``` ## Mail Location and Namespaces A private namespace configured for [Maildir](/main/core/config/mailbox_formats/maildir.html) or [mdbox](/main/core/config/mailbox_formats/dbox.html#multi-dbox-mdbox) storage to hold the mailboxes and a public namespace configured for Maildir storage with filesystem layout (/dir/subdir) and per user index-information. The index will be stored in the `public` dir under the home directories. This allows individual /SEEN information for the public namespace. doveconf ``` #mail_driver = mdbox #mail_path = ~/mdbox mail_driver = maildir mail_path = ~/maildir mail_plugins { quota = yes } namespace inbox { type = private separator = / prefix = hidden = no inbox = yes } namespace public { type = public separator = / prefix = Public/ mail_driver = maildir mail_path = /var/vmail/public mail_index_path = ~/public mailbox_list_layout = fs list = yes subscriptions = no } ``` User Home directory structure: `/var/vmail/\/\/`: ``` -rw------- 1 vmail vmail 1489 2010-03-03 19:30 .dovecot.sieve -rw------- 1 vmail vmail 2897 2010-03-14 12:22 .dovecot.svbin drwx--S--- 4 vmail vmail 4096 2010-03-17 20:15 maildir drwx--S--- 4 vmail vmail 4096 2010-03-14 13:31 public ``` ## Authentication Databases Per domain flat-files containing the virtual user's specific parameters, stored in a single passwd-like file. User logins are expected to be in full-qualified e-mail address format: `user@domain.tld`. Additional parameters may be used to override defaults, such as individual quotas or mailbox formats. dovecot.conf/var/vmail/auth.d//passwd doveconf ``` auth_mechanisms = plain passdb passwd-file { passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } userdb passwd-file { passwd_file_path = /var/vmail/auth.d/%{user | domain}/passwd } ``` ``` @:{SSHA}xxxx:5000:5000::/var/vmail//::userdb_quota_storage_size=5G ``` Later on the auth service will be configured to run in the `doveauth` user context. Therefore the directory `auth.d/` and its content will be owned by this user, while mails and Sieve scripts will be accessed using the `vmail` context specified in the passwd-file. To keep directory permissions simple these will be stored seperately under the `conf.d/` tree. Per-domain authentication and configuration structure `/var/vmail`: ``` dr-x------ 3 doveauth dovecot 4096 2010-03-17 19:09 auth.d |--> domain.tld dr-x------ 3 vmail vmail 4096 2010-03-03 19:32 conf.d |--> domain.tld ``` ## Master Configuration IMAP-Server bound to IP _1.2.3.4_ on port 143 while the IMAPS Port (993) is disabled since `STARTTLS` will be used to request encryption over the standard IMAP port. The LMTP-Server is bound to a Unix socket configured with relevant permissions allowing Postfix to inject mails through it. Dovecot presents its Authentication Mechanism and User Database as another socket to Postfix allowing SASL submission. Dovecot's authentication services will run in the `doveauth` user-context. dovecot.conf doveconf ``` service imap-login { inet_listener imap { listen = 1.2.3.4 port = 143 } inet_listener imaps { port = 0 } } service lmtp { unix_listener /var/spool/postfix/private/dovecot-lmtp { group = postfix mode = 0660 user = postfix } } service imap { } service auth { unix_listener /var/spool/postfix/private/auth { group = postfix mode = 0660 user = postfix } user = doveauth } service auth-worker { user = doveauth } verbose_proctitle = yes ``` ## Protocols dovecot.conf doveconf ``` protocol imap { mail_plugins { imap_quota = yes mail_log = yes notify = yes } } protocol lmtp { postmaster_address = postmaster@domainname # required on my debian mail_plugins { sieve = yes } } ``` ## Plugins dovecot.conf doveconf ``` quota_storage_size = 1GB quota user { } mailbox Trash { quota_storage_percentage = 110 } sieve_script personal { path = ~/.dovecot.sieve } ``` ## TLS dovecot.conf doveconf ``` ssl_server_cert_file = /etc/ssl/certs/domain_tld_2009.crt ssl_server_key_file = /etc/ssl/private/domain_tld_2009.key ``` ## Logging Simple logging using the internal mechanism. See [logging](/main/core/admin/logging.html) for more complex configurations. dovecot.conf doveconf ``` log_path = /var/log/dovecot.log info_log_path = /var/log/dovecot-info.log ``` ### Syslog Logging dovecot.conf/etc/rsyslog.conf/etc/logrotate.d/dovecot doveconf ``` #log_path = #info_log_path = syslog_facility = local1 ``` ``` local1.* -/var/log/dovecot.log local1.info -/var/log/dovecot.info local1.warn -/var/log/dovecot.warn local1.err -/var/log/dovecot.err :msg,contains,"stored mail into mailbox"\ -/var/log/dovecot.lmtp ``` ``` /var/log/dovecot.log /var/log/dovecot.info /var/log/dovecot.warn /var/log/dovecot.err /var/log/dovecot.lmtp { weekly rotate 52 missingok notifempty compress delaycompress create 640 root adm sharedscripts postrotate /bin/kill -USR1 'cat /var/run/dovecot/master.pid 2>/dev/null' 2>/dev/null || true endscript } ``` ## Postfix Configuration In this configuration Postfix will only accept SASL requests on its submission port (`TCP:587`) and will not accept them on Port 25. As Dovecot is used as authentication driver in this example, this will not break inbound mail flow in case the authentication mechanism is down e.g. due to upgrading to a new build. The configuration of the submission port is handled in `master.cf` overriding possible `main.cf` settings. Several sanity checks are performed upon submission like recipient domain validation and sender map checks. Final delivery is performed through Dovecot's LMTP server via a socket. /etc/postfix/main.cf ``` smtpd_banner = $myhostname ESMTP biff = no append_dot_mydomain = no myhostname = mail.domain.tld inet_protocols = ipv4 mydestination = $myhostname, localhost.$mydomain virtual_mailbox_domains = domain.tld, domain1.tld virtual_alias_maps = hash:/etc/postfix/virtual virtual_transport = lmtp:unix:private/dovecot-lmtp strict_rfc821_envelopes = yes disable_vrfy_command = yes smtpd_helo_required = yes smtpd_recipient_restrictions = reject_unknown_recipient_domain, reject_non_fqdn_recipient, permit_mynetworks, reject_unauth_destination permit smtpd_tls_cert_file=/etc/postfix/server.pem smtpd_tls_key_file=/etc/ssl/private/domain_tld.key smtpd_tls_session_cache_database = btree:/var/lib/postfix/smtpd_scache alias_maps = hash:/etc/aliases mailbox_size_limit = 0 message_size_limit = 20480000 ``` ## Postfix Master Configuration The submission port is configured to only accept TLS secured transmissions. Login Map checks will verify the authenticated SASL user is authorized to send using different MAIL FROM aliases. /etc/postfix/master.cf ``` # # Postfix master process configuration file. For details on the format # of the file, see the master(5) manual page (command: "man 5 master"). # # ========================================================================== # service type private unpriv chroot wakeup maxproc command + args # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - - - - smtpd submission inet n - - - - smtpd -o smtpd_tls_security_level=encrypt -o smtpd_sasl_auth_enable=yes -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o smtpd_recipient_restrictions=reject_unknown_recipient_domain,reject_non_fqdn_recipient,permit_sasl_authenticated,reject pickup fifo n - - 60 1 pickup cleanup unix n - - - 0 cleanup qmgr fifo n - n 300 1 qmgr tlsmgr unix - - - 1000? 1 tlsmgr rewrite unix - - - - - trivial-rewrite bounce unix - - - - 0 bounce defer unix - - - - 0 bounce trace unix - - - - 0 bounce verify unix - - - - 1 verify flush unix n - - 1000? 0 flush proxymap unix - - n - - proxymap smtp unix - - - - - smtp relay unix - - - - - smtp -o fallback_relay= showq unix n - - - - showq error unix - - - - - error discard unix - - - - - discard local unix - n n - - local virtual unix - n n - - virtual lmtp unix - - - - - lmtp anvil unix - - - - 1 anvil scache unix - - - - 1 scache retry unix - - - - - error ``` # Virtual Users with PostgreSQL WARNING This document has been taken out of the old wiki and has not yet been updated. Document describing gluing together virtual user/domain support for: - Debian (These instructions are for Sid) - Postfix 2 with SMTP AUTH - SASL2 with libpam-pgsql for Postfix - PostgreSQL - Dovecot (dovecot-pop3d and or dovecot-imapd) INFO On Debian, the package dovecot-pgsql do not yet include postgresql as a dependency to pull it if not installed. ## Software Installation For Debian: sh ``` apt-get install postfix-pgsql sasl2-bin libsasl2-modules postgresql \ libpam-pgsql dovecot-pgsql dovecot-imapd dovecot-pop3d ``` ## Configuring PostgreSQL Edit `/etc/postgresql/pg_hba.conf` to accept password authentication for localhost: ``` host all all 127.0.0.1 255.255.255.255 password ``` Then create the database: sh ``` sudo su postgres createdb mails psql mails ``` Create tables: sql ``` CREATE TABLE transport ( domain VARCHAR(128) NOT NULL, transport VARCHAR(128) NOT NULL, PRIMARY KEY (domain) ); CREATE TABLE users ( userid VARCHAR(128) NOT NULL, password VARCHAR(128), realname VARCHAR(128), uid INTEGER NOT NULL, gid INTEGER NOT NULL, home VARCHAR(128), mail VARCHAR(255), PRIMARY KEY (userid) ); CREATE TABLE virtual ( address VARCHAR(255) NOT NULL, userid VARCHAR(255) NOT NULL, PRIMARY KEY (address) ); create view postfix_mailboxes as select userid, home||'/' as mailbox from users union all select domain as userid, 'dummy' as mailbox from transport; create view postfix_virtual as select userid, userid as address from users union all select userid, address from virtual; ``` Create separate users for read and write accesses. Postfix and Dovecot needs only read access. You may want to use the writer user for your own purposes. sql ``` CREATE USER mailreader PASSWORD 'secret'; grant select on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailreader; create user mailwriter password 'secret'; grant select, insert, update, delete on transport, users, virtual, postfix_mailboxes, postfix_virtual to mailwriter; ``` Here's a few example values: sql ``` insert into transport (domain, transport) values ('domain.org', 'virtual:'); insert into transport (domain, transport) values ('foo.org', 'virtual:'); insert into users (userid, uid, gid, home) values ('user@domain.org', 1001, 1001, 'domain.org/mails/user'); insert into users (userid, uid, gid, home) values ('user2@domain.org', 1001, 1001, 'domain.org/mails/user2'); insert into users (userid, uid, gid, home) values ('user@foo.org', 1002, 1002, 'foo.org/mails/user'); insert into virtual (address, userid) values ('foo@foo.org', 'user@foo.org'); ``` The above examples assume that you'd use separate system UID and GID for each domain. I think that's good enough compromise between simplicity and security. The UIDs and GIDs aren't required to be in /etc/passwd and /etc/group, "ls -l" will just show them in numeric form in that case. In this case, the virtual domain "domain.org" and "foo.org" will define virtual: as the transport. Please note in this case, virtual service from postfix will deliver the mail and ignore all virtual\_transport config settings. If you prefer dovecot as the transport, make sure 'dovecot' or something like `lmtp:unix:private/dovecot-lmtp` is returned from the transport\_maps query. In order to make virtual\_transport setting effective, leave transport\_maps as default. ## Configuring Postfix /etc/postfix/main.cf/etc/postfix/sasl/smtpd.conf/etc/postfix/transport.cf/etc/postfix/uids.cf/etc/postfix/gids.cf/etc/postfix/mailboxes.cf/etc/postfix/virtual.cf ``` transport_maps = pgsql:/etc/postfix/transport.cf virtual_uid_maps = pgsql:/etc/postfix/uids.cf virtual_gid_maps = pgsql:/etc/postfix/gids.cf # Note that we've set virtual_mailbox_base to /home, which means that it's # prefixed to all home directories in SQL database. virtual_mailbox_base = /home virtual_mailbox_maps = pgsql:/etc/postfix/mailboxes.cf virtual_maps = pgsql:/etc/postfix/virtual.cf mydestination = $mydomain, $myhostname smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination smtpd_sasl_auth_enable = yes smtpd_sasl_security_options = noanonymous smtpd_sasl_local_domain = domain.org smtp_sasl_auth_enable = no ``` ``` pwcheck_method: saslauthd saslauthd_path: /etc/mux ``` ``` user=mailreader password=secret dbname=mails table=transport select_field=transport where_field=domain hosts=localhost ``` ``` user=mailreader password=secret dbname=mails table=users select_field=uid where_field=userid hosts=localhost ``` ``` user=mailreader password=secret dbname=mails table=users select_field=gid where_field=userid hosts=localhost ``` ``` user=mailreader password=secret dbname=mails table=postfix_mailboxes select_field=mailbox where_field=userid hosts=localhost ``` ``` user=mailreader password=secret dbname=mails table=postfix_virtual select_field=userid where_field=address hosts=localhost ``` ## Configuring SASL2 We want to use PAM authentication via saslauthd. SMTP process runs chrooted into /var/spool/postfix and we have to be able to communicate to saslauthd via UNIX socket, so create the socket inside the chroot. In Debian you can configure it in `/etc/default/saslauthd`: ``` START=yes MECHANISMS=pam PARAMS="-m /var/spool/postfix/etc" ``` As of version 2.1.19 of SASL you also need to add the -r parameter in order to authenticate with an email address (containing a @) as user id: ``` PARAMS="-r -m /var/spool/postfix/etc" ``` (This parameter will probably break saslauthd if used with previous versions.) Configure libpam-pgsql in `/etc/pam_pgsql.conf`: ``` database = mails host = localhost user = mailreader password = secret table = users user_column = userid pwd_column = password #expired_column = acc_expired #newtok_column = acc_new_pwreq pw_type = crypt #debug ``` And create /etc/pam.d/smtp: ``` auth required pam_pgsql.so account required pam_pgsql.so password required pam_pgsql.so ``` libsasl2-modules install a lot of plugins which you most likely don't need and which don't even work with PAM. You mostly just need PLAIN and possibly LOGIN authentication. I'm not sure if there's any pretty way to select only them, but one evil way is to just delete others: sh ``` cd /usr/lib/sasl2 rm -f libcrammd5.* libdigestmd5.* libsasldb.* libotp.* libntlm.* libanonymous.* ``` The better way is to put in `/etc/postfix/sasl/smtpd.conf` the following line: ``` mech_list: login plain ``` Where mech\_list is a list of all the mechanism names to enable. # Configuring Dovecot dovecot ``` mail_driver = maildir sql_driver = pgsql pgsql localhost { parameters { user = mailreader password = secret dbname = mails } } passdb sql { default_password_scheme = CRYPT query = SELECT userid as user, password \ FROM users \ WHERE userid = '%{user}' } userdb sql { query = SELECT '/home/' || home AS home, uid, gid \ FROM users \ WHERE userid = '%{user}' } ``` ## Restart Finally remember to restart everything before trying to figure out why nothing is working: sh ``` /etc/init.d/saslauthd restart /etc/init.d/postgresql restart /etc/init.d/postfix restart /etc/init.d/dovecot restart ``` # Simple Virtual User Installation - Virtual users configured in [passwd authentication database](/main/core/config/auth/databases/passwd.html). - Assuming an unmodified Dovecot v2.x installation. - Assuming you're not using NFS. See [NFS](/main/core/config/nfs.html) for problems related to it. ## System Configuration - Create `dovecot` and `dovenull` users and groups if they don't exist yet. These are unprivileged users for Dovecot's internal use. They don't need a home directory or a shell. - Create `vmail` user and `vmail` group. This is the user/group that's used to access the mails. - Create `/home/vmail` directory owned by `vmail:vmail`. The mails for all users are stored under this directory. - Create `/var/log/dovecot.log` and `/var/log/dovecot-info.log` files owned by `vmail:vmail`, so that [LDA](/main/core/config/delivery/lda.html) can write to them. ## `dovecot.conf` Below is a fully working `dovecot.conf` file. You can use it directly, but it might be better to instead use as a guide to alter your current configuration. If you want to configure SSL, see [SSL](/main/core/config/ssl.html). dovecot ``` protocols = imap pop3 # It's nice to have separate log files for Dovecot. You could do this # by changing syslog configuration also, but this is easier. log_path = /var/log/dovecot.log info_log_path = /var/log/dovecot-info.log # Disable SSL for now. ssl = no auth_allow_cleartext = yes # We're using Maildir format mail_driver = maildir mail_path = ~/Maildir # If you're using POP3, you'll need this: pop3_uidl_format = %{guid} # Authentication configuration: auth_verbose = yes auth_mechanisms = plain passdb passwd-file { passwd_file_path = /etc/dovecot/passwd } userdb static { fields { uid = vmail gid = vmail home = /home/vmail/%{user} } } ``` ## `/etc/dovecot/passwd` See [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) for the full file format. Here we're interested only having usernames and passwords in it: ``` test:{PLAIN}pass:::::: bill:{PLAIN}secret:::::: timo@example.com:{PLAIN}hello123:::::: dave@example.com:{PLAIN}world234:::::: joe@elsewhere.org:{PLAIN}whee:::::: jane@elsewhere.org:{PLAIN}mypass:::::: ``` As you can see, you can use multiple domains in the file, or no domains at all. Dovecot doesn't care about domains. The extra colons are needed for [passwd-file authentication database](/main/core/config/auth/databases/passwd_file.html) format, and can be omitted if you are using the static user database in the example above. Users can be added by editing this file. Dovecot automatically notices the new users immediately after they're added. It also creates their home directories when the user logs in. ### Passwords The passwords in the example passwd file are listed using cleartext scheme. It's possible to use other [Password Schemes](/main/core/config/auth/schemes.html) as well. For example, sha256-crypt would be a pretty strong scheme. You can create them using `[doveadm pw](/main/core/summaries/doveadm.html#pw)` utility, for example: sh ``` doveadm pw -s sha256-crypt ``` ``` Enter new password: foo Retype new password: foo {SHA256-CRYPT}$5$88T/Emz.AbSmbz5C$D3GLxhvDffdN1ldpKkulh2fHyUNzvojIjiVbTovPdyC ``` Note that you won't get the same output after `{SSHA256}` as above, because Dovecot uses random salts when creating the SSHA256 hash. This means that even if multiple users have the same password, you won't know that because their hashes are different. The passwd file entry would be: ``` {SHA256-CRYPT}$5$88T/Emz.AbSmbz5C$D3GLxhvDffdN1ldpKkulh2fHyUNzvojIjiVbTovPdyC ``` Joe would now have "foo" as his password. ## SMTP Server Configuration ### Delivering Mails You can configure the SMTP server to deliver mails internally, or you can use Dovecot [LDA](/main/core/config/delivery/lda.html) or Dovecot [LMTP Server](/main/core/config/delivery/lmtp.html). Using Dovecot's tools to deliver mails gives you better performance because they update Dovecot's index files while saving the mails. See [LDA](/main/core/config/delivery/lda.html) or [LMTP Server](/main/core/config/delivery/lmtp.html) for configuration information. In config you should have: doveconf ``` protocol lda { postmaster_address = postmaster@example.com } ``` ### SMTP AUTH If you're using one of these MTAs, you can use Dovecot [SASL](/main/core/admin/sasl.html) to authenticate SMTP. - [howto\_postfix\_and\_dovecot\_sasl](/main/howto/sasl/postfix.html) - [Exim (v4.64+) configuration](/main/howto/sasl/exim.html) - [chasquid (v0.04+) configuration](/main/howto/sasl/chasquid.html) ## Quota If you need to have [quota](/main/core/plugins/quota.html), add this to `dovecot.conf`: doveconf ``` mail_plugins { quota = yes } protocol imap { mail_plugins { imap_quota = yes } } quota User { } ``` Then configure quota by adding `userdb_quota_storage_size`[userdb: Extra Fields](/main/core/config/auth/userdb.html#extra-fields) `/etc/dovecot/passwd`, for example: ``` joe:{PLAIN}pass::::::userdb_quota_storage_size=100M jane:{PLAIN}pass::::::userdb_quota_storage_size=200M ``` Joe has now 100MB quota and Jane has 200MB quota. # Running with Docker Dovecot CE comes with Docker images published at [https://hub.docker.com/r/dovecot/dovecot](https://hub.docker.com/r/dovecot/dovecot). ## Image flavors Currently we provide aarch64 and amd64 architecture for the images. There are three kinds of images: Image naming follows pattern `{,-dev,-root}`, where version can be latest or exact published version. Images are based on Debian slim image, distribution is occasionally upgraded when new versions are released. ### dovecot/dovecot:latest This is a hardened rootless image, which runs everything as vmail user, and minimal amount of binaries installed. This image uses non-standard ports, see [Listening ports](#listening-ports) for more information. Linux capability `CAP_SYS_CHROOT` is needed for the container. See [below](#running-without-linux-capabilities) for how to remove this requirement. ### dovecot/dovecot:latest-dev This is the same as latest, but without hardening. It still runs rootless with vmail, and if you want to drop `CAP_SYS_CHROOT`, you still need to add the same configuration drop-in. ### dovecot/dovecot:latest-root This image is suitable for running as root, which means there will be different users like `dovenull`, `dovecot` and `vmail` used. Also listening ports will be default ports, and not the non-privileged ones. ## Minimal setup To run Dovecot you can start it with: console ``` docker run -p 143:31143 -p 993:31993 dovecot/dovecot:latest ``` This will expose IMAP and IMAPS ports, but all data is kept locally. ## Persisting mail data To persist data, mount volume to `/srv/vmail`. Remember that this needs to be writable to UID 1000 internally. Example: console ``` docker run -v /srv/vmail:/srv/vmail -p 143:31143 -p 993:31993 dovecot/dovecot:latest ``` ## Configuring instance These docker images are rootless since v2.4.0. This means they are ran with vmail (UID 1000). To change configuration, put configuration drop-ins to `/etc/dovecot/conf.d`. Example: console ``` docker run -v /etc/dovecot-config:/etc/dovecot/conf.d:ro -v /srv/vmail:/srv/vmail -p 143:31143 -p 993:31993 dovecot/dovecot:latest ``` POP3 service is not enabled by default, if you need pop3, place a `pop3.conf` drop-in to `conf.d`: doveconf ``` protocols { pop3 = yes } ``` By default imap, submission, lmtp and sieve protocols are enabled. ### Authentication The default auth configuration is in `conf.d/auth.conf`, which has doveconf ``` passdb static { password = %{env:USER_PASSWORD} } ``` This is useful only for testing purposes and single-user instances. To configure multiple users or other authentication methods, you need to override this file. If you override this file, it also contains doveconf ``` import_environment { DOVEADM_PASSWORD = %{env:DOVEADM_PASSWORD | default} USER_PASSWORD = %{env:USER_PASSWORD | default('{CRYPT}*')} } ``` If you rely on either of these variables, you need to ensure you carry it over. ### TLS configuration Default certificate is expected at `/etc/dovecot/ssl/tls.crt` and key at `/etc/dovecot/ssl/tls.key`. You can override `conf.d/ssl.conf` to change this. ### Complex configuration You can also override the entire `/etc/dovecot/dovecot.conf` file, just make sure you include `/etc/dovecot/vendor.d/rootless.conf` in your configuration either direcly, or with `!include` directive. This is not needed if you use the `-root` variant image. ## Listening ports Since v2.4.1 ports are exposed as non-privileged ports. You need to map these to the ports that you need. For latest-root image, the ports are standard, and this does not apply. ### Exposed protocols | Protocol | Port | | --- | --- | | imap | 31143 | | imaps | 31993 | | pop3 | 31110 | | pop3s | 31995 | | submissions | 31465 | | submission | 31587 | | lmtps | 31024 | | managesieve | 34190 | | HTTP API | 8080 | | Metrics | 9090 | ## Running read-only To run the system fully read-only, use: console ``` docker run --read-only --tmpfs /tmp --tmpfs /run/dovecot -v /srv/vmail:/srv/vmail --rm -it dovecot/dovecot:latest ``` Dovecot will need write permissions to `/tmp`, `/run/dovecot` and persistent mail storage at `/srv/vmail`. ## Running without Linux capabilities By default, Dovecot needs `CAP_SYS_CHROOT` capability. To remove this requirements, you can prevent chrooting by placing no-chroot.conf to `conf.d` directory: doveconf ``` service submission-login { chroot = } service imap-login { chroot = } service pop3-login { chroot = } service managesieve-login { chroot = } service imap-urlauth-login { chroot = } ``` and run Dovecot using: console ``` docker run -v /etc/dovecot-config:/etc/dovecot/conf.d:ro --security-opt "no-new-privileges" --rm -it dovecot/dovecot:latest ``` ## Running as Debug/Testing Install To run the Docker image as a test install, a password needs to be set - by default, the Docker image does not allow authentication. To quickly setup a local system to test IMAP: console ``` docker run --rm --name dovecot-test -p 31143:31143 -e USER_PASSWORD=password --pull always dovecot/dovecot:latest-dev ``` This command: - Exposes IMAP (non-secure) only on port 31143 - Allows any username to authenticate with "password" - Attempts to use/download the latest version of the container - Names the container as "dovecot-test" - All data will be removed when the container exits - Provides shell access to the container (requires `latest-dev`) To run a command on the container (for example, doveadm): console ``` docker exec -it dovecot-test doveadm ``` For shell access to the container: console ``` docker exec -it dovecot-test bash ``` # Installation Guide ## Target Platform As part of improving maintainability and sustainability, Dovecot has defined a target platform specification and a minimum language standard beginning with version v2.4.0. The target platform specification is [POSIX.1-2008](https://pubs.opengroup.org/onlinepubs/9699919799.2008edition/). A C99/C11 compatible compiler is required, optimally with GNU extensions available. Currently glibc 2.17 is assumed to be the lowest supported C Standard library version, although others (e.g. older versions or musl) might work as well, although not explicitly tested. Dovecot CE is maintained and tested for Linux. Other distributions - especially BSD derivatives (e.g. FreeBSD, OpenBSD, macOS, etc.) - are maintained on a best-effort basis only. ## Dovecot CE Repositories The Dovecot team provides packages of Dovecot components for various Linux distributions. Details can be found at: [https://repo.dovecot.org/](https://repo.dovecot.org/). ## Compiling Dovecot From Source TIP Dovecot is provided by package managers on most popular operating systems, and we also provide packages at [https://repo.dovecot.org/](https://repo.dovecot.org/). We encourage you to use these instead of building sources yourself. For most people it is enough to do: sh ``` ./configure make sudo make install ``` That installs Dovecot under the `/usr/local` directory. The configuration file is in `/usr/local/etc/dovecot.conf`. Logging goes to syslog's mail facility by default, which typically goes to `/var/log/mail.log` or something similar. If you are in a hurry, you can then jump to [Quick configuration](/main/core/config/quick.html). If you have installed some libraries into locations which require special include or library paths, you can pass them in the `CPPFLAGS` and `LDFLAGS` environment variables. For example: sh ``` CPPFLAGS="-I/opt/openssl/include" LDFLAGS="-L/opt/openssl/lib" ./configure ``` You'll need to create two users for Dovecot's internal use: - **dovenull** - Used by untrusted imap-login and pop3-login processes, `[default_login_user](/main/core/summaries/settings.html#default_login_user)` setting. - **dovecot** - Used by slightly more trusted Dovecot processes, `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` setting. Both of them should also have their own **dovenull** and **dovecot** groups. See [System Users](/main/core/config/auth/users/system.html) for more information. ### Compiling Dovecot From Git If you got Dovecot from Git, for instance with sh ``` git clone https://github.com/dovecot/core.git dovecot ``` you will first need to run `./autogen.sh` to generate the `configure` script and some other files. This requires that you have the following software/packages installed: - wget - autoconf - automake - libtool - pkg-config - gettext - GNU make. It is advisable to add `--enable-maintainer-mode` to the `configure` script. Thus: sh ``` ./autogen.sh ./configure --enable-maintainer-mode make sudo make install ``` For later updates, you can use: sh ``` git pull make sudo make install ``` ### SSL/TLS Support Dovecot uses OpenSSL for SSL/TLS support and it should be automatically detected. If it is not, you are missing some header files or libraries, or they are just in a non-standard path. Make sure you have the `openssl-dev` or a similar package installed, and if it is not in the standard location, set `CPPFLAGS` and `LDFLAGS` as shown in the first section above. By default the SSL certificate is read from `/etc/ssl/certs/dovecot.pem` and the private key from `/etc/ssl/private/dovecot.pem`. The `/etc/ssl` directory can be changed using the `--with-ssldir=DIR` configure option. Both can of course be overridden from the configuration file. You can use [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/#server=dovecot&config=modern) to help create a default SSL configuration. ### Optional Configure Options Options are usually listed as `--with-something` or `--enable-something`. If you want to disable them, do it as `--without-something` or `--disable-something`. There are many default options that come from autoconf, automake or libtool. They are explained elsewhere. #### Help Related Options ##### `--help` Gives a full list of available options. ##### `--help=short` List the dovecot specific options only and hide the generic configuration options. #### Compiler options ##### `--enable-hardening` Enables various compiler hardenings. Defaults to yes. ##### `--enable-ubsan` Compiles code with Unspecified Behaviour sanitizer. ##### `--with-fuzzer=clang` Enables AFL Fuzzing support with clang. Requires clang compiler. ##### `--enable-local-fuzzer=yes` Assumes fuzzing is not done in an isolated environment. ###### `--with-retpoline=` Sets the mitigation option for Spectre Variant 2. This can be `keep` (no mitigation), `thunk` or `thunk-inline` to enable it. ###### `--enable-lto` Turns on LTO (Link-time Optimization) support using fat objects. Requires support from compiler and linker. ###### `--with-harden-sls=` Sets the Straight-Line Speculation (SLS) mitigation mode. Defaults to none, see [https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mharden-sls-1](https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mharden-sls-1). ###### `--with-fcf-protection=` Sets Control-Flow protection mode, see [https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fcf-protection](https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fcf-protection). #### Dovecot specific Options Here is a list of options that Dovecot adds. You should not usually have to change these, these are usually not needed. ##### `--enable-devel-checks` Enables some extra sanity checks. This is mainly useful for developers. It does quite a lot of unnecessary work but should catch some programming mistakes more quickly. ##### `--enable-asserts` Enable assertion checks, enabled by default. Disabling them may slightly save some CPU, but if there are bugs they can cause more problems since they are not detected as early. ##### `--without-shared-libs` Link Dovecot binaries with static libraries instead of dynamic libraries. ##### `--disable-largefile` Specifies if we use 32bit or 64bit file offsets in 32bit CPUs. 64bit is the default if the system supports it (Linux and Solaris do). Dropping this to 32bit may save some memory, but it prevents accessing any file larger than 2 GB. ##### `--with-mem-align=BYTES` Specifies memory alignment used for memory allocations. It is needed with many non-x86 systems and it should speed up x86 systems too. Default is 8, to make sure 64bit memory accessing works. ##### `--with-ioloop=IOLOOP` Specifies what I/O loop method to use. Possibilities are `select`, `poll`, `epoll` and `kqueue`. The default is to use the best method available on your system. ##### `--with-notify=NOTIFY` Specifies what file system notification method to use. Possibilities are `dnotify`, `inotify` (both on Linux), `kqueue` (FreeBSD) and `none`. The default is to use the best method available on your system. #### Generic Features ##### `--with-lua` Enables Lua support for authentication and push notifications. ##### `--with-pcre2` Enables regular expression support with libpcre2. ##### `--with-apparmor` Enabled support for apparmor context hats. #### `--with-systemd` Enables Systemd integration support. #### `--with-libcap` Enables libcap to drop capabilities when not needed. #### Experimental features ##### `--enable-experimental-mail-utf8` Enables support from MAIL UTF8 and SMTPUTF8. These still need to be enabled with `[mail_utf8_extensions](/main/core/summaries/settings.html#mail_utf8_extensions)`. ##### `--enable-experimental-imap4rev2` Enables support for IMAPrev2. #### FTS Options ##### `--with-flatcurve` Flatcurve full text search (requires Xapian libraries). ##### `--with-solr` Build with Solr full text search support ##### `--with-textcat` Build with support for text language detection with libtextcat. ##### `--with-stemmer` Build with stemmer support from libstemmer. #### Compression Libraries ##### `--with-zlib` Build with zlib compression support (default if detected) ##### `--with-zstd` Build with zStandard compression support (default if detected) #### SQL Driver Options SQL drivers are typically used for authentication, and they may be used as a lib-dict driver too, which can be used by plugins for different purposes. ##### `--with-cassandra` Build with cassandra support (requires `cassandra-cpp-driver`) ##### `--with-pgsql` Build with PostgreSQL support (requires `pgsql-devel`, `libpq-dev` or similar package) ##### `--with-mysql` Build with MySQL or MariaDB support (requires `mysql-devel`, `libmysqlclient-dev`, `libmariadb-dev-compat` or similar package) ##### `--with-sqlite` Build with SQLite3 driver support (requires `sqlite-devel`, `libsqlite3-dev` or similar package) #### Authentication Hash Options ##### `--with-sodium` Build with libsodium support, enabling ARGON2 and ARGON2ID password hashes. #### Authentication Driver Options The basic drivers are built if the system is detected to support them: ##### `--with-pam` Build with [PAM authentication database](/main/core/config/auth/databases/pam.html) support ##### `--with-bsdauth` Build with [BSD authentication database](/main/core/config/auth/databases/bsd.html) support #### Authentication Driver Options (Extra Libraries Needed) Some drivers require extra libraries and are not necessarily wanted, so they are built only if specifically enabled: ##### `--with-sql(=plugin)` Build with generic SQL support (drivers are enabled separately, see above). You can also build this as a plugin. ##### `--with-ldap(=plugin)` Build with LDAP support (requires `openldap-devel`, `libldap2-dev` or similar package). You can also build this as a plugin. ##### `--with-gssapi(=plugin)` Build with GSSAPI authentication support (requires `krb5-devel`, `libkrb5-dev` or similar package) ### Dynamic IMAP and POP3 Modules The `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` setting lists all plugins that Dovecot is supposed to load from the `[mail_plugin_dir](/main/core/summaries/settings.html#mail_plugin_dir)` directory at program start. These plugins can do anything they want. The plugin filename is prefixed with a number which specifies the order in which the plugins are loaded. This is important if one plugin depends on another. # Sieve (Pigeonhole) Installation ## Prebuilt Binaries TIP This is the recommended way of installing Dovecot/Pigeonhole. Pigeonhole is the name of the project that adds support for the Sieve language ([RFC 5228](https://datatracker.ietf.org/doc/html/rfc5228)) and the ManageSieve protocol ([RFC 5804](https://datatracker.ietf.org/doc/html/rfc5804)) to Dovecot (`[dovecot(1)](/main/core/man/dovecot.1.html)`). You can get pigeonhole packages from [https://repo.dovecot.org/](https://repo.dovecot.org/). ### OS Distributions Dovecot/Pigeonhole is packaged by many OS distributions. Search your package manager for 'dovecot', 'pigeonhole', or 'sieve' to discover the packages on your particular system. ## Getting the Sources You can download the latest released sources from the [Pigeonhole download page](https://pigeonhole.dovecot.org/download.html). Alternatively, you can get the sources, including the most recent unreleased changes, from the [GitHub repository](https://github.com/dovecot/pigeonhole). ## Compiling If you downloaded the Git sources, you will need to execute `./autogen.sh` first to build the automake structure in your source tree. This process requires autotools and libtool to be installed. If you installed Dovecot from sources, Pigeonhole's configure script should be able to find the installed `dovecot-config` automatically: sh ``` ./configure make sudo make install ``` If this doesn't work, you can use `--with-dovecot=` configure option, where the path points to a directory containing `dovecot-config` file. This can point to an installed file: sh ``` ./configure --with-dovecot=/usr/local/lib/dovecot make sudo make install ``` or to Dovecot source directory that is already compiled: sh ``` ./configure --with-dovecot=../dovecot-2.3.21/ make sudo make install ``` WARNING You need to recompile Pigeonhole when you upgrade Dovecot to a new version, because otherwise the Sieve interpreter plugin will fail to load with a version error. # Startup Scripts ## Launchd plist File This file is for Mac OS X 10.4 and later. For 10.3 and earlier StartupItems must be used. Save it under `/Library/LaunchDaemons/` with `.plist` extension (e.g. `org.dovecot.dovecot.plist`, following naming conventions of Apple and other vendors). Ownership should be `root:wheel`. xml ``` Label org.dovecot OnDemand ProgramArguments /usr/local/sbin/dovecot -F RunAtLoad ServiceDescription Dovecot mail server ``` If Dovecot is not started as a foreground process, launchd will keep restarting it until the "No authentication sockets found" error message shows up in the logs and further logins are denied. ## Sample Dovecot init.d Script This is a basic init script that should work in all operating systems. Place it in `/etc/init.d/dovecot` and change the DAEMON path if needed. ``` ### BEGIN INIT INFO # Provides: dovecot # Required-Start: $local_fs $remote_fs $network $syslog $time # Required-Stop: $local_fs $remote_fs $network $syslog # Should-Start: postgresql mysql slapd winbind # Should-Stop: postgresql mysql slapd winbind # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Dovecot init script # Description: Init script for dovecot services ### END INIT INFO # Example /etc/init.d/dovecot script. Change DAEMON if necessary. # License is public domain. DAEMON=/usr/local/sbin/dovecot # Uncomment to allow Dovecot daemons to produce core dumps. #ulimit -c unlimited test -x $DAEMON || exit 1 set -e base_dir=`$DAEMON config -h base_dir` pidfile=$base_dir/master.pid if test -f $pidfile; then running=yes else running=no fi case "$1" in start) echo -n "Starting Dovecot" $DAEMON echo "." ;; stop) if test $running = yes; then echo "Stopping Dovecot" kill `cat $pidfile` echo "." else echo "Dovecot is already stopped." fi ;; reload) if test $running = yes; then echo -n "Reloading Dovecot configuration" kill -HUP `cat $pidfile` echo "." else echo "Dovecot isn't running." fi ;; restart|force-reload) echo -n "Restarting Dovecot" if test $running = yes; then kill `cat $pidfile` sleep 1 fi $DAEMON echo "." ;; *) echo "Usage: /etc/init.d/dovecot {start|stop|reload|restart|force-reload}" >&2 exit 1 ;; esac exit 0 ``` # Upgrading Dovecot CE from 2.2 to 2.3 WARNING Downgrading is possible to v2.2.27 and later. (v2.2.27 accidentally broke `dovecot.index*` backwards compatibility a bit.) ## Dovecot CE ### Settings Changes - `mdbox_purge_preserve_alt` setting removed. It's always assumed to be "yes" now. ### Changed Setting Defaults | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)` | 2M | 10M | ## Dovecot Core ### Settings Changes - `director_consistent_hashing` setting removed. It's always assumed to be "yes" now. WARNING You can't run a director ring with mixed `director_consistent_hashing` settings. If you already didn't have it set to "yes", upgrading to v2.3 will require you to shutdown the entire director ring. It may be safer to first do this setting change in v2.2 before the upgrade. If you really don't wish to shutdown the ring, an alternative would be to set up a whole new director ring. Then start moving users to the new ring in the Dovecot proxy. To avoid the same user having connections to both rings at the same time (-> two backends at the same time), this would need to be done so that passdb moves the user to the new ring and old connections are kicked. See [proxy passdb](/main/core/config/proxy/overview.html). - `director_doveadm_port` setting removed. Name the `inet_listener doveadm { .. }` instead. - `mdbox_purge_preserve_alt` setting removed. It's always assumed to be "yes" now. - `[recipient_delimiter](/main/core/summaries/settings.html#recipient_delimiter)` setting used to be treated as a separator string. Now it's instead treated as a list of alternative delimiter characters. - Time interval based settings no longer default to "seconds". All numbers must explicitly be followed by the time unit (except 0). This is important, because some settings now support milliseconds as well. - `fs-posix`: `prefix=path` parameter no longer automatically appends `/` to the path if it's not there. This allows using it properly as a prefix, instead of only a directory prefix. Make sure you have the `/` appended to the prefix, or the "dir/filename" will be accessed just as "dirnamename". - `ssl_protocols` setting was replaced by `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)`. Now you only specify the minimum ssl protocol version Dovecot accepts, defaulting to TLSv1. - `ssl_parameters` was replaced with `ssl_dh`. See [Diffie-Hellman Parameters for SSL](#diffie-hellman-parameters-for-ssl). - `SSLv2` is no longer supported as SSL protocol. ### Statistics Redesign The statistics code was redesigned. - Statistics is no longer optional - it is always there. - The old "stats" plugin was renamed to old\_stats - The `doveadm stats` command was renamed to `doveadm oldstats`. - There's a new `doveadm stats` command that isn't compatible with the old one. - The new stats code doesn't require a plugin, so make sure you remove `stats` from `[mail_plugins](/main/core/summaries/settings.html#mail_plugins)` setting. For more details see [Statistics](/main/core/config/statistics.html). Config changes required to 2.2.x config to keep using the "old" stats: - `mail_plugins = stats` -> `mail_plugins = old_stats` - `mail_plugins = imap_stats` -> `mail_plugins = imap_old_stats` - `service stats` -> `service old-stats` - `executable = stats` -> `executable = old-stats` - `fifo_listener stats-mail` -> `fifo_listener old-stats-mail` - `fifo_listener stats-user` -> `fifo_listener old-stats-user` - `unix_listener stats` -> `unix_listener old-stats` - `plugin { stats_refresh }` -> `plugin { old_stats_refresh }` - `plugin { stats_notify_path }` -> `plugin { old_stats_notify_path }` - `plugin { stats_track_cmds }` -> `plugin { old_stats_track_cmds }` - `auth_stats` -> keep as `auth_stats` - `stats_*` settings -> `old_stats_*` ### Submission Service (new) Dovecot can now act as a submission service. See [submission server](/main/core/config/submission.html) for more information. ### Localhost Auth Penalty Dovecot no longer disables auth penalty waits for clients connecting from localhost (or `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` in general). The previous idea was that it would likely be a webmail that would have its own delays, but there are no guarantees about this. If the old behavior is still wanted, it's possible to do nowadays even more generically with e.g. setting following as the first passdb: dovecot.conf/etc/dovecot/passdb doveconf ``` passdb { driver = passwd-file args = username_format=%{rip} /etc/dovecot/passdb fields { noauthenticate = y } } ``` ``` 127.0.0.1:::::::nodelay=yes 192.168.10.124:::::::nodelay=yes ``` ### Changed Setting Defaults | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `[mdbox_rotate_size](/main/core/summaries/settings.html#mdbox_rotate_size)` | 2M | 10M | | `[mailbox_list_index](/main/core/summaries/settings.html#mailbox_list_index)` | no | yes | | `[imap_logout_format](/main/core/summaries/settings.html#imap_logout_format)` | n=%i out=%o | in=%i out=%o deleted=%{deleted} expunged=%{expunged} trashed=%{trashed} hdr\_count=%{fetch\_hdr\_count} hdr\_bytes=%{fetch\_hdr\_bytes} body\_count=%{fetch\_body\_count} body\_bytes=% | | `[ssl_cipher_list](/main/core/summaries/settings.html#ssl_cipher_list)` | ALL:!LOW:!SSLv2:!EXP:!aNULL | ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW@STRENGTH | | `[mail_log_prefix](/main/core/summaries/settings.html#mail_log_prefix)` | "%s(%u): " | "%s(%u)<%{pid}><%{session}>: " | | mysql: `ssl_verify_server_cert` | no | yes | | `[ssl_options](/main/core/summaries/settings.html#ssl_options)` | | no\_compression is now the default, and a new compression option is introduced for enabling compression | ### Diffie-Hellman Parameters for SSL - `ssl-parameters.dat` file is now obsolete. You should use `ssl_dh` setting instead: `ssl_dh= /etc/dovecot/dh.pem` ``` - ssl-params process has also been removed, as it is no longer used to generate these parameters. - You are encouraged to create at least 2048 bit parameters. 4096 is industry recommendation. - Note that it will take LONG TIME to generate the parameters, and it should be done with a machine that has GOOD SOURCE OF ENTROPY. Running it on a virtual machine is not recommended, unless there is some entropy helper/driver installed. Running this on your production proxy can starve connections due to lack of entropy. - Since v2.3.3+ DH parameter usage is **optional** and can be omitted. In that case one must also remove (or rename) `/var/lib/dovecot/ssl-parameters.dat` . You are invited to amend ciphers to disallow non-ECC based DH algorithms, but if you don't and someone does try to use them, error will be emitted. - Example: `ssl_cipher_list=ALL:!kRSA:!SRP:!kDHd:!DSS:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK:!RC4:!ADH:!LOW:!DH@STRENGTH` ### Other Changes - Invalid `[postmaster_address](/main/core/summaries/settings.html#postmaster_address)` now causes a failure early on with sieve/imap\_sieve plugin enabled. It still defaults to `postmaster@%d`, which expands to invalid `postmaster@` address if your usernames do not contain a domain, or are converted into domainless usernames by passdb/userdb. - Linux: Dovecot no longer enables core dumping for "setuid processes", which most of them are. - To enable them with Linux kernel v3.6+: Make sure core dumps get written to a globally shared directory and enable them with: `sysctl -w fs.suid_dumpable=2` - With older Linux kernel versions you can set it to 1, but that's not good for security of your system. - You can also revert to old behavior with: `import_environment = $import_environment PR_SET_DUMPABLE` - However, this also may have some security implications depending on the setup. Mainly if you have system users and you've enabled chrooting or mail\_access\_groups, this could allow the system users to gain unintentional access. - userdb nss was removed. Use userdb passwd instead. - doveadm: table formatter prints the header now to stdout, not stderr - doveadm: Removed mount commands - OpenSSL version is required to be at least 1.0.1 for Dovecot to build - subscriptions file is written in a new version 2 format. Dovecot v2.2.17 and newer can read this file. - mail\_log plugin: Headers are logged as UTF-8 (instead of MIME-encoded) - auth: When iterating users in userdb passwd, skip users that aren't in the first/last\_valid\_gid range - auth protocol has changed some error fields: - temp -> code=temp\_fail - authz -> code=authz\_fail - user\_disabled -> code=user\_disabled - pass\_expired -> code=pass\_expired - auth now supports bcrypt algorithm by default. - Some API changes have been made, if you have your own plugins please be aware that they might require change(s) to be compatible again. - Due to the new stats environment, for now some environments may get harmless errors about not being able to connect to stats-writer socket. To avoid these errors, give enough permissions for the processes to connect to the stats-writer, for example: doveconf ``` service stats { client_limit = 10000 # make this large enough so all Dovecot processes (especially imap, pop3, lmtp) can connect to it unix_listener stats-writer { user = vmail #mode = 0666 # Use only if nothing else works. It's a bit insecure, since it allows any user in the system to mess up with the statistics. } } ``` # Upgrading Dovecot CE from 2.3 to 2.3.x ## Dovecot CE ### v2.3.x to v2.3.14 - Removed cydir storage format. It was never intended for production use. - Removed snarf plugin. It was for UW-IMAP's mbox compatibility, which is unlikely to be needed anymore. - Removed mail\_filter plugin. It was mainly intended as an example plugin. ## Dovecot Core ### v2.3.x to v2.3.7 - fts-solr: The obsolete break-imap-search parameter is no longer recognized ### v2.3.x to v2.3.12 - Event filter syntax has changed, see [Event Filtering](/main/core/config/events/filter.html). ### v2.3.x to v2.3.14 - Removed autocreate plugin. Use `[mailbox { auto }](/main/core/summaries/settings.html#mailbox_auto)` instead. - Removed expire plugin. Use `[mailbox { autoexpunge }](/main/core/summaries/settings.html#mailbox_autoexpunge)` instead. - Removed xz write support from zlib plugin. (Reading xz compressed mails is still supported.) Use another compression algorithm. ### v2.3.x to v2.3.15 - `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` default changed to TLSv1.2, as older TLS versions are deprecated (see [RFC 8996](https://datatracker.ietf.org/doc/html/rfc8996)). Change it to TLSv1 or TLSv1.1 if you need to support older, deprecated protocols. - The 'SNIPPET' and 'PREVIEW (w/explicit algorithm selection)' IMAP commands have been deprecated. The new [RFC 8970](https://datatracker.ietf.org/doc/html/rfc8970) compliant PREVIEW command should be exclusively used in the future. - [fs-compress plugin](/main/core/plugins/fs_compress.html) now accept per-algorithm value. - `plugin-zlib ` now accepts per-algorithm value. - `plugin-imap-zlib` now uses per-algorithm compression level settings. The old setting is ignored. ### v2.3.x to v2.3.16 - [auth\_worker service](/main/core/config/service.html#auth-worker) service service\_count setting has been changed. ### v2.3.x to v2.3.20 - `fts_stopwords_workaround` has been introduced. The default for the setting, `auto`, activates some mitigations for the problem of some searches failing to retrieve the expected result when stopwords and multiple languages are used together. To revert to the pre 2.3.20 behavior, set `fts_stopwords_workaround = no`. # Upgrading Dovecot CE from 2.3 to 2.4 ## Upgrade Path Before upgrading, please look at the list of removed features carefully. If you are doing in-place upgrade, ensure that you first upgrade to latest 2.3 release, and then upgrade to 2.4. Alternatively, you can [migrate](/main/core/admin/migration.html) your data to new setup. ## Example Configuration The old v2.3 example configuration has been converted to v2.4 once. It's not being kept up-to-date afterwards, but it can be helpful: [https://github.com/dovecot/tools/blob/main/dovecot-2.4.0-example-config.tar.gz](https://github.com/dovecot/tools/blob/main/dovecot-2.4.0-example-config.tar.gz) ## Upgrading tool For configuration conversion assistance, we provide [https://dovecot.org/upgrader/](https://dovecot.org/upgrader/) that can be used to convert your 2.3 configuration into 2.4. ## Dovecot CE ### Required Admin Actions #### Sieve Global Sieve scripts must be recompiled. See [sievec](/main/core/config/sieve/overview.html#manually-compiling-sieve-scripts). ### Configuration Changes WARNING Dovecot 2.3.x settings will NOT work unless the configuration is changed as described in this section. #### Required Settings The first setting in `dovecot.conf` **MUST** now be `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)`. This helps to avoid unexpected configuration changes in the future. Another new required setting is `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)`. This helps to avoid unexpected storage file format incompatibilities. Note that the configuration syntax has been changed, and your old configuration **will not** work without changes. #### Configuration Redesign See [Dovecot Config File Syntax](/main/core/settings/syntax.html) for the new configuration syntax. This is similar to v2.3, but different in some ways. Especially the configuration is no longer hierarchical - all settings are global settings and can be used anywhere (although they might not actually do anything there). Settings can be inside various filters to specify where they are wanted to be used. To avoid repetition in setting name prefixes, they are automatically attempted to be prefixed to their parent filter names. For example these are equivalent for the `[passdb_sql_query](/main/core/summaries/settings.html#passdb_sql_query)` setting inside the `[passdb](/main/core/summaries/settings.html#passdb)` filter: doveconf ``` passdb sql { passdb_sql_query = SELECT ... } passdb sql { sql_query = SELECT ... } passdb sql { query = SELECT ... } passdb sql2 { # This will NOT work, as it expands to nonexistent passdb_sql2_query: #query = SELECT ... } ``` #### Plugin settings The `plugin { ... }` section no longer exists. Plugin settings are global the same as all other settings. #### Config $variables Using `$setting` variables in config files requires `$SET:` prefix now. For example `user = $default_internal_user` is now `user = $SET:default_internal_user`. See [variable expansion](/main/core/settings/syntax.html#variable-expansion) for more details. Note that earlier it was common to refer to the same setting, e.g.: doveconf ``` mail_plugins = $mail_plugins quota ``` Most of this is now unnecessary, because of the new [boollist](/main/core/settings/types.html#boolean-list) setting type, which allows you to use: doveconf ``` mail_plugins { quota = yes } ``` #### Variable expansion A new [Settings Variables](/main/core/settings/variables.html) syntax has been introduced. All old one-letter %variables have been removed, and some old variable aliases have been dropped too. The new syntax uses `|` character for "filters" which can be chained. This is why some of the old variables have been replaced by filters. For example the old `%d` variable is now `%{user | domain}`, which gets the "user" variable and applies the "domain" filter for it. ##### List of common short variables and their replacements. | Old variable | New syntax | | --- | --- | | `%a` | `%{local_port}` | | `%b` | `%{remote_port}` | | `%c` | `%{secured}` | | `%d` | `%{user | domain}` | | `%h` | `%{home}` | | `%l` | `%{local_ip}` | | `%m` | `%{mechanism}` | | `%n` | `%{user | username}` | | `%p` | `%{client_pid}` | | `%r` | `%{remote_ip}` | | `%s` | `%{protocol}` for login & auth settings, `%{service}` for storage settings | | `%u` | `%{user}` | | `%w` | `%{password}` | ##### Common modifier syntaxes and their replacements. | Old syntax | New syntax | | --- | --- | | `%1Mu` | `%{user | md5 | hexlify(1)}` | | `%2.1Mu` | `%{user | md5 | hexlify | substr(2,1)}` | | `%2.256N` | `%{user | md5 | substr(0, 8) % 256 | hex(2)}` | | `%256Nu` | `%{user | md5 | substr(0, 8) % 256 | hex}` | | `%Hu` | No conversion available, use one of the syntaxes. | | `%Ln` | `%{user | username | lower}` | | `%Lu` | `%{user | lower}` | | `%Mu` | `%{user | md5}` | ##### Common long variables and their replacements. | Old variable | New syntax | | --- | --- | | `%{auth_domain}` | `%{auth_user | domain}` | | `%{auth_username}` | `%{auth_user | username}` | | `%{lip}` | `%{local_ip}` | | `%{lport}` | `%{local_port}` | | `%{mech}` | `%{mechanism}` | | `%{orig_domain}` | `%{original_user | domain}` | | `%{orig_username}` | `%{original_user | username}` | | `%{orig_user}` | `%{original_user}` | | `%{real_lip}` | `%{real_local_ip}` | | `%{real_lport}` | `%{real_local_port}` | | `%{real_rip}` | `%{real_remote_ip}` | | `%{real_rport}` | `%{real_remote_port}` | | `%{rip}` | `%{remote_ip}` | | `%{rport}` | `%{remote_port}` | | `%{service}` for login & auth settings (but not storage settings) | `%{protocol}` | ##### Shared namespace variables Previously `%%u`, `%%n` and `%%d` could be used in shared namespace configuration. These were changed for the `[namespace_prefix](/main/core/summaries/settings.html#namespace_prefix)`: | Old variable | New syntax | | --- | --- | | `%%u` | `$user` | | `%%n` | `$username` | | `%%d` | `$domain` | For other settings, such as `[mail_path](/main/core/summaries/settings.html#mail_path)`: | Old variable | New syntax | | --- | --- | | `%%u` | `%{owner_user}` | | `%%n` | `%{owner_user | username}` | | `%%d` | `%{owner_user | domain}` | | `%%h` | `%{owner_home}` | #### Directory Hashing If you have been using `/home/%2.256N/%u` or similar constructs: - How to replace `%N` in new format: - `%2.256Nu` becomes `%{user | md5 | substr(0, 8) % 256 | hex(2)}` to return maximum 256 different hashes in range `00..ff`. - `%256Nu` becomes `%{user | md5 | substr(0, 8) % 256 | hex}` to return maximum 256 different hashes in range `0..ff` (without 0-padding in the front). - How to replace `%M` in new format: - `%1Mu/%2.1Mu/%u` becomes `%{user | md5 | hexlify(1)}/%{user | md5 | hexlify | substr(2,1)}/%{user}` to returns directories from `0/0/user` to `f/f/user`. - There is no way to use `%H` anymore. #### Unknown/Invalid Variables Unknown/invalid `%{variables}` cause Dovecot errors. This may cause, e.g., authentication failures if the old (broken) behavior was relied on. #### passdb/userdb Section Naming [passdb](/main/core/config/auth/passdb.html) and [userdb](/main/core/config/auth/userdb.html) sections now require a name, i.e.: doveconf ``` # This gives an error: passdb { ... } # Use this instead: passdb some_name { } ``` #### Empty userdb Variables userdb fields can be set to empty value. Previously they became changed to `yes` value. ### Added Features #### Auth Policy Parameters See [Authentication Policy](/main/core/config/auth/policy.html). | Parameter | Notes | | --- | --- | | `%{fail_type}` variable to `[auth_policy_request_attributes](/main/core/summaries/settings.html#auth_policy_request_attributes)` | Variable was added. | #### Cryptographic Features | Feature | Notes | | --- | --- | | ARGON2 password scheme | Support for the ARGON2 password scheme was added. | | SCRAM-SHA-1, SCRAM-SHA-256 | Support SASL mechanisms for outgoing connections. | | X25519, X448 | [mail-crypt plugin](/main/core/plugins/mail_crypt.html) and [Mail Crypt Plugin: fs-crypt](/main/core/plugins/mail_crypt.html#fs-crypt) now support these curves. | | ML-KEM | Changed: 2.4.5 [mail-crypt plugin](/main/core/plugins/mail_crypt.html) and [Mail Crypt Plugin: fs-crypt](/main/core/plugins/mail_crypt.html#fs-crypt) support ML-KEM keys with OpenSSL 3.3+. | #### imapc\_features Parameters See `[imapc_features](/main/core/summaries/settings.html#imapc_features)`. | Feature | Notes | | --- | --- | | `no-qresync` | Parameter was added. | #### Settings - `[auth_internal_failure_delay](/main/core/summaries/settings.html#auth_internal_failure_delay)` - `[fts_message_max_size](/main/core/summaries/settings.html#fts_message_max_size)` - `[login_socket_path](/main/core/summaries/settings.html#login_socket_path)` - `[quota_mailbox_count](/main/core/summaries/settings.html#quota_mailbox_count)` - `[quota_mailbox_message_count](/main/core/summaries/settings.html#quota_mailbox_message_count)` - `[submission_add_received_header](/main/core/summaries/settings.html#submission_add_received_header)` - `[cassandra_log_retries](/main/core/summaries/settings.html#cassandra_log_retries)` ### Deprecated Features | Feature | Notes | | --- | --- | | `[acl_global_path](/main/core/summaries/settings.html#acl_global_path)` | Using ACL file is deprecated, see [ACL Settings Migration](/main/installation/upgrade/2.3-to-2.4.html#use-acl-settings-instead-of-global-acl-directories-or-global-acl-file). | | fs-sis | Saving new mails' attachments via fs-sis is disabled, but reading SIS attachments is still supported. Missing SIS attachments are replaced with files filled with spaces. | ### Removed Features | Feature | Notes | | --- | --- | | fts-lucene, fts-squat | Use [fts-flatcurve plugin](/main/core/plugins/fts_flatcurve.html) or [fts-solr plugin](/main/core/plugins/fts_solr.html). | | dsync: Remove -D parameter | Parameter for disabling mailbox rename syncing removed. It hasn't been necessary for a long time, and it is broken. | | dsync | Use `doveadm sync` instead. `dsync` has been a symlink to `doveadm` already, this release removed the symlink completely. | | director | Support for the Dovecot Director architecture has been removed. For a small-scale, unsupported replacement, see [Lua Director](/main/howto/lua_director.html). | | replicator | Use [NFS](/main/core/config/nfs.html) or some other shared filesystem instead. | | Dovecot director role | Replaced with the [Dovecot Pro Palomar architecture](https://doc.dovecotpro.com/). | | Global ACL directory | Use `[acl](/main/core/summaries/settings.html#acl)` instead. See [below](#use-acl-settings-instead-of-global-acl-directories-or-global-acl-file) for details on migration. | | IMAP SETQUOTA command | Quota limits can no longer be modified using the IMAP SETQUOTA command. The `set_quota` setting has been removed. | | IPC process | Has been merged to anvil. | | OpenSSL support for older than 1.0.2 | Older versions are not supported anymore. | | Sieve extensions: `notify`, `imapflags`, `vnd.dovecot.duplicate` | These deprecated Sieve extensions have been removed. | | `ssl-parameters.dat` | This file is no longer converted automatically by config process, you need to set `[ssl_server_dh_file](/main/core/summaries/settings.html#ssl_server_dh_file)` setting if you need non-ECC Diffie-Hellman. | | TCP wrapper support | Use [Lua authentication database](/main/core/config/auth/databases/lua.html) instead. | | Weak password schemes | Weak password schemes are disabled by default; you need to use `[auth_allow_weak_schemes](/main/core/summaries/settings.html#auth_allow_weak_schemes)` to enable them. | | `local_name "multiple names" { ... }` | List each name as a separate `local_name { ... }` | #### Cassandra Parameters See [Cassandra configuration](/main/core/config/sql/cassandra.html). | Parameter | Notes | | --- | --- | | Cassandra `ssl_verify=cert-dns` setting | Removed, as it was deprecated by Cassandra cpp-driver due to it being insecure against MITM attacks. | #### Backends and Plugins | Backend | Notes | | --- | --- | | checkpassword auth database | Use [Lua authentication database](/main/core/config/auth/databases/lua.html) instead. | | Dict passdb & userdb driver | Use [Lua authentication database](/main/core/config/auth/databases/lua.html) instead. | | Dict quota; Dirsize quota | These drivers are removed. You should use [Quota Driver: Count](/main/core/plugins/quota.html#driver-count) instead along with [quota-clone plugin](/main/core/plugins/quota_clone.html). Note that switching to quota count can cause all users' indexes to update, so reserve time for this. | | imap-zlib plugin | The IMAP `COMPRESS` extension is now automatically enabled. | | listescape plugin | Use `[mailbox_list_storage_escape_char](/main/core/summaries/settings.html#mailbox_list_storage_escape_char)` instead. | | mailbox-alias plugin | Depending on the use case, replacement may be the `[mailbox_special_use](/main/core/summaries/settings.html#mailbox_special_use)` mailbox setting and/or [Sieve](/main/core/config/sieve/overview.html) filters. | | Memcached dict driver | Use [Redis](/main/core/config/dict.html#redis) instead. | | old-stats plugin | Use [Statistics](/main/core/config/statistics.html) instead. `auth_stats` setting has been removed too. | | shadow auth driver | Use [PAM authentication database](/main/core/config/auth/databases/pam.html) instead. | | XZ Compression | You need to perform migration using a different compression format. With [Maildir](/main/core/config/mailbox_formats/maildir.html), you can try uncompressing all your mail and compressing them with another algorithm while Dovecot is not running. | | zlib plugin | Use [mail-compress plugin](/main/core/plugins/mail_compress.html) with the `[mail_compress_write_method](/main/core/summaries/settings.html#mail_compress_write_method)` setting instead. | #### Settings | Setting | Notes | | --- | --- | | `auth_stats` | | | `dict_db_config` | Berkeley DB is not supported anymore. | | `imap_id_log` | Replaced by the `[imap_id_received](/main/core/summaries/events.html#imap_id_received)` event. | | `login_access_sockets` | Use [Lua authentication database](/main/core/config/auth/databases/lua.html) instead. Dovecot will fail to start if this setting is present in configuration. | | `quota_set` | | | `sieve_dir` | See [Script storage](/main/core/plugins/sieve.html#script-storage). | | `sieve_global_dir` | See [Script storage](/main/core/plugins/sieve.html#script-storage). | | `sieve_global_path` | See [Script storage](/main/core/plugins/sieve.html#script-storage). | | `sieve_editheader_protected` | Replaced by `[sieve_editheader_header_forbid_add](/main/core/summaries/settings.html#sieve_editheader_header_forbid_add)` and `[sieve_editheader_header_forbid_delete](/main/core/summaries/settings.html#sieve_editheader_header_forbid_delete)`. | | `sieve_vacation_max_subject_codepoints` | | ### Changed Features #### Settings | Setting | Notes | | --- | --- | | `[ssl](/main/core/summaries/settings.html#ssl)` | Connections from `[login_trusted_networks](/main/core/summaries/settings.html#login_trusted_networks)` are now also required to be SSL/TLS encrypted with the setting `ssl=required`. | | `[ssl_min_protocol](/main/core/summaries/settings.html#ssl_min_protocol)` | The `SSLv3` option was removed, as it is no longer secure. | #### Converted Settings | Setting | Notes | | --- | --- | | `acl` | Replaced by `[acl_driver](/main/core/summaries/settings.html#acl_driver)`. | | `acl_anyone` | Renamed to `[imap_acl_allow_anyone](/main/core/summaries/settings.html#imap_acl_allow_anyone)`. | | `auth_debug` | Use `[log_debug](/main/core/summaries/settings.html#log_debug)` filter instead. Example: `log_debug=category=auth`. | | `auth_default_realm` | Replaced by `[auth_default_domain](/main/core/summaries/settings.html#auth_default_domain)`. | | `auth_policy_server_timeout_msecs` | Replaced by `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` inside `[auth_policy](/main/core/summaries/settings.html#auth_policy)` named filter. | | `auth_worker_max_count` | Use [service-specific process limit](/main/core/config/service.html). | | `default_idle_kill` | Renamed to `[default_idle_kill_interval](/main/core/summaries/settings.html#default_idle_kill_interval)`. | | `disable_plaintext_auth` | Replaced by `[auth_allow_cleartext](/main/core/summaries/settings.html#auth_allow_cleartext)`. | | `doveadm_http_rawlog_dir` | Replaced by `[http_client_rawlog_dir](/main/core/summaries/settings.html#http_client_rawlog_dir)` inside `[doveadm_server](/main/core/summaries/settings.html#doveadm_server)` named filter. | | `event_exporter_format_args` | Replaced by `[event_exporter_time_format](/main/core/summaries/settings.html#event_exporter_time_format)`. | | `event_exporter_transport` | Renamed to `[event_exporter_driver](/main/core/summaries/settings.html#event_exporter_driver)`. | | `event_exporter_transport_args` | Split off to driver-specific settings, see [Event Export](/main/core/config/events/export.html). | | `event_exporter_transport_timeout` | Replaced by `[http_client_request_timeout](/main/core/summaries/settings.html#http_client_request_timeout)` and `[event_exporter_unix_connect_timeout](/main/core/summaries/settings.html#event_exporter_unix_connect_timeout)`. | | `imap_capability = +CAPA1` | Use `[imap_capability](/main/core/summaries/settings.html#imap_capability)` `{ CAPA1 = yes }` instead. | | `inet_listener { address }` | Replaced by `[listen](/main/core/summaries/settings.html#listen)` setting. | | `push_notification_backend` | Use `[push_notification_driver](/main/core/summaries/settings.html#push_notification_driver)` instead. | | `mail_crypt_require_encrypted_user_key` | Replaced by `[crypt_user_key_require_encrypted](/main/core/summaries/settings.html#crypt_user_key_require_encrypted)`, `[crypt_user_key_password](/main/core/summaries/settings.html#crypt_user_key_password)` and `[crypt_user_key_encryption_key](/main/core/summaries/settings.html#crypt_user_key_encryption_key)`. | | `verbose_ssl` | Use `[log_debug = category=ssl](/main/core/summaries/settings.html#log_debug)` instead. | | `mail_attachment_dir` | Renamed to `[mail_ext_attachment_path](/main/core/summaries/settings.html#mail_ext_attachment_path)`. | | `mail_attachment_fs` | Use `[fs](/main/core/summaries/settings.html#fs)` inside `[mail_ext_attachment](/main/core/summaries/settings.html#mail_ext_attachment)` named filter. | | `mail_attachment_hash` | Renamed to `[mail_ext_attachment_hash](/main/core/summaries/settings.html#mail_ext_attachment_hash)`. | | `mail_attachment_min_size` | Renamed to `[mail_ext_attachment_min_size](/main/core/summaries/settings.html#mail_ext_attachment_min_size)`. | | `mail_attribute_dict` | Use `[dict](/main/core/summaries/settings.html#dict)` inside `[mail_attribute](/main/core/summaries/settings.html#mail_attribute)` named filter. | | `mail_location` setting & `mail` userdb field | Split into multiple [mail\_\*](/main/core/config/mail_location.html#mail-location-setting) settings. | | `namespace { location }` setting | Use [mail\_\*](/main/core/config/mail_location.html#mail-location-setting) settings inside `[namespace](/main/core/summaries/settings.html#namespace)` named list filter. | | `notify_status_dict` | Use `[dict](/main/core/summaries/settings.html#dict)` inside `[notify_status](/main/core/summaries/settings.html#notify_status)` named filter. | | `notify_status_mailbox` | Use `[mailbox_notify_status](/main/core/summaries/settings.html#mailbox_notify_status)` inside `[mailbox](/main/core/summaries/settings.html#mailbox)` named list filters. | | `fts` | Replaced by `[fts](/main/core/summaries/settings.html#fts)` named filter. | | `fts_autoindex_exclude` | Replaced by boolean property `[fts_autoindex](/main/core/summaries/settings.html#fts_autoindex)`, nested inside `[mailbox](/main/core/summaries/settings.html#mailbox)` blocks. Note that the values are inverted as compared to those in the old `fts_autoindex_exclude`. | | `fts_decoder` | Replaced by `[fts_decoder_driver](/main/core/summaries/settings.html#fts_decoder_driver)` and `[fts_decoder_script_socket_path](/main/core/summaries/settings.html#fts_decoder_script_socket_path)`. | | `fts_enforced` | Replaced by `[fts_search_add_missing](/main/core/summaries/settings.html#fts_search_add_missing)` and `[fts_search_read_fallback](/main/core/summaries/settings.html#fts_search_read_fallback)`. | | `fts_index_timeout` | Renamed to `[fts_search_timeout](/main/core/summaries/settings.html#fts_search_timeout)`. The default value is set to `30 secs`. | | `fts_solr` | Replaced by `[fts_solr_url](/main/core/summaries/settings.html#fts_solr_url)`, `[fts_solr_batch_size](/main/core/summaries/settings.html#fts_solr_batch_size)`, `[fts_solr_soft_commit](/main/core/summaries/settings.html#fts_solr_soft_commit)`, and `[http_client_rawlog_dir](/main/core/summaries/settings.html#http_client_rawlog_dir)` settings. | | `fts_tika` | Replaced by `[fts_decoder_driver](/main/core/summaries/settings.html#fts_decoder_driver)` and `[fts_decoder_tika_url](/main/core/summaries/settings.html#fts_decoder_tika_url)`. | | `fts_language_config` | Renamed to `[textcat_config_path](/main/core/summaries/settings.html#textcat_config_path)`. | | `fts_languages` | Converted into `[language](/main/core/summaries/settings.html#language)` blocks. | | `fts_filters` | Split into `[language_filters](/main/core/summaries/settings.html#language_filters)`, `[language_filter_normalizer_icu_id](/main/core/summaries/settings.html#language_filter_normalizer_icu_id)`, `[language_filter_stopwords_dir](/main/core/summaries/settings.html#language_filter_stopwords_dir)`. | | `fts_tokenizers` | Split into `[language_tokenizers](/main/core/summaries/settings.html#language_tokenizers)`, `[language_tokenizer_address_token_maxlen](/main/core/summaries/settings.html#language_tokenizer_address_token_maxlen)`, `[language_tokenizer_generic_algorithm](/main/core/summaries/settings.html#language_tokenizer_generic_algorithm)`, `[language_tokenizer_generic_token_maxlen](/main/core/summaries/settings.html#language_tokenizer_generic_token_maxlen)`, `[language_tokenizer_generic_wb5a](/main/core/summaries/settings.html#language_tokenizer_generic_wb5a)`, `[language_tokenizer_kuromoji_icu_id](/main/core/summaries/settings.html#language_tokenizer_kuromoji_icu_id)`, `[language_tokenizer_kuromoji_split_compounds](/main/core/summaries/settings.html#language_tokenizer_kuromoji_split_compounds)`, `[language_tokenizer_kuromoji_token_maxlen](/main/core/summaries/settings.html#language_tokenizer_kuromoji_token_maxlen)`. | | `lazy_expunge` | Renamed to `[lazy_expunge_mailbox](/main/core/summaries/settings.html#lazy_expunge_mailbox)`. | | `service { idle_kill }` | Renamed to `[service_idle_kill_interval](/main/core/summaries/settings.html#service_idle_kill_interval)`. | | `service { service_count }` | Renamed to `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)`. The default value is set to `unlimited`. Value `0` is now a configuration error. | | oauth2 `username_format` | Renamed to `[oauth2_username_validation_format](/main/core/summaries/settings.html#oauth2_username_validation_format)`. | | oauth2 `pass_attrs` | Replaced by `[oauth2_fields](/main/core/summaries/settings.html#oauth2_fields)`. | | oauth2 `local_validation_key_dict` | Replaced by `[dict](/main/core/summaries/settings.html#dict)` inside `[oauth2_local_validation](/main/core/summaries/settings.html#oauth2_local_validation)` named filter. | | oauth2 `timeout_msecs`, `max_idle_time_msecs`, `max_parallel_connections`, `max_pipelined_requests`, `rawlog_dir` | Replaced by `http_client_*` settings inside `[oauth2](/main/core/summaries/settings.html#oauth2)` named filter. | | oauth2 `tls_*` | Replaced by `ssl_client_*` settings inside `[oauth2](/main/core/summaries/settings.html#oauth2)` named filter. | | oauth2 `debug` | Use `[log_debug](/main/core/summaries/settings.html#log_debug)` filter instead. Example: `log_debug=category=oauth2`. | | oauth `use_grant_password` | Replaced by [passdb oauth2](/main/core/config/auth/databases/oauth2.html). | | passdb/userdb `:protected` | Renamed to `:default`. | | `passdb { default_fields`, `override_fields` } | Replaced by `[passdb_fields](/main/core/summaries/settings.html#passdb_fields)`. | | `userdb { default_fields`, `override_fields` } | Replaced by `[userdb_fields](/main/core/summaries/settings.html#userdb_fields)`. | | `plugin` | All plugin settings are now global settings. | | `quota`, `quota_rule` | Split into separate [quota settings](/main/core/plugins/quota.html). | | `quota_grace` | Renamed to `[quota_storage_grace](/main/core/summaries/settings.html#quota_storage_grace)`. | | `quota_over_flag` | Renamed to `[quota_over_status_current](/main/core/summaries/settings.html#quota_over_status_current)`. | | `quota_over_flag_lazy_check` | Renamed to `[quota_over_status_lazy_check](/main/core/summaries/settings.html#quota_over_status_lazy_check)`. | | `quota_over_flag_value` | Renamed to `[quota_over_status_mask](/main/core/summaries/settings.html#quota_over_status_mask)`. | | `quota_over_script` | Replaced by `[quota_over_status](/main/core/summaries/settings.html#quota_over_status)` named filter with `[execute](/main/core/summaries/settings.html#execute)`. | | `quota_max_mail_size` | Renamed to `[quota_mail_size](/main/core/summaries/settings.html#quota_mail_size)`. The default value is set to `unlimited`. | | `sieve` | Replaced by [storage settings](/main/core/plugins/sieve.html#script-storage) inside `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter. | | `sieve_after` | Replaced by `[sieve_script_type = after](/main/core/summaries/settings.html#sieve_script_type)` for `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter. | | `sieve_default` | Replaced by `[sieve_script_type = default](/main/core/summaries/settings.html#sieve_script_type)` for `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter. | | `sieve_default_name` | Replaced by `[sieve_script_name](/main/core/summaries/settings.html#sieve_script_name)`. | | `sieve_discard` | Replaced by `[sieve_script_type = discard](/main/core/summaries/settings.html#sieve_script_type)` for `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter. | | `sieve_global` | Replaced by `[sieve_script_type = global](/main/core/summaries/settings.html#sieve_script_type)` for `[sieve_script](/main/core/summaries/settings.html#sieve_script)` filter. | | `sieve_quota_max_scripts` | Renamed to `[sieve_quota_script_count](/main/core/summaries/settings.html#sieve_quota_script_count)`. | | `sieve_quota_max_storage` | Renamed to `[sieve_quota_storage_size](/main/core/summaries/settings.html#sieve_quota_storage_size)`. | | `sieve_user_log` | Renamed to `[sieve_user_log_path](/main/core/summaries/settings.html#sieve_user_log_path)`. | | `sieve_editheader_forbid_add` | Renamed to `[sieve_editheader_header_forbid_add](/main/core/summaries/settings.html#sieve_editheader_header_forbid_add)`. | | `sieve_editheader_forbid_delete` | Renamed to `[sieve_editheader_header_forbid_delete](/main/core/summaries/settings.html#sieve_editheader_header_forbid_delete)`. | | `sieve_spamtest_max_header` | Renamed to `[sieve_spamtest_score_max_header](/main/core/summaries/settings.html#sieve_spamtest_score_max_header)`. | | `sieve_spamtest_max_value` | Renamed to `[sieve_spamtest_score_max_value](/main/core/summaries/settings.html#sieve_spamtest_score_max_value)`. | | `sieve_spamtest_text_value` | Changed `[sieve_spamtest_text_value](/main/core/summaries/settings.html#sieve_spamtest_text_value)` into string list. | | `sieve_vacation_dont_check_recipient` | replaced by `[sieve_vacation_check_recipient](/main/core/summaries/settings.html#sieve_vacation_check_recipient)`. | | `sieve_variables_max_scope_size` | Renamed to `[sieve_variables_max_scope_count](/main/core/summaries/settings.html#sieve_variables_max_scope_count)`. | | `sieve_variables_max_variable_size` | Renamed to `[sieve_variables_max_value_size](/main/core/summaries/settings.html#sieve_variables_max_value_size)`. | | `sieve_virustest_max_header` | Renamed to `[sieve_virustest_score_max_header](/main/core/summaries/settings.html#sieve_virustest_score_max_header)`. | | `sieve_virustest_max_value` | Renamed to `[sieve_virustest_score_max_value](/main/core/summaries/settings.html#sieve_virustest_score_max_value)`. | | `sieve_virustest_text_value` | Changed `[sieve_virustest_text_value](/main/core/summaries/settings.html#sieve_virustest_text_value)` into string list. | | `imapsieve_mailbox*` | See [imap-sieve plugin](/main/core/plugins/imap_sieve.html). | | `ssl_alt_cert` | Renamed to `[ssl_server_alt_cert_file](/main/core/summaries/settings.html#ssl_server_alt_cert_file)`. | | `ssl_alt_key` | Renamed to `[ssl_server_alt_key_file](/main/core/summaries/settings.html#ssl_server_alt_key_file)`. | | `ssl_ca` | Split to `[ssl_server_ca_file](/main/core/summaries/settings.html#ssl_server_ca_file)` and `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)`. | | `ssl_ca_dir` | Renamed to `[ssl_client_ca_dir](/main/core/summaries/settings.html#ssl_client_ca_dir)`. | | `ssl_ca_file` | Renamed to `[ssl_client_ca_file](/main/core/summaries/settings.html#ssl_client_ca_file)`. | | `ssl_cert` | Renamed to `[ssl_server_cert_file](/main/core/summaries/settings.html#ssl_server_cert_file)`. | | `ssl_cert_username_field` | Renamed to `[ssl_server_cert_username_field](/main/core/summaries/settings.html#ssl_server_cert_username_field)`. | | `ssl_client_cert` | Renamed to `[ssl_client_cert_file](/main/core/summaries/settings.html#ssl_client_cert_file)`. | | `ssl_client_key` | Renamed to `[ssl_client_key_file](/main/core/summaries/settings.html#ssl_client_key_file)`. | | `ssl_dh` | Renamed to `[ssl_server_dh_file](/main/core/summaries/settings.html#ssl_server_dh_file)`. | | `ssl_key` | Renamed to `[ssl_server_key_file](/main/core/summaries/settings.html#ssl_server_key_file)`. | | `ssl_key_password` | Renamed to `[ssl_server_key_password](/main/core/summaries/settings.html#ssl_server_key_password)`. | | `ssl_prefer_server_ciphers` | Replaced by `[ssl_server_prefer_ciphers](/main/core/summaries/settings.html#ssl_server_prefer_ciphers)`. | | `ssl_require_crl` | Renamed to `[ssl_server_require_crl](/main/core/summaries/settings.html#ssl_server_require_crl)`. | | `ssl_verify_client_cert` | Renamed to `[ssl_server_request_client_cert](/main/core/summaries/settings.html#ssl_server_request_client_cert)`. | | `stats_http_rawlog_dir` | Replaced by `[http_client_rawlog_dir](/main/core/summaries/settings.html#http_client_rawlog_dir)` inside `[stats_server](/main/core/summaries/settings.html#stats_server)` named filter. | #### External Configuration Files v2.3 configured sql, ldap and some other features using `.conf.ext` external configuration files. These have been replaced by regular settings, which can be used in the same place where they used to be referred to before. For example: dovecot-2.3.confdovecot-2.3-sql.conf.extdovecot-new.conf ``` passdb { driver = mysql args = /etc/dovecot/dovecot-sql.conf.ext } ``` ``` connect = host=127.0.0.1 user=mysql_user pass=mysql_pass password_query = SELECT password FROM users WHERE user = '%u' ``` ``` # Use these mysql settings globally. These could be also inside passdb sql {} mysql 127.0.0.1 { user = mysql_user password = mysql_pass } sql_driver = mysql passdb sql { query = SELECT password FROM users WHERE user = '%{user}' } ``` - [SQL authentication database](/main/core/config/auth/databases/sql.html) - [LDAP authentication](/main/core/config/auth/databases/ldap.html) - [OAuth2 Authentication Database](/main/core/config/auth/databases/oauth2.html) #### Default Settings | Setting | Old Default | New Default | Notes | | --- | --- | --- | --- | | `[imapc_features](/main/core/summaries/settings.html#imapc_features)` | Features "delay-login", "search", "fetch-headers", "fetch-bodystructure", "fetch-size" by default. Enable "acl" and "modseq" by default, if the remote server supports it. | | | | `[mail_cache_max_headers_count](/main/core/summaries/settings.html#mail_cache_max_headers_count)` | unlimited | 100 | New feature, explicitly set to `0` for the old behavior. | | `[mail_cache_max_header_name_length](/main/core/summaries/settings.html#mail_cache_max_header_name_length)` | unlimited | 100 | New feature, explicitly set to `0` for the old behavior. | | `[mail_log_prefix](/main/core/summaries/settings.html#mail_log_prefix)` | `%s(%u)<%{pid}><%{session}>:` | `%{service}(%{user})<%{process:pid}><%{session}>:` | New variable expansion syntax. | | `[mailbox_list_drop_noselect](/main/core/summaries/settings.html#mailbox_list_drop_noselect)` | `no` | `yes` | `\NoSelect` folders are now dropped by default. | | `service/anvil/chroot` | empty | | Anvil is no longer chrooted. | | `service/anvil/user` | $default\_internal\_user | | Anvil runs as root. | | `service/auth-worker/process_limit` | 1 | 30 | | | `[protocols](/main/core/summaries/settings.html#protocols)` | `imap pop3 lmtp` | | No protocols are enabled by default. | #### fs-crypt [Mail Crypt Plugin: fs-crypt](/main/core/plugins/mail_crypt.html#fs-crypt) now requires encryption keys by default. #### Changed types for FTS header settings | Setting Name | Update | | --- | --- | | `[fts_header_excludes](/main/core/summaries/settings.html#fts_header_excludes)` | Changed to [Boolean List](/main/core/settings/types.html#boolean-list). | | `[fts_header_includes](/main/core/summaries/settings.html#fts_header_includes)` | Changed to [Boolean List](/main/core/settings/types.html#boolean-list). | ### Service defaults ##### LMTP Default LMTP proxy destination port is now `24`. ### Event Changes #### Core Events | Event | Change | | --- | --- | | `[fts_flatcurve_expunge](/main/core/summaries/events.html#fts_flatcurve_expunge)` | This event was added. | | `[fts_flatcurve_index](/main/core/summaries/events.html#fts_flatcurve_index)` | This event was added. | | `[fts_flatcurve_last_uid](/main/core/summaries/events.html#fts_flatcurve_last_uid)` | This event was added. | | `[fts_flatcurve_optimize](/main/core/summaries/events.html#fts_flatcurve_optimize)` | This event was added. | | `[fts_flatcurve_query](/main/core/summaries/events.html#fts_flatcurve_query)` | This event was added. | | `[fts_flatcurve_rescan](/main/core/summaries/events.html#fts_flatcurve_rescan)` | This event was added. | | `[fts_flatcurve_rotate](/main/core/summaries/events.html#fts_flatcurve_rotate)` | This event was added. | | `auth_client_cache_flush_started` | Event was removed. | | `auth_client_cache_flush_finished` | Event was removed. | | `[imap_id_received](/main/core/summaries/events.html#imap_id_received)` | Event was added. | | `[login_aborted](/main/core/summaries/events.html#login_aborted)` | Event was added. | | `[mail_metadata_accessed](/main/core/summaries/events.html#mail_metadata_accessed)` | Event was added. | | `[pop3_command_finished](/main/core/summaries/events.html#pop3_command_finished)` | Event was added. | #### Event Fields | Event | Field | Change | | --- | --- | --- | | `[dns_worker_request_finished](/main/core/summaries/events.html#dns_worker_request_finished)` | `cached` | Field was added. | | Mail user events | `service` | Field was added. | | `[proxy_session_finished](/main/core/summaries/events.html#proxy_session_finished)` | `error_code` | Field was added. | | `[proxy_session_finished](/main/core/summaries/events.html#proxy_session_finished)` | `idle_usecs` | Field was changed from `idle_secs`. | | `[smtp_server_transaction_rcpt_finished](/main/core/summaries/events.html#smtp_server_transaction_rcpt_finished)` | `dest_host` | Field was added. | | `[smtp_server_transaction_rcpt_finished](/main/core/summaries/events.html#smtp_server_transaction_rcpt_finished)` | `dest_ip` | Field was added. | | `[sql_query_finished](/main/core/summaries/events.html#sql_query_finished)` | `consistency` | Field was added. | | `[sql_query_finished](/main/core/summaries/events.html#sql_query_finished)` | `error_consistency` | Field was added. | | Various | `net_bytes_in` | Field was changed from `bytes_in`. | | Various | `net_bytes_out` | Field was changed from `bytes_out`. | | Various | `transport` | `transport=trusted` was changed to `transport=secured`. See also [secured connections](/main/core/config/ssl.html#secured-connections). | #### Event Filters Size units are allowed when specifying event filter values. For example, `100kb` is accepted as equivalent to `102400`. Interval units are allowed when specifying event filter values. For example, `1min` is accepted as equivalent to `60000000`. Event filters support escaping wildcard `*` and `?` characters by prefixing them with `\`. #### Exports Events can now be exported to a local file or a unix socket. See [Event Export: Drivers](/main/core/config/events/export.html#drivers). ### ACLs #### Use ACL settings instead of Global ACL Directories or Global ACL File With the following starting configuration: dovecot.conf/etc/dovecot/acls/INBOX/etc/dovecot/acls/INBOX/foo/.DEFAULT/etc/dovecot/acls/INBOX/foo/bar doveconf ``` namespace inbox { prefix = INBOX/ separator = / } plugin { acl = vfile:/etc/dovecot/acls/ } ``` ``` owner lrwstipekxa anyone lr user=kim l ``` ``` user=timo lr user=kim lrw ``` ``` user=kim lrw ``` Or the same ACLs in a global ACL file: ``` INBOX owner lrwstipekxa INBOX anyone lr INBOX user=kim l INBOX/foo user=timo lr INBOX/foo user=kim lrw INBOX/foo/bar user=kim lrw ``` You have to create the new ACLs to `dovecot.conf`: doveconf ``` namespace inbox { # previously from /etc/dovecot/acls/INBOX mailbox INBOX { acl owner { rights = lrwstipekxa } acl anyone { rights = lr } acl user=kim { rights = l } } # previously from /etc/dovecot/acls/foo/.DEFAULT mailbox INBOX/foo { acl user=timo { rights = lr } acl user=kim { rights = lrw } } # previously from /etc/dovecot/acls/foo/bar mailbox INBOX/foo/bar { acl user=kim { rights = lrw } } ``` Note that at this point you could simplify specific rules, e.g. use mailbox name wildcards to replace lines for a specific user: doveconf ``` mailbox INBOX/* { acl user=kim { rights = lrw } } ``` And re-configure the ACL plugin: doveconf ``` acl_driver = vfile acl_globals_only = yes ``` Afterwards you can remove the old global ACL directory parent: sh ``` rm -rf /etc/dovecot/acls/ ``` ### Lua HTTP API Some settings were renamed. See [lib-lua: HTTP Functions](/main/core/admin/lua/lib_lua.html#http-functions). The `debug` setting was removed - use `[log_debug](/main/core/summaries/settings.html#log_debug)` instead to enable debugging. ### Lua Authentication Lua passdb/userdb now passes all args key/values to an initialization function. See [Lua authentication initialization](/main/core/config/auth/databases/lua.html#initialization). [Variable expansion](/main/installation/upgrade/2.3-to-2.4.html#variable-expansion) changes affect also `auth_request` fields. For example `auth_request.service` is now `auth_request.protocol`. ### Doveadm #### doveadm batch The `doveadm batch` command was removed. #### doveadm fs `[doveadm fs put]()` can now put metadata also. #### doveadm indexer Added `[doveadm indexer](/main/core/summaries/doveadm.html#indexer)` command. #### doveadm save Added `-r received-date` parameter. See `[doveadm save](/main/core/summaries/doveadm.html#save)`. #### dsync The `dsync` command symlink was removed. Use `[doveadm sync](/main/core/summaries/doveadm.html#sync)` or `[doveadm backup](/main/core/summaries/doveadm.html#backup)` commands directly instead. #### Mailbox Commands `USER` environment variable can be used only with `--no-userdb-lookup` parameter. All mail commands require providing `-u`, `-F`, `-A` parameter or `--no-userdb-lookup` parameter. This will always be subject to user database lookup and requires access to auth userdb socket, unless `--no-userdb-lookup` was used. ### LDA `HOME` environment is no longer used. (It was previously used when `-d` parameter was not given.) You can now use it by explicitly giving `-o mail_home=$HOME` parameter. ### Exim Authentication Dovecot authentication protocol changed slightly, which is now causing Exim's Dovecot authentication to hang. Use the `auth-legacy` listener type to work around it until Exim supports the updated protocol: dovecot.confexim.conf doveconf ``` service auth { unix_listener auth-exim { type = auth-legacy } } ``` ``` dovecot_plain: server_socket = /var/run/dovecot/auth-exim ... ``` ### Doveadm HTTP API #### Boolean Request Values The doveadm HTTP API now requires valid boolean values. Providing invalid boolean values will result in a 400 response. ## Upgrade Tips ### Mailbox Location WARNING These are examples of some common conversions. You MUST adjust the specific examples to your own environment. diff ``` -mail_home = /home/vmail/%u +mail_home = /home/vmail/%{user} ``` diff ``` -mail_home = /home/vmail/%d/%u +mail_home = /home/vmail/%{user|domain}/%{user|username} # or some variant of this. ``` diff ``` -mail_location = maildir:~/ +mail_path = ~/ +mail_driver = maildir ``` diff ``` -mail_location = maildir:/var/srv/foo/%d/%u +mail_path = /var/srv/foo/%{user|domain}/%{user|username} +mail_driver = maildir ``` In addition you might want to convert: diff ``` -INDEX=~/.index +mail_index_path=~/.index ``` diff ``` -CONTROL=~/.control +mail_control_path=~/.control ``` diff ``` -LAYOUT=fs +mailbox_list_layout=fs ``` A minimal working config is: doveconf ``` mail_home=/home/vmail/%{user} mail_driver=maildir mail_path=~/maildir mail_uid=vmail mail_gid=vmail ``` #### Namespace diff ``` namespace other { - location = maildir:/foo/bar + mail_path = /foo/bar + # optional, if this is matches your top level + mail_driver = maildir } ``` # Upgrading Dovecot CE from 2.4 to 2.4.x ## Dovecot CE ### v2.4.0 to v2.4.1 - Added `MALLOC_MMAP_THRESHOLD_=131072` to `[import_environment](/main/core/summaries/settings.html#import_environment)` when building with glibc. Note that `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` value does not affect change - it's always added. ### v2.4.2 to v2.4.3 - Using [Settings Variables](/main/core/settings/variables.html) with LDAP settings now requires using `| safe` filter to allow passing through LDAP characters that are otherwise escaped. The `safe` filter must always be the last filter in the pipeline. Note that this should be used only for variables coming from trusted sources, not for e.g. username variable. #### Changed Setting Defaults These changes don't take effect until `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` is changed from `2.4.0` to `2.4.1`. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `[mail_cache_fields](/main/core/summaries/settings.html#mail_cache_fields)` | flags | flags hdr.date hdr.subject hdr.from hdr.sender hdr.reply-to hdr.to hdr.cc hdr.bcc hdr.in-reply-to hdr.message-id date.received size.virtual imap.bodystructure mime.parts hdr.references hdr.importance hdr.x-priority hdr.x-open-xchange-share-url pop3.uidl pop3.order | | `service/auth/unix_listener/auth-userdb/group` | (empty = root) | `$SET:default_internal_group` | | `service/lmtp/restart_request_count` | unlimited | 1 | | `[lmtp_user_concurrency_limit](/main/core/summaries/settings.html#lmtp_user_concurrency_limit)` | unlimited | 10 | ### v2.4.1 to v2.4.2 #### Changed Setting Defaults These changes don't take effect until `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` is changed to `2.4.2`. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `@metric_defaults/proxy/metric` | | Adds proxy\_session\_established | | `service/anvil/unix_listener/anvil/mode` | `0600` | `0660` | | `service/anvil/unix_listener/anvil/group` | (empty = root) | `$SET:default_internal_group` | | `[mailbox_directory_name_legacy](/main/core/summaries/settings.html#mailbox_directory_name_legacy)` | `yes` | `no` | | `[mail_attachment_detection_options](/main/core/summaries/settings.html#mail_attachment_detection_options)` | | `add-flags content-type=!application/signature` | ### v2.4.2 to v2.4.3 #### Changed Setting Defaults These changes don't take effect until `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` is changed to `2.4.3`. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `[fts_message_max_size](/main/core/summaries/settings.html#fts_message_max_size)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[last_valid_gid](/main/core/summaries/settings.html#last_valid_gid)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[last_valid_uid](/main/core/summaries/settings.html#last_valid_uid)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[lazy_expunge_only_last_instance](/main/core/summaries/settings.html#lazy_expunge_only_last_instance)` | `no` | `yes` | | `[mail_access_groups](/main/core/summaries/settings.html#mail_access_groups)` | | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | `[mail_cache_max_header_name_length](/main/core/summaries/settings.html#mail_cache_max_header_name_length)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[mail_cache_max_headers_count](/main/core/summaries/settings.html#mail_cache_max_headers_count)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[mail_sort_max_read_count](/main/core/summaries/settings.html#mail_sort_max_read_count)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[mail_vsize_bg_after_count](/main/core/summaries/settings.html#mail_vsize_bg_after_count)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[passdb_default_password_scheme](/main/core/summaries/settings.html#passdb_default_password_scheme)` | `PLAIN` | `CRYPT` | | `[sieve_quota_script_count](/main/core/summaries/settings.html#sieve_quota_script_count)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[sieve_quota_storage_size](/main/core/summaries/settings.html#sieve_quota_storage_size)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `[submission_max_recipients](/main/core/summaries/settings.html#submission_max_recipients)` | | 0 is not allowed anymore to mean unlimited use `unlimited` instead | | `maildir/mail_attachment_detection_options` | `add-flags content-type=!application/signature` | | | `passdb_passwd_file/passdb_default_password_scheme` | `PLAIN` | `CRYPT` | | `passdb_sql/passdb_default_password_scheme` | `PLAIN` | `CRYPT` | | `service/doveadm/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/imap/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/imap-urlauth-worker/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/indexer-worker/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/lmtp/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/pop3/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/submission/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `service/managesieve/service_extra_groups` | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | | `@mailbox_defaults/english/namespace/inbox/mailbox/Drafts/mailbox_auto` | `no` | `subscribe` | | `@mailbox_defaults/english/namespace/inbox/mailbox/Junk/mailbox_auto` | `no` | `subscribe` | | `@mailbox_defaults/english/namespace/inbox/mailbox/Sent/mailbox_auto` | `no` | `subscribe` | | `@mailbox_defaults/english/namespace/inbox/mailbox/Trash/mailbox_auto` | `no` | `subscribe` | | `service/anvil/unix_listener/anvil-auth-penalty/mode` | `0600` | `0660` | | `service/anvil/unix_listener/anvil-auth-penalty/group` | | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | | `service/imap/unix_listener/imap-master/user` | | `[default_internal_user](/main/core/summaries/settings.html#default_internal_user)` | #### Removed Features | Feature | Notes | | --- | --- | | `[acl_global_path](/main/core/summaries/settings.html#acl_global_path)` setting | See [ACL Settings Migration](/main/installation/upgrade/2.3-to-2.4.html#use-acl-settings-instead-of-global-acl-directories-or-global-acl-file). | ### v2.4.3 to v2.4.4 #### Forcibly Changed Setting Defaults Unlike other default setting changes, this change takes effect regardless of `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)`. This is because `indexer-worker` now drops root privileges, and the old default could make the service non-working. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `service/indexer-worker/service_restart_request_count` | `[service_restart_request_count](/main/core/summaries/settings.html#service_restart_request_count)` | `1` | #### Changed Setting Defaults These changes don't take effect until `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` is changed to `2.4.4`. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `push_notification_ox/http_client_request_max_attempts` | `1` | `2` | | `push_notification_ox/http_client_request_timeout` | `1min` | `2s` | | `service/lmtp/service_extra_groups` | | `[default_internal_group](/main/core/summaries/settings.html#default_internal_group)` | ### v2.4.4 to v2.4.5 - [String List](/main/core/settings/types.html#string-list) and [Boolean List](/main/core/settings/types.html#boolean-list) setting keys are now expanded with `[Settings variables](/main/core/settings/variables.html)` the same way their values already were. Keys defined in the configuration are always expanded. For settings overrides only default overrides expand the keys; keys coming from `-o` command line parameters and from userdb are kept literal. - The per-field time and date [Settings Variables](/main/core/settings/variables.html) providers are now deprecated: `time:hour`, `time:min`, `time:minute`, `time:sec`, `time:second`, `time:usec` and `date:year`, `date:month`, `date:day`. Use the new `time:unix` provider together with the `date` filter instead. Note that the old providers always used the local timezone, whereas the `date` filter defaults to UTC, so to keep the old behavior pass `tz='local'`: use `%{time:unix | date('%H', 'local')}` in place of `%{time:hour}`. The new approach additionally supports UTC, timestamps from any other variable, and ISO 8601 / RFC 3339 output via the `iso8601` filter. The deprecated providers keep working for now. #### Changed Setting Defaults These changes don't take effect until `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` is changed to `2.4.5`. | Setting | Old Default Value | New Default Value | | --- | --- | --- | | `[sieve_max_cpu_time](/main/core/summaries/settings.html#sieve_max_cpu_time)` | `0` (unlimited) | `30s` | | `[imap_compress_on_proxy](/main/core/summaries/settings.html#imap_compress_on_proxy)` | `no` | `yes` | #### New Features | Feature | Notes | | --- | --- | | [\`escape\` filter](/main/core/settings/variables.html) | Explicitly apply the configured escape function within a variable expansion pipeline. Useful for partial escaping when combined with `concat` and `safe`. | | [\`time:unix\` provider](/main/core/settings/variables.html) | Returns the current time as a `.` UNIX timestamp. Recommended replacement for the deprecated per-field `time:`/`date:` providers. | | [\`epoch\`](/main/core/settings/variables.html) | Convert and format UNIX timestamps (unit scaling, calendar formatting via `strftime`, and ISO 8601 / RFC 3339 output). | #### Removed Features | Feature | Notes | | --- | --- | | `auth`: `SIGHUP`, `SIGUSR2` | Use `[doveadm auth cache flush]()` to flush the cache and `[doveadm auth cache status]()` to inspect cache statistics instead. | ### v2.4.5 to v2.4.6 - A [passdb](/main/core/config/auth/passdb.html) lookup returning an empty password now fails authentication instead of allowing login with an empty password. Passdb entries intended to have no fixed password should return the `nopassword`[extra field](/main/core/config/auth/passdb.html#extra-fields) (e.g. `user:::::::nopassword=yes` in a passwd-file) instead of an empty password field; any non-empty client password is then accepted. - An empty client-supplied password is now always rejected before the passdb lookup, for all passdb drivers. Previously only LDAP auth binds rejected it. `nopassword` does not allow an empty client password either; only mechanisms that authenticate without a password (EXTERNAL via client certificate, OAuth2) are unaffected. - The EXTERNAL SASL mechanism now treats the client certificate as the authentication and no longer verifies any passdb password. Existing client-certificate setups (e.g. passwd-file entries with an empty password field) keep working. Passdbs that verify the password themselves no longer reject EXTERNAL logins either: e.g. an LDAP auth bind passdb attempts no bind - the lookup succeeds without verifying anything and only provides the extra fields, since the certificate already authenticated the user. For EXTERNAL logins`passdb { skip = authenticated }` passdbs are now skipped and `skip = unauthenticated` passdbs run; if all passdbs are skipped, the login fails. # Upgrading Dovecot The [NEWS](https://github.com/dovecot/core/blob/main/NEWS) file, contained in the top-level of the source, contains all the important changes between releases. These changes are marked with `*` character. Read them to see if there is anything that concerns you. In general all the changes try to preserve backwards compatibility, but some changes which are meant to improve the stability and correctness of the configuration could mean breaking some existing installations. That may be a good thing, since it can expose problems which could otherwise show up as random errors. ## File Formats Since v2.4.0 release, upgrading won't silently change configuration (defaults). Either the old configuration is preserved, or startup fails asking you to upgrade the configuration file. The defaults are changed only when `[dovecot_config_version](/main/core/summaries/settings.html#dovecot_config_version)` setting is changed. Similarly since v2.4.0 release, upgrading won't do any backwards incompatible changes to storage files. If the release no longer supports an old storage file format, the startup fails instead. The new file formats are used only when `[dovecot_storage_version](/main/core/summaries/settings.html#dovecot_storage_version)` setting is changed. ## Dovecot CE 2.3 and later - [Upgrading Dovecot CE from 2.2 to 2.3](/main/installation/upgrade/2.2-to-2.3.html) - [Upgrading Dovecot CE from 2.3 to 2.3.x](/main/installation/upgrade/2.3-to-2.3.x.html) - [Upgrading Dovecot CE from 2.3 to 2.4](/main/installation/upgrade/2.3-to-2.4.html) ## Dovecot CE 2.2 and earlier See [https://doc.dovecot.org/installation\_guide/upgrading/](https://doc.dovecot.org/installation_guide/upgrading/). # Issue Reporting ## Security Issues Report security-related issues to: - [Dovecot's YesWeHack Program](https://vdp.open-xchange.com/) - [security@dovecot.org](mailto:security@dovecot.org) ## Bugs Bugs should be reported to the [Dovecot mailing list](https://www.dovecot.org/mailing-lists/). ### Report Details #### Technical Details Recommended You can provide most of the system related information with [`dovecot-sysreport`](https://raw.githubusercontent.com/dovecot/core/master/src/util/dovecot-sysreport) command. Provide Dovecot configuration produced by `dovecot -n`. If the output does doesn't already show it, specify also: - Dovecot version - Operating system or Linux distribution name - CPU architecture (x86 or something else?) - Filesystem you used (especially if you use NFS or not) TIP Don’t send the whole `dovecot.conf` file, it’s way too large especially if you don’t remove the comments. #### Problem Description Some kind of description of what you were doing and with what mail (e.g., IMAP) client. #### Reproducible Testcase If the problem can be reproduced, it helps a lot if you can tell how. If it happens only with a specific mail, add it as attachment if possible. WARNING Don't send sensitive details in your report. You can hide all the actual mail data from messages using [mbox-anonymize](https://dovecot.org/tools/mbox-anonymize.pl). ### Debugging Crashes See [Core Dumps](/main/developers/debugging.html#core-dumps). ### Debugging Hangs See [Debugging Hangs](/main/developers/debugging.html#hangs). ### Debugging Clients See [Debugging Clients](/main/developers/debugging.html#client-traffic-sniffing). # License ![Creative Commons License](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFgAAAAfCAMAAABUFvrSAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjnz7UZMAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAAf5QTFRFAAAAAAAA////////////8fHx7+/v6Ofn4+Pj4N/g39/f1tXV09bS0tXS0tXR0dTR0dTQ0NTQ0NPPz9PPztLOztHNzdHNzdHMz8/PzdDMzNDMzNDLzM/Ly8/Ly8/Ky87Kys3Jyc3Jyc3Iy8rLyMzIyMzHx8vHxsrGycjIxsrFxcnFyMfHxcnExMnExMjDw8jDxMfDw8fCwsfCwcXAwMXAwMW/wMS/v8S+v8O+vsO+vsK9vcK9vcK8v7+/vMG8vMG7vMC8u8C7u8C6ur+6ur+5ub65ub64uL23t7y2urm5tru1tbq0tLqztLmzs7iysrixtbW1srexsbewsLavsLWvr7Wur7SusLOvrrStrrOtr7KvrbOsrLKrr6+vq7GqrKurpqqmo6ijoqaho6Ghn6OenqCdn5+fnp2dn5aampiZlpmWlZmUmJaXk5iTkZSRkZORkY+Pj4+PiYyJjIqLjoeLh4aHhIaEhIWEgoWChIGCf4F+gICAfX98fH98fnt8en15eXx5eHV2dnN0dXJzcHJvcHBwbmxsaGVmY19hYGBgXV5dWldYUFFQUFBQQ0RDQEBAPj8+Pzs8Pzc5NTY1MjMxMjExMDAwMS0uLS0tKioqKSopKSkpKCkoKCgoKicnKCUmJCQkIx8gICAgHxscGxsbGRkZEBAQDg4ODQ4NDQwNNUYWmgAAAAR0Uk5T/wAKDnDBpeYAAAPASURBVHjatZaNf1JVHMaf6kAEbF6vsgyQ6ygsmjKJptNG2NoY23Lm1mq2DSp1682xrLSF1qKocAO0F1ZCL4cm4n/Zb4fdl27zM8XPHuBzPvfLPQ+H53fP7148hh3RIwBGE/HY8Uh3MNCpeN1ur+9AIBiK9MYGE6Nj429MTU/PJB9cEL4DfUfDQb/PJUttTmfbLtm13x8M9/QNbDhPTk3PtOhMvi+GDiou6UqxAVKjeFna6wscjrxEzqcnJs9OzyxXQKos6/O2JQDi5BvwyOl1aFpfkN3+Q5G+wcSpMxNTH1WBUi5XAqoXk0IXtycAYkdDAbe0CqCez0SjmXwdwOqRJ/2He2LxkbHx9+6gYGckewF3xKzz90EAHA8f9JAv6hkra2qOrKtH3IFwb//Q6JkK8sSAjS9QTZKqgiyumIitq8umEQCRoCKTb9nONFnLwKrsC0b64iNXUXCoxqyAZUoTv35AvrfvZm0aoXPOcdKwIE3j7mdcafIVyzU6L+z1h471J27BrnM7KslkBX/xmz8g8zlSGrGzLv7Tj1nOFwURxkGftI76PvYfWetYl/YHIy/HUWKkPJBnpBJoTunbbzjnWZtDJ8zyPv+MsXm+JogwDnRcATLMpDngsisQPjGAHCOBxEg50BwiXVkAWYdOLH/zPyhmzh8n0jTu3FNEnYKIFkqlTHP4hCbUUZQPUBZbG5NHdo3zlWGNvP5bVBg/oRkrUgN5MgRIpVkx0HEejV2+Qz2v/D8KQSzzzMFJN1UyfO45xlKc61F42wFaYhk1e7Q2V0bZPlubE1m0eYOR2O/N4gGG4hFZ4WtryKa++kcl85wvLf3JP9aL53ECUcZEztog/oHT/Wwk9iUKzHy5EbEt3b67aDGSJU763iLI/RgP3dI3SM64QT5dMZGnUylHk5ijsO6rZSiKjUGPYuidlra0XrzNqmnFy6EhKVS8odferQDlfK6st5zz1W2JuNxWUWfa5TZbEgOroSh3ho69mjg1/ubXLbXNQEeasth6g7xwYnBkbIJ6fSuN/nmxpe332tKjp1s1piZ0YasmlHb5u3v7qdVPnp1pyRjXFPk6ORvakF20TeULPKS+8+42Nnprpg78LHmu4aF11b37uunWJD31IVQ19wfER0g9IG46iZmdvfIF4800LXt0X7DNN9NJ89j0UwKY01BcUlq9/S9ILsWYg9o2DWTzZZDaTmDSL2/7OvZI7U5nu0QPLG/dgEF6EEbCzAtUgVk3Lp1UvB6PVzl5iWyxzYpVc3MdGHZcj+7Q0+a/KrvgUC2hl58AAAAASUVORK5CYII=) This documentation is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/). Dovecot CE code is distributed under a variety of licenses. See [COPYING](https://github.com/dovecot/core/blob/main/COPYING). # Debugging TIP For reporting issues with Dovecot CE, see [https://dovecot.org/bugreport-mail](https://dovecot.org/bugreport-mail). ## Crashes Dovecot has been designed to crash rather than continue in a potentially unsafe manner that could cause data loss. Most crashes usually happen just once and retrying the operation will succeed, so usually even if you see them it's not a big problem. Of course, all crashes are bugs that should eventually be fixed, so feel free to report them always even if they're not causing any visible problems. Crashes appear in the logs like: ``` dovecot: Apr 23 11:16:05 Error: child 86116 (imap) killed with signal 11 ``` Instead of crashing, there have been some rare bugs in Dovecot when some process could go into infinite loop, which causes the process to use 100% CPU. If you detect such processes, it would be helpful to get a gdb backtrace of the running process: sh ``` gdb -p pid-of-process bt full ``` After getting the backtrace, you can `kill -9` the process. ### Reporting Reporting crashes is usually best accompanied with a gdb backtrace as described in [https://www.dovecot.org/bugreport.html](https://www.dovecot.org/bugreport.html). See [core dumps](#core-dumps) for further information. ## Process Tracing If a Dovecot process hangs or is just really slow, the best way to debug it is to see what it's really doing. ### Linux sh ``` strace -tt -o log -p ``` ### BSDs, OS X <= 10.4 sh ``` # enable process tracing ktrace -f log -p # do whatever makes it break, then stop the process tracing: ktrace -C # and see what it's done: kdump -T -f log ``` ### OS X >= 10.5 sh ``` dtruss -p ``` ### Solaris sh ``` truss -d -r0 -w1 -o log -p ``` `-r0` and `-w1` cause all IMAP input/output to be logged. `-d` adds timestamps to the log. ## Core Dumps ### dovecot-sysreport TIP This is the recommended way of reporting core dumps to the Dovecot developers. WARNING Before posting the output of the script publicly, make sure the exported configuration doesn't have anything sensitive in it. Use the [dovecot-sysreport](https://raw.githubusercontent.com/dovecot/core/master/src/util/dovecot-sysreport), which can also be found in the Dovecot packages: sh ``` dovecot-sysreport --core ``` #### Debugging Core Dumps in Other Systems If you have a tar.gz generated from dovecot-sysreport, you can debug it in any Linux distribution. But you still need to have the Dovecot debuginfo packages installed globally, which could be a bit tricky. You need the core dump, the binary that produced it and ALL the shared libraries on the system. For example: sh ``` binary=/usr/libexec/dovecot/imap core=/var/core/core.12345 dest=core.tar.gz (echo "info shared"; sleep 1) | gdb $binary $core | grep '^0x.*/' | sed 's,^[^/]*,,' | xargs tar czf $dest --dereference $binary $core ``` ### core-tar.sh [https://dovecot.org/tools/core-tar.sh](https://dovecot.org/tools/core-tar.sh) Usage: `./core-tar.sh ` Debugging on the test server then ideally would have all the debuginfo packages (for exactly the same binaries). You can run gdb there with: sh ``` mkdir coretest cd coretest tar xzf ../core.tar.gz gdb imap set solib-absolute-prefix . core imap.core bt full ``` ## Session IDs Each IMAP, POP3, and LMTP connection has its own unique session ID. This ID is logged in all the lines and passed between Dovecot services, which allows tracking it all the way through proxies to backends and their various processes. The session IDs look like ``. ## Mail Debugging Setting `[log_debug](/main/core/summaries/settings.html#log_debug)` will make Dovecot log all kinds of things about mailbox initialization. WARNING This setting won't increase error logging at all, so if you're having some random problems it's unlikely to provide any help. If there are any problems with a mailbox, Dovecot should automatically fix it. If that doesn't work for any reason, you can manually also request fixing a mailbox by running `[doveadm force-resync -u user@domain INBOX](/main/core/summaries/doveadm.html#force-resync)`, where `INBOX` should be replaced with the folder that is having problems (or `*` if all folders should be fixed). Users may sometimes complain that they have lost emails. The problem is almost always that this was done by one of the user's email clients accidentally. Especially accidentally configuring a "POP3 client" to a new device that deletes the mails after downloading them. For this reason it's very useful to enable the [mail-log plugin](/main/core/plugins/mail_log.html) and enable logging for all the events that may cause mails to be lost. This way it's always possible to find out from the logs what exactly caused messages to be deleted. If you're familiar enough with Dovecot's index files, you can use `[doveadm dump](/main/core/summaries/doveadm.html#dump)` command to look at their contents in human readable format and possibly determine if there is something wrong in them. ## Rawlogs See [rawlog](/main/core/admin/rawlog.html). ## Authentication Debugging See [Debugging Authentication](/main/core/config/auth/overview.html#debugging). ## Developer Debugging For detailed debugging of Dovecot issues, see [Developer Debugging](/main/developers/debugging.html). ## Docker Debugging You can run a local, temporary instance of Dovecot to test and debug. See [Docker Container Debugging/Testing](/main/installation/docker.html#running-as-debug-testing-install).