Repository navigation
Configuration Overview
This page covers the ALPS configuration file as a whole: how ALPS is started with it, the smallest file that works, how defaults and startup checks behave, which sections exist and which page covers each, the environment variables ALPS reads, and a checklist for production. Every key, with its type and default, is listed in docs/CONFIGURATION.md.
ALPS reads one TOML file, named on the command line with -config. There is no default path, and no environment variable names the file: without -config, ALPS prints its usage and exits. A relative path is relative to the working directory of the process.
The repository ships a commented example, config.example.toml, which gives each key's default. The usual start is to copy it to config.toml and edit it. Three things in the example need attention before you use it:
- It points
[provider.imap] serverand[smtp] serverat Migadu's servers. Point them at your own. - It sets
enabled = truefor thecaldav,carddavandmanagesieveplugins and leaves theirserverlines commented out. CalDAV and CardDAV then load without a default server, so users get Calendar, Tasks and Contacts only once they enter servers of their own in Settings. ManageSieve stays off, and startup warns that it did not load. Set a server for each service you run, andenabled = falsefor the others (see Config-Plugins). - Its
[logging]section writes console text to stderr (output = "stderr",format = "console"). Setformat = "json"if your log collector reads JSON lines.
The shipped service files read /etc/alps/config.toml (systemd, dist/alps.service) and /usr/local/etc/alps/config.toml (FreeBSD rc.d, dist/alps). See Running-in-Production and docs/OPERATIONS.md.
ALPS reads the file once, at startup, so restart ALPS to apply a change. SIGHUP (systemctl reload alps, service alps reload) only reopens the log file when [logging] output is a file path. It does not read the configuration again, as the comments in both shipped service files say.
| Flag | Effect |
|---|---|
-config <path> |
The configuration file. Required. --config also works. |
-version |
Prints alps <version> (<commit>) built at <date> and exits with status 0. |
-h |
Prints the usage and exits with status 0. |
make build writes the version, commit and build date into the binary. A plain go build, go install or go run does not, and -version then prints alps unknown (unknown) built at unknown (see docs/OPERATIONS.md).
ALPS exits with status 1 when -config is missing, when the file cannot be read, parsed or validated, or when a startup step fails. An unknown flag exits with status 2. The full table is in docs/OPERATIONS.md.
ALPS needs a mail store and an SMTP submission server. [provider] type defaults to "imap", so this file is enough:
[provider.imap]
server = "imaps://imap.example.com:993"
[smtp]
server = "smtps://smtp.example.com:465"Both URLs need a scheme. For IMAP, use imaps:// (TLS, default port 993) or imap:// (STARTTLS required, default port 143). For SMTP, use smtps:// (TLS, default port 465) or smtp:// (STARTTLS required, default port 587). ALPS always verifies the IMAP and SMTP servers' certificates. What the mail server has to support, METADATA above all, is on Mail-Server-Requirements.
Start ALPS with the file:
alps -config config.toml
# or, from a checkout of the repository:
go run ./cmd/alps -config config.tomlWith this file, startup logs these lines (time fields left out):
{"level":"INFO","msg":"Configured SMTP server: smtps://smtp.example.com:465"}
{"level":"INFO","msg":"Rate limiting enabled: IP=5/min, 20/hour | User=5/15min, 10/hour | Global=100/sec"}
{"level":"INFO","msg":"Loaded plugin \"gpg\""}
{"level":"INFO","msg":"Loaded plugin \"base\""}
{"level":"WARN","msg":"WebAuthn: [webauthn] rpid is unset, defaulting to \"localhost\" — 2FA will not work unless served from localhost; set rpid and origins to your public hostname"}
{"level":"INFO","msg":"Starting server on :1323"}
Check that ALPS answers, then open http://localhost:1323 and sign in with the address and password of a mail account:
curl -sI http://localhost:1323/ | head -1
# HTTP/1.1 200 OKA successful sign-in is logged as "msg":"sign-in" with the user and client address. A sign-in that fails because the mail server could not be reached is logged as "msg":"sign-in failed" with "reason":"error" and the cause, for example x509: certificate signed by unknown authority for a server whose CA the host does not trust (see SSL_CERT_FILE under Environment variables).
What this file gives you:
- ALPS listens on port 1323 on every interface, over plain HTTP. It logs JSON lines at info level to stdout.
- The
base(mail) andgpgplugins load. There is no Password section in Settings, since password change needs[plugin.password] enabled = trueand an endpoint or command. There are no contacts, calendars or filters, since the file has no[plugin.carddav],[plugin.caldav]or[plugin.managesieve]section. - Sign-in rate limits are on, at their defaults.
- Security keys work only at the default origins, http://localhost:1323 and the Vite dev server's http://localhost:5173, as the warning says.
- There is no
login_key, so sessions do not survive a restart of ALPS, and linking accounts is unavailable.
That is enough to try ALPS out on one machine (Setup-Local-Try-Out). For anything users reach over a network, work through the production checklist.
Only the provider section and [smtp] server are required. Every other section is optional.
| Section | What it sets | Page |
|---|---|---|
[server] |
Listen address, debug mode, trusted proxies and origins, login_key, the users' own CalDAV and CardDAV servers, temp directory, session lifetimes and caps, HTTP, IMAP and SMTP timeouts, attachment limits, the background connection, new-mail push, CONDSTORE |
Config-Server |
[server.rate_limit] |
Limits on sign-ins and other password and recovery-code checks | Config-Rate-Limiting-and-WebAuthn |
[webauthn] |
Hostname, origins and display name for security keys | Config-Rate-Limiting-and-WebAuthn |
[provider], [provider.imap], [provider.maildir], [provider.multi]
|
The mail store: one IMAP server, a local Maildir, or routing by domain | Config-Providers-and-SMTP |
[smtp] |
The submission server | Config-Providers-and-SMTP |
[tls], [tls.letsencrypt], [tls.letsencrypt.s3]
|
TLS in ALPS itself: certificate files, or Let's Encrypt with file or S3 storage | Config-TLS |
[cluster] |
Gossip between several ALPS nodes | Config-Cluster |
[logging] |
Log output, format, level and the per-request lines | Config-Logging-and-Cache |
[cache] |
The per-session cache of listings and message parts | Config-Logging-and-Cache |
[plugin.<name>] |
Which of base, caldav, carddav, managesieve, gpg and password load, the CalDAV, CardDAV and ManageSieve servers, the calendar's first day of the week, and the password plugin's options |
Config-Plugins |
[branding] |
The default colour theme and the logo | Config-Branding |
The provider section must match [provider] type: [provider.imap] for "imap", [provider.maildir] for "maildir", [provider.multi] for "multi". ALPS looks for an exact match first, then for one that differs only in case.
A key left out takes the default listed in docs/CONFIGURATION.md. config.example.toml sets a few keys to other values than the defaults. For example, it sets write_timeout_sec = 30 where the default is 60, and the [logging] values above.
-
Numbers. For most integer keys, 0 or a negative value means the default, so those limits cannot be turned off. This covers
session_minutes, the attachment limits, the HTTP, IMAP and SMTP timeouts, every limit in[server.rate_limit],[cache] ttl_minutesandmax_part_kib, and[tls.letsencrypt] sync_interval_minutes. Four keys work differently:max_session_minutes = 0means no cap, a negativeabsolute_session_hoursremoves the absolute session limit, and a negativemax_sessionsormax_sessions_per_user(such as-1) turns that session cap off, while 0 keeps its default. -
Switches that are on by default.
[server] background_connection,[cache] enabled,[logging] accessand[server.rate_limit] enabledstay on unless the file saysfalse. Leaving a key out never turns one of them off. -
[webauthn]. Each key falls back to its own default, so settingrpidwithoutoriginskeeps the localhost origins. -
[logging]. Each key has its own default, whichever of the others are set:outputis stdout,formatis JSON (console with[server] debug = true), andlevelis info (debug withdebug = true).
Some values are not checked and fall back without a message: an unknown level means info, and any format other than "json" gives console text. See Config-Logging-and-Cache.
ALPS has no mode that only checks the file. It checks the file when it starts, and stops with exit status 1 at the first problem. Errors found while the file is read and checked, before the log is opened, are written to stderr as plain text. Errors found in the later startup steps (the cluster, the SMTP server URL, the plugins, WebAuthn, TLS, the listener) go to the log, which is stdout unless [logging] output says otherwise. Under systemd, both end up in the journal (journalctl -u alps).
| Message | Cause |
|---|---|
Error: -config flag is required |
-config is missing. |
Error loading config file: failed to read config file: open ...: no such file or directory |
The path is wrong, or the file cannot be read. |
Error loading config file: failed to parse TOML config: toml: line <n> ... |
A TOML syntax error, or a value of the wrong type, such as addr = 1323 instead of addr = ":1323". |
Error loading config file: provider.imap: missing TOML section [provider.imap] |
No section matches [provider] type. |
Error generating options: unknown provider type 'pop3' |
[provider] type is not imap, maildir or multi, and a section of that name exists. Without the section, the message is the one above. |
Error generating options: IMAP server requires a scheme (imaps://, imap://, imap+insecure://), got: ... |
[provider.imap] server has no scheme, or is missing (got empty string). |
Error generating options: no SMTP server specified in config file ([smtp] server) |
[smtp] server is missing. It is required with every provider. |
Error generating options: invalid login key |
login_key is not 32 bytes in base64 or hex. |
Error generating options: invalid trusted_proxies IP/CIDR ... |
An entry of trusted_proxies is not an address or CIDR block. |
Error generating options: branding.default_theme: unknown theme ... |
[branding] default_theme is not alps, migadu, nord or ocean. |
Error generating options: cluster.bind: must be an IP address with an optional port, got ... |
With [cluster] enabled = true, bind is a host name or an interface name (see Config-Cluster). |
Error generating options: multi: ... |
A mistake in [provider.multi], such as a template without its allowlist or a backend endpoint without a scheme (see Config-Providers-and-SMTP). |
SMTP server requires a scheme (smtps://, smtp://, smtp+insecure://), got: smtp.example.com:465 (in the log) |
[smtp] server has no scheme, for example host:port. Write smtps://smtp.example.com:465. |
failed to load plugins: CardDAV server requires a scheme ... (in the log) |
A [plugin.carddav] server without a scheme. ALPS reads a plugin's server only while enabled lets the plugin load, so with enabled = false the value is not checked. |
failed to initialize cluster ... or failed to decode cluster secret key: ... (in the log) |
The cluster could not start, most often because [cluster] enabled = true has no valid secret_key (see Config-Cluster). |
Failed to build TLS config: unsupported tls provider: (in the log) |
[tls] enabled = true without provider = "file" or "letsencrypt" (see Config-TLS). |
listen tcp :1323: bind: address already in use (in the log) |
Another process listens on [server] addr. |
Each section's page lists that section's own checks.
Some settings do not stop ALPS and are only logged at startup:
- at warn level: each unknown key (see Misspelt keys), an unset
[webauthn] rpid(shown above), an entry of[webauthn] originswhose host name is notrpidor under it (WebAuthn: origin "http://localhost:1323" is outside rpid "webmail.example.com"; ...), and a name in the plugins' enabled list that did not load (Enabled plugin "<name>" did not load; check its name and backend configuration); - at info level:
push_new_mailwithbackground_connection = false(push_new_mail needs background_connection, which is off: new mail is not pushed),push_subscribed_folderswithoutpush_new_mail, a ManageSieve plugin with nothing to connect to (ManageSieve enabled without a server or routed endpoint; plugin disabled), and a password plugin with no way to change a password (password: enabled without an endpoint, command or routed backend; plugin disabled).
ALPS ignores keys and sections it does not know, so a misspelt key keeps its default. It logs a warning at startup for each one, once the log is open, naming the key but not its value. For example, write_timeout = 5 leaves write_timeout_sec at 60 and logs:
{"level":"WARN","msg":"Unknown configuration key: server.write_timeout (ignored)"}
A [serverr] section is skipped entirely, and ALPS names the section and each key in it. The keys of a [provider.<type>] section for another type than [provider] type selects are reported too. Two kinds of table are free-form and not checked: a plugin's options, and under [provider] type = "multi" the password table of a backend. Look for Unknown configuration key after each change.
Plugin names are not checked this way, since every [plugin.<name>] section is valid TOML for ALPS. One [plugin.<name>] enabled = true anywhere, even under a misspelt name such as [plugin.carddv], means only base and the plugins whose own sections say enabled = true load. ALPS then warns Enabled plugin "carddv" did not load; check its name and backend configuration, and starts without the other plugins. base, with sign-in and mail, always loads. After each start, check the Loaded plugin lines for every plugin you expect (Config-Plugins).
Everything else is set in the file. ALPS also reads these variables (docs/CONFIGURATION.md):
| Variable | Effect |
|---|---|
CLUSTER_SECRET_KEY |
When set, used in place of [cluster] secret_key. This keeps the gossip key out of the file. |
TMPDIR |
The temp directory when [server] temp_dir is unset. temp_dir sets TMPDIR for the whole ALPS process. |
SSL_CERT_FILE, SSL_CERT_DIR
|
On Linux and the BSDs, where Go reads the trusted CA certificates. They apply to ALPS's TLS connections to the IMAP, SMTP, ManageSieve, CalDAV and CardDAV servers. A mail server with a private CA needs that CA here or in the host's trust store. |
HTTPS_PROXY, HTTP_PROXY, NO_PROXY
|
Go's proxy settings, for the HTTP requests ALPS makes to the CalDAV and CardDAV servers, the password plugin's endpoint, Let's Encrypt and S3. IMAP, SMTP and ManageSieve connect directly, and so do ALPS's fetches of remote message content and BIMI logos, and its requests to the CalDAV and CardDAV servers users enter in Settings while [server] dav_allow_private_servers is off. |
| The AWS SDK's own variables | Credentials and region for the [tls.letsencrypt.s3] bucket, where the file does not set them. |
The password plugin's command runs with PATH as its only environment variable.
Each item links to the page with the details. Running-in-Production covers the service files, logs and restarts.
-
Keep debug off. Leave
[server] debugand[provider.imap] debugatfalse. The protocol traces they turn on go to the log at debug level, and they include message, contact and calendar content (Config-Server, Config-Logging-and-Cache). -
Use your own mail servers, with TLS. Use
imaps://orimap://, andsmtps://orsmtp://. The+insecureschemes andinsecure = truesend passwords in clear text (Config-Providers-and-SMTP). Use a mail server with METADATA, or settings, security keys and keyrings are not kept (Mail-Server-Requirements). -
Choose where TLS ends. Behind a reverse proxy, leave
[tls]off, bindaddr = "127.0.0.1:1323"when the proxy runs on the same host, and have the proxy sendX-Forwarded-Proto,X-Forwarded-HostandX-Forwarded-For(Setup-Behind-a-Reverse-Proxy). Without a proxy, enable[tls](Config-TLS, Setup-Lets-Encrypt-Standalone). -
List only your own proxies in
trusted_proxies. List every proxy hop in front of ALPS, and nothing else. ALPS takes the client address for rate limits and logs, the scheme and host for the origin check, and the scheme forSecurecookies from these peers only (Config-Server). -
Set
[webauthn] rpidandoriginsto the public hostname and its origin, for example"webmail.example.com"and["https://webmail.example.com"]. Neither can be taken from a proxy's headers. The startup warning goes away oncerpidis set (Config-Rate-Limiting-and-WebAuthn). -
Set
login_keyand keep it secret. With it, sessions survive a restart, "Stay signed in after closing the browser" keeps a sign-in for 30 days, users can link accounts, and users can save separate CalDAV and CardDAV passwords. Generate one withopenssl rand -base64 32. Every node serving the same hostname needs the same key. Changing it signs everyone out, linked accounts must be linked again, and separate DAV passwords entered again (Config-Server, Sessions-and-Sign-in, docs/SESSIONS.md). -
Set
[provider.imap] authserv_idsto the authserv-id your receiving server writes inAuthentication-Results. Sender badges and BIMI logos then follow your server's verdict (Config-Providers-and-SMTP). -
Leave rate limiting on.
[server.rate_limit] enabled = falseturns every tier off. With several nodes, enable[cluster]so the nodes share their counts (Config-Rate-Limiting-and-WebAuthn). -
Choose the log format and destination. The example's console text suits a terminal, and
format = "json"suits a log collector. Give a log file an absolute path, since the shipped service files run ALPS in/(Config-Logging-and-Cache). -
Check the temp directory. Under the shipped systemd unit,
PrivateTmpgives ALPS its own/tmp. Leavetemp_dirunset there, or point it outside/tmpand/var/tmpand list it inReadWritePaths. Atemp_dirmust exist before ALPS starts and be writable only by the user ALPS runs as (docs/OPERATIONS.md). -
Set up the plugins you use. Give
caldav,carddavandmanagesieveaserver, orenabled = false.enabled = falsekeeps a plugin off. Once any section setsenabled = true, only the plugins whose sections set it load, so then give every plugin you wantenabled = true. Leave[server] dav_allow_private_serversoff unless your users keep their own DAV servers on a trusted intranet. Check theLoaded pluginlines and the warnings after a restart (Config-Plugins, Config-Server). -
For several nodes, enable
[cluster]with asecret_key(orCLUSTER_SECRET_KEY), a stable and uniquenode_id, and the samelogin_keyon every node. With Let's Encrypt, the certificates must be stored in S3 (Config-Cluster, Setup-Cluster). -
Protect the file. It holds
login_key, and possibly S3 keys and the password plugin's credentials. Make it readable by the user ALPS runs as and by no other users. -
Restart and read the startup lines after every change: no errors, no
Unknown configuration keyor other warnings, the expectedLoaded pluginlines, andStarting server on <addr>.
A file for one node behind a reverse proxy on the same host, before plugins and TLS:
[server]
addr = "127.0.0.1:1323"
trusted_proxies = ["127.0.0.1/32", "::1/128"]
login_key = "<output of openssl rand -base64 32>"
[webauthn]
rpid = "webmail.example.com"
display_name = "Example Webmail"
origins = ["https://webmail.example.com"]
[logging]
format = "json"
[provider.imap]
server = "imaps://imap.example.com:993"
authserv_ids = ["mx.example.com"]
[smtp]
server = "smtps://smtp.example.com:465"It starts with no warning and logs JSON lines to stdout, the default output.
- docs/CONFIGURATION.md: every key, with its type, default and meaning
- config.example.toml: the commented example
- docs/OPERATIONS.md: signals, startup order, exit status, files on disk, service files
- Architecture-Overview and Mail-Providers: what the provider and plugin sections plug into
These pages describe ALPS as it is on the main branch; the reference documents live in the repository's docs/ directory.
Architecture
- Architecture Overview
- Mail Server Requirements
- Sessions and Sign-in
- Storage in IMAP METADATA
- Mail Providers
- Plugins
- Features
- Security Features
- Clustering
Tests
Configuration
- Configuration Overview
- Server
- Providers and SMTP
- TLS
- Cluster
- Rate Limiting and WebAuthn
- Logging and Cache
- Plugins
- Branding
Common Setups