---
# nocheck: seaweedfs-required β object storage not implemented for this role
sso:
bond: 1
enabled: "{{ 'web-app-keycloak' in group_names }}"
shared: "{{ 'web-app-keycloak' in group_names }}"
flavor: oidc
oidc:
email_by_username: true
enable_user_creation: true
logout:
bond: 1
enabled: "{{ 'web-svc-logout' in group_names }}"
shared: "{{ 'web-svc-logout' in group_names }}"
dashboard:
bond: 1
enabled: "{{ 'web-app-dashboard' in group_names }}"
shared: "{{ 'web-app-dashboard' in group_names }}"
matomo:
bond: 1
enabled: "{{ 'web-app-matomo' in group_names }}"
shared: "{{ 'web-app-matomo' in group_names }}"
# nocheck: dynamic-flag
email:
bond: 1
enabled: "{{ MAIL_PROVIDER in group_names }}"
shared: "{{ MAIL_PROVIDER in group_names }}"
# nocheck: playwright-service-flag β self-provider
mailu:
modes:
compose:
enabled: true
swarm:
enabled: true
bond: 1
# nocheck: default-placement-manager mail edge at a known IP: MX A-record, PTR and SPF bind inbound/outbound SMTP to the stack host IP (a floating task's egress IP breaks deliverability); postfix /queue and dovecot mail+index state are NFS-hostile and ARE the app (no central-service equivalent); cert renewals land in a node-local bind via a manager-side systemd timer
placement: manager
enabled: true # nocheck: dynamic-flag
shared: true
covers:
- email
# nocheck: unwatched-version Reason: Mailu ships its images under the release-series tag while its git tags are patch level (2024.06.58), so the series is the pin and moves only on a deliberate migration
version: "2024.06"
name: mailu
backup:
project_hard_restart: true
min_storage: 20GB
domain: "{{ lookup('email').domain }}"
dmarc:
rua_user: administrator
ports:
local:
http: 8007
public:
smtp: 25
smtps: 465
submission: 587
pop3: 110
pop3s: 995
imap: 143
imaps: 993
sieve: 4190
internal:
http: 80
smtp: 25
smtps: 465
submission: 587
pop3: 110
pop3s: 995
imap: 143
imaps: 993
sieve: 4190
onion:
smtp: true
submission: true
imap: true
pop3: true
# nocheck: onion-flag ManageSieve edits server-side filters, which is administration rather than the sending and receiving the onion transport is for
sieve: false
proxy:
service_key: front
run_after:
- web-app-matomo
- web-app-stalwart
lifecycle: eol
cpus: "1.0"
mem_reservation: 512m
mem_limit: 2g
pids_limit: 1024
# nocheck: shared
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β DB engine, covered by role-local integration tests
redis:
bond: 1
name: mailu-redis
backup:
disabled: true
enabled: true # nocheck: dynamic-flag
# Exception: central redis overlay adds VIP pressure on manager-pinned mailu; embedded redis avoids it.
shared: "{{ ('svc-db-redis' in group_names) and (DEPLOYMENT_MODE == 'compose') }}"
cpus: "0.2"
mem_reservation: 256m
mem_limit: 512m
pids_limit: 256
# nocheck: playwright-service-flag - central DNS resolver, infrastructural (no Playwright surface)
# nocheck: dynamic-flag enabled+shared (not the `in group_names` form) marks svc-dns-unbound `required` so the loader deploys it every compose round; reverting reintroduces the admin DNS hang.
unbound:
bond: 1
enabled: "{{ DEPLOYMENT_MODE == 'compose' }}"
shared: true
# nocheck: playwright-service-flag β DB engine, covered by role-local integration tests
mariadb:
bond: 1
enabled: true # nocheck: dynamic-flag
shared: "{{ 'svc-db-mariadb' in group_names }}"
cpus: "0.8"
mem_reservation: 1g
mem_limit: 2g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
admin:
name: mailu-admin
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.3"
mem_reservation: 256m
mem_limit: 2g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
imap:
name: mailu-imap
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.5"
mem_reservation: 512m
mem_limit: 1.5g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
smtp:
name: mailu-smtp
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.5"
mem_reservation: 512m
mem_limit: 1.5g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
antispam:
name: mailu-antispam
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.6"
mem_reservation: 512m
mem_limit: 1.5g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
antivirus:
name: mailu-antivirus
enabled: true # nocheck: dynamic-flag
shared: false
image: clamav/clamav-debian
version: 1.5.4
cpus: "0.6"
mem_reservation: 2g
mem_limit: 3g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
oletools:
name: mailu-oletools
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.2"
mem_reservation: 256m
mem_limit: 512m
pids_limit: 256
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
webdav:
name: mailu-webdav
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.2"
mem_reservation: 256m
mem_limit: 512m
pids_limit: 256
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
fetchmail:
name: mailu-fetchmail
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.2"
mem_reservation: 256m
mem_limit: 512m
pids_limit: 256
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
webmail:
name: mailu-webmail
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.3"
mem_reservation: 256m
mem_limit: 1g
pids_limit: 512
# nocheck: dynamic-flag
# nocheck: playwright-service-flag β internal sub-component
front:
name: mailu-front
enabled: true # nocheck: dynamic-flag
shared: false
cpus: "0.3"
mem_reservation: 256m
mem_limit: 1g
pids_limit: 512
# nocheck: playwright-service-flag β verified by web-svc-css provider spec
css:
bond: 1
enabled: "{{ 'web-svc-css' in group_names }}"
shared: "{{ 'web-svc-css' in group_names }}"
prometheus:
bond: 1
enabled: "{{ 'web-app-prometheus' in group_names }}"
shared: "{{ 'web-app-prometheus' in group_names }}"
# nocheck: playwright-service-flag
# nocheck: variants-coverage β sso-variant-tor requires the onion on in every variant, so the false polarity has no variant left to live in
tor:
bond: 0.5
enabled: "{{ 'svc-net-tor' in group_names }}"
shared: true # nocheck: dynamic-flag
# nocheck: dynamic-flag
# nocheck: playwright-service-flag, dynamic-flag infrastructure backup consumer
container_backup: # nocheck: bond-baseline backup host joins outside the variant topology
bond: 1
enabled: "{{ 'svc-bkp-volume-2-local' in group_names }}"
shared: true