Skip to content

Running in Production

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

This page covers running ALPS as a service: the systemd unit and the FreeBSD rc.d script shipped in dist/, the log and its rotation, the signals ALPS handles, what a restart or an upgrade costs users, what to back up, which connections the host must allow, and a checklist. The systemd setup below was built and checked on Debian 13. The reference is docs/OPERATIONS.md.

What you get

  • ALPS at /usr/local/bin/alps, started by systemd as user alps with /etc/alps/config.toml, listening on 127.0.0.1:1323 behind a reverse proxy that serves https://webmail.example.com.
  • The log in /var/log/alps/alps.log, one JSON object per line, rotated by logrotate, which tells ALPS to reopen the file.
  • A drop-in for the shipped unit that lets ALPS write its log.
  • Browsers that stay signed in across restarts and upgrades, through the login token that login_key enables.
  • Uploaded attachments in a /tmp private to the service, which systemd deletes at every stop.

ALPS keeps no database. The mail, the settings and the security keys live on the mail server. The host holds the binary, the configuration and the log.

Before you start

  • A Linux host with systemd. For FreeBSD, see FreeBSD below.
  • An ALPS binary. make build-linux writes a static amd64 binary to build/alps-linux-amd64, and make build writes build/alps for the build host (README). Both embed the web app, so nothing else needs to be installed.
  • A mail server that meets Mail-Server-Requirements. The example uses IMAPS on imap.example.com (port 993), SMTPS on smtp.example.com (port 465) and ManageSieve on imap.example.com (port 4190).
  • A reverse proxy that terminates TLS for webmail.example.com and forwards to 127.0.0.1:1323 (Setup-Behind-a-Reverse-Proxy). To have ALPS serve HTTPS itself, see Setup-Lets-Encrypt-Standalone. The unit allows ALPS to listen on ports 443 and 80.
  • A login_key, generated once and kept secret:
    openssl rand -base64 32

Configuration

/etc/alps/config.toml

[server]
addr = "127.0.0.1:1323"
trusted_proxies = ["127.0.0.1/32", "::1/128"]
login_key = "paste-the-generated-value-here"
# temp_dir is left unset: the unit's PrivateTmp gives ALPS a /tmp of its own.

[logging]
output = "/var/log/alps/alps.log"
format = "json"
level = "info"

[webauthn]
rpid = "webmail.example.com"
origins = ["https://webmail.example.com"]

[provider]
type = "imap"

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

[smtp]
server = "smtps://smtp.example.com"

[plugin.base]
enabled = true

[plugin.managesieve]
enabled = true
server = "managesieve://imap.example.com"
  • addr on the loopback address means only the proxy on this host reaches ALPS. trusted_proxies lists that proxy and nothing else, so ALPS takes the client's address from X-Forwarded-For only when the request comes from it (Config-Server).
  • login_key lets a browser come back after a restart without a new sign-in. The file holds it, so it is installed readable by root and the alps group only (see Start it).
  • temp_dir is not set. Under the shipped unit, /tmp is private to ALPS (see Variations for a directory of its own).
  • [logging] writes to a file. Leave the section out to log to the journal instead (see Variations).
  • [webauthn] names the public hostname, so security keys work. Without it ALPS logs a warning at startup and keys work only from localhost.
  • authserv_ids names the receiving mail server whose Authentication-Results field ALPS trusts for sender verdicts and BIMI logos. Use the authserv-id your MX writes.
  • [server] debug is not set, so it stays off. Keep it off in production: it turns on traces of the IMAP, CalDAV, CardDAV and ManageSieve exchanges, message content included. They are logged as protocol trace lines at debug level, so this file's level = "info" leaves them out, but level = "debug" would write them to this log.
  • ALPS ignores a key it does not know, keeps its default, and warns at startup, such as Unknown configuration key: server.trusted_proxy (ignored). Check the log after each change, and names against config.example.toml.

CalDAV, CardDAV, OpenPGP and password change are left out here; Config-Plugins covers them. Every key is on the Configuration-Overview pages.

Drop-in for the unit

The shipped unit runs with ProtectSystem=strict, so ALPS cannot write outside its private /tmp and /var/tmp. Run systemctl edit alps and enter:

[Service]
ReadWritePaths=/var/log/alps

systemd saves it as /etc/systemd/system/alps.service.d/override.conf. systemctl edit needs a terminal; from a script, write the same lines to that file and run systemctl daemon-reload.

ReadWritePaths=/var/log/alps lets ALPS open its log file. Without it, ALPS logs to standard error, which goes to the journal (see Troubleshooting). A reload during startup, from a log rotation for example, needs nothing in the drop-in: ALPS handles SIGHUP from the moment its log is open (see Signals and stopping).

The shipped unit's own TimeoutStopSec=60 covers ALPS's longest shutdown, 55 seconds with Let's Encrypt on S3 and [cluster] (see Signals and stopping). A unit copied from an earlier release has TimeoutStopSec=30, which systemd can reach while ALPS still waits for a request in progress. Copy the current dist/alps.service, or add TimeoutStopSec=60 to the drop-in.

/etc/logrotate.d/alps

/var/log/alps/alps.log {
    weekly
    rotate 8
    compress
    delaycompress
    missingok
    notifempty
    create 0640 alps alps
    postrotate
        systemctl reload alps.service > /dev/null 2>&1 || true
    endscript
}

systemctl reload alps sends SIGHUP, and ALPS opens the log path again, so it goes on writing to the new file. On Debian, logrotate.timer runs logrotate daily.

Variations

  • Journal only. Leave out the whole [logging] section, or its output line. ALPS then writes to standard output, as JSON lines at info level unless format or level say otherwise, and the journal keeps them: journalctl -u alps. Drop ReadWritePaths=/var/log/alps and the logrotate file.
  • A temp directory of its own, on disk rather than in the host's /tmp. Create it, set it, and make it writable:
    install -d -o alps -g alps -m 700 /var/lib/alps /var/lib/alps/tmp
    [server]
    temp_dir = "/var/lib/alps/tmp"
    [Service]
    ReadWritePaths=/var/lib/alps /var/log/alps
    Leave room there above max_global_attachment_mib (default 1024 MiB). Upload files left behind by a stop or a crash then stay until ALPS's hourly sweep removes them, 7 days later (see Troubleshooting).
  • Let's Encrypt. Set an absolute [tls.letsencrypt] cache_dir, such as /var/lib/alps/cert-cache, and list /var/lib/alps in ReadWritePaths. The unit sets no working directory, so the default cache_dir = "cert-cache" would resolve to /cert-cache, which cannot be written. See Setup-Lets-Encrypt-Standalone.
  • Maildir provider. List the maildirs in ReadWritePaths. For maildirs under /home, also set ProtectHome=false, since the unit hides /home (Setup-Maildir).

Start it

As root, from a checkout with the built binary, with the files above saved there as config.toml and alps.logrotate:

useradd --system --home-dir /nonexistent --no-create-home --shell /usr/sbin/nologin alps
install -m 755 build/alps-linux-amd64 /usr/local/bin/alps
install -d -m 750 -o root -g alps /etc/alps
install -m 640 -o root -g alps config.toml /etc/alps/config.toml
install -d -m 750 -o alps -g alps /var/log/alps
cp dist/alps.service /etc/systemd/system/alps.service
systemctl daemon-reload
systemctl edit alps                  # the drop-in above
cp alps.logrotate /etc/logrotate.d/alps
systemd-analyze verify alps.service  # prints nothing when the unit is valid
systemctl enable alps
systemctl start alps

systemctl start returns as soon as ALPS has been started, even when ALPS then exits with a configuration error, so check the result as shown below.

Check it works

systemctl is-active alps
grep 'Starting server on' /var/log/alps/alps.log
grep -c 'rpid is unset' /var/log/alps/alps.log
journalctl -u alps -n 20 -o cat
curl -sI http://127.0.0.1:1323/ | grep -iE '^HTTP|x-alps-build'
  • systemctl is-active prints active. activating means ALPS keeps exiting and being started again (see Troubleshooting).
  • The log has {"time":"…","level":"INFO","msg":"Starting server on 127.0.0.1:1323"}, and the count of rpid is unset warnings is 0.
  • Once the log file is open, the journal holds only systemd's own lines and any error ALPS met before it opened the log (see Logs and rotation).
  • curl prints HTTP/1.1 200 OK and an X-Alps-Build header. ALPS has no separate health endpoint. GET / needs no session, so it serves as a liveness probe, but it says nothing about the mail server (TLS.md).

Then sign in at https://webmail.example.com and check:

  • The log has a sign-in line with the browser's address, not the proxy's:
    {"time":"…","level":"INFO","msg":"sign-in","user":"user@example.com","ip":"203.0.113.10","session":"f707b6116cda","2fa":false,"remember":false,"restored":false,"agent":"…"}
    
  • ss -tnp | grep alps lists the connections from ALPS to the IMAP server's port 993: one or two for each session.
  • systemctl reload alps adds {"…","msg":"SIGHUP: log file reopened"} to the log.
  • logrotate -f /etc/logrotate.d/alps leaves alps.log.1 beside a new alps.log that starts with that same SIGHUP: log file reopened line.
  • grep 'open files' /proc/$(systemctl show alps -p MainPID --value)/limits shows 65536, the unit's LimitNOFILE. Each session usually holds one or two mail connections.

Logs and rotation

ALPS writes its log where [logging] output says: stdout, stderr, syslog or a file path. Without output, it writes to standard output, as JSON unless format says otherwise. Under the systemd unit, standard output and standard error go to the journal. Config-Logging-and-Cache covers the format, the level and the per-request lines (access).

The cluster's and the certificate manager's lines, the IMAP provider's warnings, such as the one for a server without METADATA, and the protocol traces of [server] debug follow [logging] like every other line. Only the errors found before the log is opened do not, such as Error loading config file: …: they go to standard error, so under the unit they are in the journal even when the log is a file. So check journalctl -u alps too when ALPS does not start.

ALPS opens a log file for appending and creates it with mode 0644 if it is missing, but it does not create its directory. It never rotates or truncates the file. A rotation tool moves the file aside and then sends SIGHUP, and ALPS opens the same path again. If that fails, ALPS logs SIGHUP: reopen log file … with the reason and keeps writing to the moved file.

The log holds user names, client addresses and browser user agents, and with debug on at level debug, message content. Keep it in a directory that only the alps user and administrators can read, as the 750 directory above does. When the reverse proxy keeps its own access log, [logging] access = false drops ALPS's per-request lines; sign-ins and session ends are still logged.

Signals and stopping

Signal Sent by What ALPS does
SIGHUP systemctl reload alps, service alps reload (see FreeBSD), the logrotate script above Opens the [logging] file path again and logs SIGHUP: log file reopened. With no log file, it logs SIGHUP: no log file to reopen. Nothing else: it does not reload the configuration or the TLS certificate files.
SIGTERM systemctl stop alps, systemctl restart alps, service alps stop (see FreeBSD) Shuts down gracefully.
SIGINT Ctrl-C when ALPS runs in a terminal The same as SIGTERM.

To apply a change to config.toml or a renewed certificate file, restart ALPS: systemctl restart alps. Reload only reopens the log file, as the comments in both service files say.

A graceful shutdown goes in this order (OPERATIONS.md):

  1. The listener closes, and with Let's Encrypt the ACME HTTP-01 listener too, so new connections are refused. Idle connections close, and open new-mail event streams (/events) are ended. Requests in progress get up to 30 seconds; one still running then is cut off, and ALPS logs HTTP server shutdown: context deadline exceeded.
  2. Every session is told to close. Each releases its mail connections and its upload files, and ALPS waits up to 5 seconds in all for that. When the cleanup takes longer, it logs Session shutdown timed out waiting for cleanup and goes on.
  3. The rate limiter and the hourly jobs stop, and the plugins are closed.
  4. With Let's Encrypt, the certificate workers stop: up to 5 seconds, or 15 with S3 storage.
  5. With [cluster], the node announces that it is leaving, waiting up to 5 seconds.
  6. ALPS exits with status 0.

A shutdown therefore takes up to 35 seconds without Let's Encrypt and [cluster]. Let's Encrypt adds up to 5 seconds, or 15 with S3 storage, and [cluster] up to 5, so with both a shutdown takes up to 55 seconds.

What the lab measured:

Situation Time to exit
Nothing in progress 0.06 s
An open /events stream 0.06 s
An upload still arriving, default read_timeout_sec = 10 9.2 s: the upload was cut off 10 s after it began, by its read timeout
A request still running, read_timeout_sec = 60 30.05 s on the host, 30.02 to 30.03 s under systemd

ALPS writes no log line of its own when a stop goes within these limits. The journal shows Stopping alps.service, then Deactivated successfully. Once a shutdown has begun, a second SIGTERM is ignored, and a SIGHUP still only reopens the log. SIGKILL ends it sooner, and systemd sends that when TimeoutStopSec runs out.

ALPS handles SIGHUP from the moment its log is open, before the slower startup steps: joining the cluster, the startup OPTIONS check of each CalDAV and CardDAV server, which can take up to 10 seconds each, and the S3 check for Let's Encrypt, which can take up to 30. A log rotation during startup therefore only reopens the log. SIGINT and SIGTERM end ALPS at once until its listener has started; startup normally takes milliseconds.

If ALPS is killed with SIGKILL or crashes, systemd starts it again after 5 seconds (Restart=on-failure, RestartSec=5). Requests in progress fail, a cluster node leaves without announcing it, and upload files stay in the temp directory until the hourly sweep removes them, or, with the unit's private /tmp, until systemd deletes it.

What a restart loses

ALPS keeps sessions and everything tied to them in memory. A restart, an upgrade or a crash loses (OPERATIONS.md):

  • Every session. A browser comes back without a password only when login_key is set and unchanged and the browser still has its login token, at most 30 days old. ALPS then opens a new session on the browser's next request and logs session restored. Without login_key, everyone signs in again. A changed login_key signs everyone out, linked accounts must be linked again, and separate CalDAV and CardDAV passwords saved in Settings must be entered again (Sessions-and-Sign-in).
  • Uploaded attachments not yet sent or saved in a draft. The composer's next save or send is refused, and the composer lists the files to add again.
  • Sign-ins waiting for a security key or a recovery code, and security-key enrolments in progress. They start over.
  • Open new-mail event streams.
  • Rate-limit counts and lockouts.
  • In-memory caches, such as the CalDAV and CardDAV home sets.

A restart keeps everything on the mail server (mail, settings, sender identities, security keys, linked accounts, the users' own CalDAV and CardDAV connections) and the files ALPS wrote: the log, the Let's Encrypt certificate cache, and the BIMI logo cache unless it is in the private /tmp.

Upgrades

On a single host:

systemctl stop alps
install -m 755 build/alps-linux-amd64 /usr/local/bin/alps
systemctl start alps
/usr/local/bin/alps -version

When several ALPS instances serve the same accounts, in a cluster or as separate standalone instances, upgrade them together, never one node at a time:

  1. Stop every instance that runs the old binary.
  2. Wait until each one has exited: systemctl is-active alps prints inactive on every host.
  3. Install the new binary everywhere and start the instances, in any order.

Mixing binaries is not supported, because of how two-factor sign-in records are kept (WEBAUTHN.md). A configuration change with the same binary can be applied one node at a time (Clustering). Going back to an older binary needs a maintenance step for the two-factor records, so read Deployment and rollback before a rollback.

An upgrade ends every session, as any restart does. With login_key set and unchanged, browsers are restored from their login token.

To see what is running:

  • alps -version prints alps <version> (<commit>) built at <date>. make build, make build-linux and make build-freebsd set the three values. A binary from a plain go build prints alps unknown (unknown) built at unknown. ALPS does not log its version.
  • Every response carries X-Alps-Build, a hash of the web app ALPS serves together with the [branding] settings. It changes when the embedded frontend, [branding] default_theme or the choice of logo changes, or a file of a favicon package used as logo (read at startup) changes (Config-Branding), not with every binary. The open web app uses it to notice that a newer frontend is being served.

Backups

The ALPS host holds no account data. Mail and the records ALPS keeps for each account (settings, identities, OpenPGP keys, linked accounts, security keys, own CalDAV and CardDAV connections) are on the mail server, in its folders and its IMAP METADATA. Back up the mail server, METADATA included (Storage-in-IMAP-METADATA).

On the ALPS host, back up:

  • /etc/alps/config.toml, and keep the copy as private as the file. A restored host needs the same login_key for login tokens, linked accounts and saved CalDAV and CardDAV passwords, and the same [webauthn] rpid for security keys.
  • The drop-in and the logrotate file.
  • The Let's Encrypt cache_dir when storage_provider = "file": it is the only copy of the account key and the certificates.
  • With the maildir provider, each maildir's alps_store.json along with the mail.

The temp directory, the BIMI logo cache and the log need no backup for ALPS to work.

Network connections

Inbound, the host must accept:

  • [server] addr from the reverse proxy (here 127.0.0.1:1323, loopback only);
  • when ALPS terminates TLS: the HTTPS port in addr, and with Let's Encrypt the ACME HTTP-01 listener on acme_http_addr (default :80);
  • with [cluster]: the gossip port (default 7946), TCP and UDP, from the other nodes.

Outbound, the firewall must allow (OPERATIONS.md):

Destination When Port
IMAP server Sign-in, and for the life of each session 993 for imaps://, 143 for imap://, unless the URL names one
SMTP server Each send 465 for smtps://, 587 for smtp://, unless the URL names one
ManageSieve server Filters and auto-reply 4190 unless the URL names one
CalDAV and CardDAV servers One OPTIONS check each at startup, then as users work HTTP or HTTPS
CalDAV and CardDAV servers that users enter in Settings When a user saves a connection, then as the user works HTTPS at public addresses; with [server] dav_allow_private_servers, also HTTP and any address
Password-change endpoint Each password change HTTP or HTTPS
Let's Encrypt With provider = "letsencrypt" HTTPS
S3 With storage_provider = "s3" HTTPS, or HTTP for an http:// endpoint
Other ALPS nodes With [cluster] 7946 TCP and UDP by default
DNS Every host name above, and the BIMI and SRV lookups 53
Any public web host Remote images and fonts that users choose to load, and BIMI sender logos HTTP and HTTPS, on any port a URL names

The remote-content proxy, the BIMI fetch and, while dav_allow_private_servers is off, the requests to the DAV servers users enter connect directly and ignore HTTP_PROXY and HTTPS_PROXY. A host whose web traffic must pass an HTTP proxy cannot load remote content or BIMI logos, nor reach those DAV servers. The other HTTP clients (CalDAV and CardDAV for the servers in the configuration, the password endpoint, ACME, S3) follow HTTP_PROXY, HTTPS_PROXY and NO_PROXY.

FreeBSD

dist/alps is an rc.d script. rc.subr starts daemon(8) as user alps, and daemon(8) runs /usr/local/bin/alps -config /usr/local/etc/alps/config.toml. daemon(8) appends ALPS's standard output and standard error to /var/log/alps/alps.log, writes ALPS's pid to /var/run/alps/alps.pid and its own to /var/run/alps/daemon.pid, and reopens alps.log when it receives SIGHUP. The script sets procname to /usr/local/bin/alps, so service alps status, stop and reload find ALPS by that pid file. Before each start, the script creates the log directory and /var/run/alps with mode 750, owned by alps_user and alps_group.

cp dist/alps /usr/local/etc/rc.d/alps
chmod +x /usr/local/etc/rc.d/alps
sysrc alps_enable=YES
service alps start
rc.conf variable Default
alps_enable NO
alps_user alps
alps_group alps
alps_config /usr/local/etc/alps/config.toml
alps_logdir /var/log/alps
  • The user alps must exist.

  • service alps reload sends SIGHUP to ALPS. That reopens only a file ALPS opened itself through [logging] output, not daemon(8)'s alps.log, and it does not reload the configuration. Use service alps restart for a configuration change.

  • daemon(8), not ALPS, holds alps.log open, so newsyslog must send SIGHUP to the pid in /var/run/alps/daemon.pid after moving it. The script's comments give this entry for /etc/newsyslog.conf.d/alps.conf:

    /var/log/alps/alps.log alps:alps 640 7 * @T00 JC /var/run/alps/daemon.pid 1
    

    A log file of its own set in [logging] output is rotated the other way: newsyslog sends SIGHUP to ALPS itself, at /var/run/alps/alps.pid.

  • daemon(8) runs ALPS in /, so set an absolute cache_dir for Let's Encrypt.

  • ALPS uses the shared /tmp unless told otherwise, so set temp_dir to a directory of its own, created beforehand with mode 700 and owned by alps.

The FreeBSD script was checked against its source, FreeBSD's rc.subr and OPERATIONS.md only; it was not run on FreeBSD for this page.

Troubleshooting

These came up while building the setup.

Failed to open log file /var/log/alps/alps.log: open /var/log/alps/alps.log: read-only file system, falling back to stderr in journalctl -u alps. The unit's ProtectSystem=strict keeps ALPS from writing there. Add the directory to ReadWritePaths in the drop-in and restart. The log directory must also exist and belong to alps.

Attachments fail with HTTP 500 upload_storage_failed, small ones work. Uploads over 32 KiB are written to the temp directory, and it is missing or cannot be written. ALPS logs an ERROR line upload multipart: ... with the cause, such as no such file or directory or read-only file system. Under the shipped unit this happens when temp_dir is under the host's /tmp (the unit's private /tmp hides it) or outside ReadWritePaths. Leave temp_dir unset, or follow A temp directory of its own.

systemctl is-active alps prints activating, and the journal repeats the same error every 5 seconds, for example:

alps[3871]: Error loading config file: failed to parse TOML config: toml: line 9 (last key "server.read_timeout_sec"): incompatible types: TOML value has type string; destination has type integer
systemd[1]: alps.service: Main process exited, code=exited, status=1/FAILURE
systemd[1]: alps.service: Scheduled restart job, restart counter is at 2.

ALPS exits with status 1 on a configuration error and Restart=on-failure starts it again 5 seconds later, without end. These errors come before the log file is opened, so they are in the journal only. ALPS has no flag that only checks a configuration, so keep the previous file to go back to. Fix the file and run systemctl restart alps.

A configuration change does not take effect after systemctl reload alps. Reload only reopens the log. Run systemctl restart alps.

multipart-* files in a temp_dir of ALPS's own. A stop waits up to 5 seconds for the sessions to remove their upload files, and a crash or a kill leaves them all. A running ALPS removes them once an hour, starting an hour after startup: every multipart-* file at the top of the temp directory that is more than 7 days old, apart from the uploads it still holds. The sweep does not tell ALPS's files from another program's files of the same name, which is one more reason to give ALPS a temp directory of its own. No upload survives a restart, so while ALPS is stopped every multipart-* file in its own temp directory can be deleted. With temp_dir unset under the unit, systemd deletes the private /tmp at every stop.

A stop takes 30 seconds. A request was still running, such as a large download. ALPS gives it up to 30 seconds, and the unit's TimeoutStopSec=60 lets systemd wait for that.

Everyone has to sign in again after a restart. login_key is not set, or it changed. Set it once and keep it, the same on every node.

Production checklist

  • [server] debug off (the default).
  • [webauthn] rpid and origins set to the public hostname, and no rpid is unset warning at startup.
  • login_key set, kept secret, the same on every node; config.toml mode 640, owner root, group alps.
  • Behind a proxy: addr on the loopback address or a private interface, and only your own proxies in trusted_proxies. The sign-in log line shows the client's address.
  • [provider.imap] authserv_ids set to your receiving mail server's authserv-id.
  • The rate limiter left on (the default), with the same [server.rate_limit] values on every node.
  • The log: in the journal, or in a file in a 750 directory with ReadWritePaths and logrotate.
  • temp_dir: unset under the unit, or a directory of ALPS's own, mode 700, listed in ReadWritePaths, with room above max_global_attachment_mib.
  • The unit from the current dist/alps.service, with TimeoutStopSec=60. The drop-in: ReadWritePaths for every directory ALPS writes. On FreeBSD, the current dist/alps script, and newsyslog signalling /var/run/alps/daemon.pid.
  • Let's Encrypt: an absolute cache_dir in ReadWritePaths.
  • The firewall allows the outbound connections ALPS needs.
  • Backups cover the mail server with its METADATA, and the ALPS configuration.
  • Upgrades stop every instance before any new one starts.

Security-Features has the security side of this list, and Configuration-Overview goes through the configuration file key by key.

Tested with

ALPS main a5443047 (linux/arm64 and darwin/arm64 builds), Debian 13.7 (debian:trixie image) with systemd 257.13-1~deb13u1, logrotate 3.22.0-1+b2, curl 8.14.1 and Python 3.13.5, in Docker Desktop (engine 29.9.2, arm64) on macOS 26.4; mail server sora (lab build, checkout e8eff5cb). Signal timings were measured with the darwin/arm64 build on the host, and the stop with a request still running also under systemd. The systemd setup was then rebuilt from this page in a fresh container, interactive systemctl edit included. The FreeBSD rc.d script was not run. 2026-11-12.

Updated from the code at main 92ec0e11 without a rerun: the drop-in, which no longer needs RestartForceExitStatus=SIGHUP now that ALPS handles SIGHUP during startup, so that troubleshooting case is gone; the protocol traces of debug, which now follow [logging]; the startup warning for unknown keys; the 500 answer and log line for an upload the temp directory cannot take; X-Alps-Build with a favicon package; and the CalDAV and CardDAV connections users save in Settings, in what a restart keeps, what to back up, the key they need and the outbound connections. The configuration file above, alone and with a temp_dir, was started once with a 92ec0e11 build on macOS, to check that ALPS knows every key and loads base and managesieve.

Clone this wiki locally