Skip to content

Running yarilo with Docker Compose ​

A single-host standalone deployment for local development, evaluation and small self-hosted installs. It mirrors the standalone topology from the Helm chart, on one host, with Docker Compose.

Clients connect to login proxies, which terminate TLS and forward to the session backends. The backends share a Maildir store and coordinate through yarilo-auth, yarilo-warden and yarilo-locks (Redis-backed). One image serves every role — each container picks its component via YARILO_COMPONENT.

WARNING

Single-host, not highly available. For HA or multi-node, use the Helm chart — see Deployment.

Everything lives in deploy/compose/.

Services ​

GroupContainers
Infraredis, yarilo-auth (userdb), yarilo-warden, yarilo-locks
Session backendsyarilo-imap, yarilo-pop3, yarilo-lmtp, yarilo-submission, yarilo-managesieve, yarilo-jmap
Full-text searchyarilo-fts, the search index service the sessions dial at yarilo-fts:9106
Login proxies (TLS)yarilo-imap-login, yarilo-pop3-login, yarilo-submission-login, yarilo-lmtp-login, yarilo-managesieve-login, yarilo-jmap-login
MTA integrationyarilo-sasl-login (SASL auth for Postfix), yarilo-quota-status (quota policy)
Operator APIyarilo-backend-api, the API yarctl backend talks to (not published on the host)

The userdb is SQLite — yarilo-auth owns it in the state volume; mail lives in the shared mail volume. No external database is required.

Prerequisites ​

  • Docker Engine 24+ with the Compose v2 plugin (docker compose version)
  • Free host ports: 143/993, 110/995, 587/465, 4190, 8443, and — loopback-only by default — 24, 12345, 12340. Override in .env.
  • ~512 MB RAM

Quickstart ​

sh
cd deploy/compose
cp .env.example .env
sed -i.bak "s/^BACKEND_API_TOKEN=.*/BACKEND_API_TOKEN=$(openssl rand -hex 32)/" .env
./gen-certs.sh mail.example.test
docker compose up -d
docker compose ps

This sequence:

  • Copies the default environment; adjust the image tag or ports in .env if needed
  • Sets BACKEND_API_TOKEN, the bearer token of yarilo-backend-api; compose refuses to start without it
  • Generates a self-signed TLS certificate for local use
  • Starts all containers; docker compose ps should report every one as healthy

Creating the first user ​

The SQLite userdb lives in the state volume at /var/lib/yarilo/users.db. yarilo-auth creates the yarilo_users table on first start.

Insert a user with a one-off sqlite container:

sh
docker run --rm -v yarilo_state:/data nouchka/sqlite3 /data/users.db \
  "INSERT INTO yarilo_users (username,password,home,mail,enabled) VALUES \
   ('[email protected]','{PLAIN}changeit', \
    '/var/mail/vhosts/example.test/[email protected]', \
    'maildir:/var/mail/vhosts/example.test/[email protected]',1);"

The mail column is the per-user mail_location (maildir: / sdbox: / mdbox:).

TIP

{PLAIN} is fine for local testing only. Use a hashed scheme for real use.

Sending and reading a test message ​

Deliver a message via LMTP on the loopback port, as your MTA would:

sh
printf 'LHLO t\r\nMAIL FROM:<[email protected]>\r\nRCPT TO:<[email protected]>\r\nDATA\r\nSubject: hi\r\n\r\nhello\r\n.\r\nQUIT\r\n' \
  | nc 127.0.0.1 24

Read it back over IMAPS — with the self-signed certificate the verify warning is expected:

sh
printf 'a login [email protected] changeit\r\nb select INBOX\r\nc logout\r\n' \
  | openssl s_client -quiet -connect 127.0.0.1:993

Listening ports ​

ServiceHost portNotes
IMAP / IMAPS143 / 993login proxy, STARTTLS / implicit TLS
POP3 / POP3S110 / 995login proxy
Submission587 / 465login proxy
ManageSieve4190login proxy
JMAP8443login proxy, HTTPS
LMTP24 (loopback)unauthenticated — for your MTA only
SASL auth12345 (loopback)Postfix smtpd_sasl_type = dovecot
Quota policy12340 (loopback)Postfix check_policy_service

TLS ​

For local use, ./gen-certs.sh <hostname> writes a self-signed tls/cert.pem and tls/key.pem. The key is world-readable so the uid-1000 container can load it.

For production, drop a real certificate and key (e.g. Let's Encrypt fullchain.pem / privkey.pem) into deploy/compose/tls/ as cert.pem / key.pem and restart the login proxies. The tls/ directory is git-ignored.

Fronting MTA (Postfix) ​

Inbound mail reaches yarilo over LMTP (port 24); outbound is your MTA's job.

Wire Postfix to yarilo:

  • Deliver local recipients: mailbox_transport = lmtp:inet:127.0.0.1:24
  • SMTP AUTH against yarilo: smtpd_sasl_type = dovecot, smtpd_sasl_path = inet:127.0.0.1:12345
  • Reject over-quota recipients: check_policy_service inet:127.0.0.1:12340 in smtpd_recipient_restrictions
  • Mail that Sieve sends (redirect, vacation, notify, report): set sieve.sieve_submission_host to your MTA. The shipped localhost:25 points inside the container, where no MTA listens, so those messages fail until it is changed.

::: note For a public deployment you also need MX, SPF, DKIM (signed at the MTA) and PTR records. :::

Verifying ​

Check that every container reports healthy:

sh
docker compose ps

Check the IMAP/POP3/Submission/ManageSieve greetings with openssl s_client or nc, or run the smoke test. The entrypoint dispatches on YARILO_COMPONENT, so invoke the binary directly:

sh
docker compose exec yarilo-imap \
  yarilo-smoketest -host yarilo-imap-login -imap-port 10993 \
  -telemetry http://yarilo-imap-login:8080 -insecure=true

Check the operator API. Once the user exists, this answers with the user's home, mail paths and namespaces:

sh
docker compose exec yarilo-backend-api yarctl backend user info [email protected]

Operations ​

To follow logs of any service:

sh
docker compose logs -f yarilo-imap

Set LOG_LEVEL=debug in .env for verbose output.

To upgrade, bump YARILO_TAG in .env, then:

sh
docker compose pull && docker compose up -d

To back up, snapshot the mail (messages + indexes) and state (userdb) volumes.

For the admin CLI, run yarctl inside yarilo-backend-api. Its environment already points yarctl backend … at the API and carries the token:

sh
docker compose exec yarilo-backend-api yarctl backend ...

Migrate mailbox formats with yarilo-migrate.

Troubleshooting ​

SymptomCheck
a container is unhealthydocker compose logs <svc>; is tls/cert.pem present and readable?
client connects but gets no greetingthe matching login proxy AND its backend are up; both share the compose network
auth always failsuser row exists with enabled=1; password has a {SCHEME} prefix
0 messages for a userthe mail column driver (maildir/sdbox/mdbox) matches how mail was delivered
DNS / network is unreachable between servicesrecreate cleanly: docker compose down && docker compose up -d (stale network)