Skip to content

Mail Providers

Dejan Strbac edited this page Nov 12, 2026 · 1 revision

ALPS reads and stores mail through a provider, chosen with [provider] type. This page covers the three providers built into ALPS (imap, maildir and multi): when to use each, how multi picks a backend for each login, and how a multi backend names its own SMTP, CardDAV, CalDAV, ManageSieve and password-change settings. Every key is listed in Config-Providers-and-SMTP and in CONFIGURATION.md.

Choosing a provider

Provider Use it when Mail is read from Each account's ALPS settings are kept in
imap (the default) One IMAP server holds every account. That server, over IMAP. The server's IMAP METADATA (Storage-in-IMAP-METADATA).
maildir ALPS runs on the host that holds the mail, and you do not run an IMAP server. Maildir directories on the local disk. alps_store.json at the root of each user's maildir.
multi Accounts of different domains live on different servers, or users sign in with a bare username and should still send from an address. A backend chosen for each login, of type imap or maildir. Wherever that backend keeps them.

These rules hold for all three:

  • [provider] type takes "imap", "maildir" or "multi", and defaults to "imap". The section for that type ([provider.imap], [provider.maildir] or [provider.multi]) is required. Without it ALPS stops at startup with missing TOML section [provider.<type>], and any other type stops startup too.
  • [smtp] server is required whatever the provider, even when every multi backend names its own smtp.
  • ALPS passes the login and password to the backend exactly as the user typed them. The imap provider signs in with them, and the maildir provider looks the login up in its passwd file.
  • The provider is set up once, at startup. A change to it takes a restart.

The imap provider

[provider]
type = "imap"

[provider.imap]
server = "imaps://imap.example.com:993"

[smtp]
server = "smtps://smtp.example.com:465"
  • server is required and must carry a scheme: imaps:// (implicit TLS, port 993 by default), imap:// (STARTTLS required, port 143 by default) or imap+insecure:// (no TLS). ALPS always verifies the server's certificate, so a server with a private CA needs that CA in the host's trust store.
  • insecure = true makes imap:// connect without TLS, like imap+insecure://. The password then crosses the network in clear text.
  • authserv_ids lists the authserv-ids of your receiving mail servers, for example ["mx.example.com"]. ALPS reads a message's sender verdict, which drives the sender badge and brand logo, from the Authentication-Results field those servers wrote. Left empty, ALPS reads the topmost such field, which a message your server did not stamp may carry from its sender. Set it whenever your mail server writes that field.
  • debug = true logs the IMAP exchange, message content included, through [logging] as protocol trace lines at debug level, so they appear only while the log level is debug. [server] debug does the same. Keep both off in production.
  • [provider] timeout_sec (30 seconds by default) bounds connecting to the server and the wait for each of its answers, LOGIN and every other command included. A fetched message, or a part of one, has a fixed 5 minutes to arrive instead. An answer that does not come in time closes the connection. For this provider only, [server] imap_timeout_sec is read in its place when timeout_sec is unset.

What the server has to offer (METADATA for settings to persist, UIDPLUS and MOVE, and the optional extensions) is on Mail-Server-Requirements. For a complete local mail stack, see Setup-Dovecot-and-Postfix.

The maildir provider

[provider]
type = "maildir"

[provider.maildir]
auth_passwd_file = "/etc/dovecot/users"
path = "/var/vmail/%d/%u/Maildir"

[smtp]
server = "smtps://smtp.example.com:465"
  • auth_passwd_file is required: a Dovecot-style passwd file. ALPS reads it, and never writes it, on every sign-in, so new users can sign in without a restart. The first field of a line is compared exactly, case included, with the login as typed. The password schemes ALPS accepts are listed in MAILDIR.md. A user whose line uses another scheme cannot sign in, and ALPS logs user "<login>" has unsupported password scheme "<scheme>" at each attempt.
  • path is each user's own maildir once %u (the local part, or the whole login when it has no @), %d (the domain, empty when the login has no @) and %n (the whole login) are replaced. Include %n, or %u together with %d, so that every account gets a directory of its own. Without path, each user's maildir is <home>/Maildir, where <home> is field 6 of the user's passwd line.
  • A user can sign in before the maildir exists, and save settings too: the first save creates the maildir directory when it is missing.
  • ALPS reads and writes the files as its own user and ignores the uid and gid in the passwd file, so run it as the user that owns the mail store.
  • Sending still goes through [smtp] server, which ALPS signs in to with the same login and password. The SMTP server must accept the credentials in auth_passwd_file.
  • [provider] timeout_sec does not apply to this provider.

Compared with imap, the maildir provider has no new-mail push (pages check at the user's interval instead), no storage quota, no folder sharing, no conversation threading from the server and no search across all folders. Its search reads the query as on an IMAP account, prefixes and quotes included, and ALPS does the matching itself, against the decoded headers and bodies and the flags in the file names. It shows no sender badges or brand logos, lists every folder as subscribed, and cannot store tags: a tag change is refused with HTTP 400 and changes nothing. Messages are sorted by their Date header when Settings > Reading & Composing > Message Sort Criteria is Received Date (the default), and by file name, which normally starts with the delivery time, under Folder Filing Date. Sent copies and drafts go only to folders named exactly Sent and Drafts (or Draft), in any case, which ALPS creates when they are missing. The full list is in MAILDIR.md.

Serve each maildir from one ALPS process: changes to alps_store.json are serialised only within a process. With [cluster] enabled = true, maildir accounts are not offered recovery codes for their security keys (WEBAUTHN.md).

For a walkthrough, see Setup-Maildir.

The multi provider

multi chooses a backend for each login when the user signs in. The session then works with that backend as if it were the only provider: an imap backend offers what its server supports, and a maildir backend has the limits listed above. The connections the session opens later, its background connection and reconnects included, go through the same choice. A linked account is routed by its own login.

How a login is routed

ALPS first takes the domain from the login: the part after the @, lowercased. A login without @ gets default_domain when one is set. A login with more than one @ is refused, unless its local part is quoted, as in "a@b"@example.com.

It then tries these rules in order, and the first one that matches chooses the backend:

  1. [provider.multi.domains."<domain>"], an entry for exactly that domain.
  2. [[provider.multi.routes]], the first route whose domains list contains the domain.
  3. template, when the domain is listed in template_domains.
  4. autodiscover, when autodiscover = true and the domain is listed in autodiscover_domains.
  5. [provider.multi.default], for every login no earlier rule matched, including a bare login when there is no default_domain.

Domain keys and route lists are lowercased when ALPS loads them, so domains = ["Example.COM"] matches a login at example.com. The backend still receives the login as typed: a maildir backend looks up jdoe@Example.COM in its passwd file as written, and fills %d with Example.COM. A bare login that default_domain routes to a maildir backend is looked up under the bare name, and its %d is empty.

What a sign-in ends in:

Situation HTTP answer The sign-in page shows Reason in the sign-in failed log line
A rule matched, and the backend refused the password 401 "Login failed. Please check your credentials." bad credentials, with the backend's answer in error
No rule matched, and there is no default 401 "Login failed. Please check your credentials." bad credentials, with no mail provider configured for domain "<domain>" in error
The login has more than one unquoted @ 401 "Login failed. Please check your credentials." bad credentials, with malformed username in error
template or autodiscover matched, but no usable server was found or reached 503, code service_unavailable "The service could not sign you in just now. Please try again in a moment." error, with the cause in the line's error field

Once template or autodiscover has matched a domain, a failure there ends the sign-in. ALPS does not go on to default, so a user is never signed in to a server other than the one their domain names.

The browser gets the same 401 for each of the first three, but the log tells them apart. When every user of one domain is told the password is wrong, look for no mail provider configured for domain in their sign-in failed lines, and add a rule that covers that domain.

Backends

Each domains entry, each routes entry and default describes one backend: its type ("imap", the default, or "maildir"), the keys of that provider (server and the other [provider.imap] keys, or auth_passwd_file and path), and optionally its own endpoints. A routes entry also lists its domains. [provider] timeout_sec applies to every IMAP backend, and [server] imap_timeout_sec is not read under multi. The keys, with a complete example of each kind of backend, are on Config-Providers-and-SMTP.

With that example, a user who signs in as jdoe gets default_domain = "example.com", is routed by the routes entry for example.com to mail.example.com, signs in there as jdoe, and sends from jdoe@example.com through that route's smtp. A user at a domain whose entry names no smtp sends through [smtp] server.

Template and autodiscovery

Both rules find an IMAP server for a domain that has no entry of its own:

  • template builds the server URL from placeholders: %d for the domain, as in imaps://mail.%d:993, and %u for the local part. %n, the whole login, is refused at startup, since a whole login cannot form a host name.
  • autodiscover = true looks the server up in the domain's RFC 6186 SRV records, _imaps._tcp and then _imap._tcp.

Both apply only to the domains on their allowlist, template_domains and autodiscover_domains. With the list empty the rule never matches, so always set the list together with the rule. Through these rules ALPS connects only to public addresses: name a mail server on a private network in a domains or routes entry instead. Both lists accept "*", which lets the domain a user types choose the server ALPS connects to, so use it only where the sign-in page is open to users you know. The servers found this way take their endpoints from default.

The placeholders, the checks on the login, how long lookups are cached and what ALPS logs when a lookup fails are on Config-Providers-and-SMTP.

Per-backend endpoints

A backend can name the other services its users reach, so that a user does not read mail from one host and then send, sync contacts or calendars, edit filters or change a password somewhere unrelated. The keys are smtp, carddav, caldav, managesieve and a password sub-table, each used in place of the global setting for that backend's users.

Where a login's endpoints come from:

The login was routed by Each endpoint comes from
a domains entry that entry, or else the global setting
a routes entry that route, or else the global setting
template, autodiscover or default default, or else the global setting

A domains or routes entry never borrows an endpoint from another backend. A domains entry without smtp sends through [smtp] server, even when default names an smtp.

ManageSieve signs in with the login and password the user signed in to ALPS with, and so do CalDAV and CardDAV unless the user saves a separate username and password in Settings, so those servers must accept the same credentials as the backend. With type = "multi", the caldav, carddav and managesieve plugins load without a global server when some backend names their endpoint, provided enabled lets them through (see Config-Plugins). caldav and carddav also load with a section of their own and no endpoint anywhere. A user whose backend names no CalDAV or CardDAV endpoint, with no global server either, gets only the plugin's section in Settings > Calendar or Settings > Contacts, where they can enter a server of their own; Calendar and Tasks, or Contacts, appear once they have one. A user whose backend names no ManageSieve endpoint, with no global server, sees Filters and Auto-reply, but they cannot load. For password change, with [plugin.password] enabled = true, a backend whose password block sets an endpoint or command is enough for the plugin to load without a global one.

The form each endpoint takes, the password sub-table and where it goes in TOML are on Config-Providers-and-SMTP. More on each service: MAIL_SERVER.md for SMTP, CDAV.md for contacts and calendars, FILTERS.md for filters, and Setup-Password-Change for password change.

Bare usernames on one server

multi also serves a single IMAP server whose users sign in with a bare username. With only default_domain and default, a user who signs in as jdoe signs in to the server as jdoe, and sends and organizes meetings as jdoe@example.com when default_domain = "example.com". With the imap or maildir provider, a bare login stays the From address. The password-change settings still receive the bare login (jdoe), as PASSWORD_CHANGE.md describes. The configuration is on Config-Providers-and-SMTP.

Checking a multi configuration

ALPS checks the multi section at startup. It stops in each of these cases, and where one backend is at fault, the message names its domain, the route by its index from 0, or the default backend:

  • the section has no rule at all, or a routes entry has no domains;
  • a backend has an unknown type, an imap backend has no usable server, or a maildir backend has no auth_passwd_file;
  • a key in a backend has a value of the wrong type, such as a password that is not a table;
  • a backend's smtp, caldav, carddav or managesieve is not a URL that service takes, for example multi: invalid service for domain "example.com": ...;
  • template or autodiscover is set with an empty allowlist (multi: template requires non-empty template_domains), or template holds %n or does not make an IMAP URL (multi: invalid template: ...).

ALPS also logs a warning for each key it does not know, such as a misspelled one (Unknown configuration key: ... (ignored)), but not for the keys inside a backend's password table, which are free-form.

After changing the multi section, sign in as one user of each backend, send a message, and open Contacts, Calendar and Filters. Then search the log for sign-in failed.

Common setups

Adding a provider

A provider registers itself from init() with provider.Register(name, factory), and [provider] type selects it by that name. Handlers reach mail only through the provider.MailProvider interface; optional interfaces switch on features such as push, quota and folder sharing. The API's JSON reuses the IMAP library's types, so a new backend has to return data that maps onto them. See ARCHITECTURE.md and the code: provider/imap/config.go, provider/maildir/config.go, provider/multi/config.go (routing and per-backend endpoints) and provider/multi/autodiscover.go.

Clone this wiki locally