maxipass.at
    [2026-01-31 Sat 19:05]
    I wanted to see if it's possible to host a mail server from home on IPv6 (spoiler: nearly).

    I added the following records to my DNS config:

    #+begin_example @ MX 10800 10 mail.maxipass.at. mail AAAA 10800 2a01:e0a:b5a:de71::1#+end_example
    Certbot is happy to generate a certificate for mail.
    Config firewall to open TCP 25.
    Then exim can't read certs because it's not root. That's ok kitnil has the solution for that, a hook on cert bot:

    #+begin_src scheme (define exim-deploy-hook (program-file "exim-deploy-hook" #~(begin (unless (file-exists? "/etc/exim") (mkdir "/etc/exim")) (let* ((cert-directory (getenv "RENEWED_LINEAGE")) (user (getpw "exim")) (uid (passwd:uid user)) (gid (passwd:gid user))) (copy-file (string-append cert-directory "/" (readlink (string-append cert-directory "/fullchain.pem"))) "/etc/exim/exim.crt") (copy-file (string-append cert-directory "/" (readlink (string-append cert-directory "/privkey.pem"))) "/etc/exim/exim.pem") (chown "/etc/exim" uid gid) (chown "/etc/exim/exim.crt" uid gid) (chown "/etc/exim/exim.pem" uid gid) (invoke #$(file-append psmisc "/bin/killall") #$(file-append exim-content-scan "/bin/exim")))))) ;; ... which is then used like so: (service certbot-service-type (certbot-configuration (certificates (list (certificate-configuration (domains '("mail.maxipass.at")) (deploy-hook exim-deploy-hook)) ;; <- here (certificate-configuration (domains '("maxipass.at" "www.maxipass.at")))))))#+end_src
    Next exim's email routing failed because it still isn't running as root and it is installed without setuid binaries so it can't gain root privileges. Which is good for security, but means we have to disable some functionality, namely checking a user's ~/.forward file before delivering email.

    #+begin_src conf userforward: driver = redirect check_local_user data = "" no_verify no_expn check_ancestor file_transport = address_file pipe_transport = address_pipe reply_transport = address_reply#+end_src
    Then I setup dovecot with local lmtp delivery.
    Exim config looks like this:

    #+begin_src conf local_user: debug_print = "R: local_user for $local_part@$domain" driver = accept domains = +local_domains check_local_user transport = dovecot_lmtp cannot_route_message = Unknown user # ... dovecot_lmtp: driver = smtp #allow suffixes/prefixes (default unset) rcpt_include_affixes protocol = lmtp allow_localhost = true # <- this is important hosts = 127.0.0.1 port = 2525#+end_src
    Dovecot service is a bit verbose but not complex, just make sure the lmtp ports match on both sides:

    #+begin_src scheme (service dovecot-service-type (dovecot-configuration (mail-location "maildir:~/.mail") (services (list (service-configuration (kind "imap-login") (client-limit 0) (process-limit 0) (listeners (list (inet-listener-configuration (protocol "imaps") (port 993) (ssl? #t))))) (service-configuration (kind "lmtp") (client-limit 1) (process-limit 0) (listeners (list (inet-listener-configuration (protocol "lmtp") (port 2525) (ssl? #f))))) (service-configuration (kind "auth") (service-count 0) (client-limit 0) (process-limit 1) (listeners (list (unix-listener-configuration (path "auth-userdb"))))) (service-configuration (kind "auth-worker") (client-limit 1) (process-limit 0)) (service-configuration (kind "dict") (client-limit 1) (process-limit 0) (listeners (list (unix-listener-configuration (path "dict"))))))) ;; (mail-debug? #t) <-- helpful when mucking around (auth-username-format "%n") (protocols (list (protocol-configuration (name "lmtp")) (protocol-configuration (name "imap"))))))#+end_src
    At this point I was able to receive email 🥳

    Let's try to send some. I setup DMARC, SPF & DKIM:

    #+begin_src shell openssl genrsa -out dkim.private.key 2048 openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM#+end_src
    btw now is a good time to mention that exim doc is very complete and you should refer to it every step of the way: 58. DKIM, SPF, SRS and DMARC
    My DMARC, SPF & DKIM records in DNS:

    #+begin_example @ TXT 10800 "v=spf1 mx -all" _dmarc TXT 10800 "v=DMARC1; p=quarantine; rua=mailto:webmaster@maxipass.at" default._domainkey TXT 10800 "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxG7jjFxmeSpkHQMQ2fKbMG2KTrGGELVT9G1n6rWRKVgYxMXKpf8FkJRDLqsg0f9cf1KcRlWtvDxb0X" "VZmjVz9Xe2mQw4YRMhdb4HtI27BJU6cuJ5MLRnQoj4aEpf1Qg8POFlVYSYuqpBprTXOH+BddmHSmIHQ4VhHuxqmQjn9i7fKdSofYX6PuQj9EoMvK5MTztnRlGYPJKphbwE2youqZxW/wtdT+Z/ucFR" "pWiB86IXmwHGHUSp24uzGcZIOR5HP3Sh7mfJ/vQNPkYStxruuA4zPiyyPfgAo/x0w5oQlkk0JCEznSMrEzLcT+MPbQFJwTZ5s9srVuxEhvi5X8t8iwIDAQAB"#+end_example
    DKIM is added to exim like so:

    #+begin_src conf remote_smtp: driver = smtp dkim_domain = maxipass.at dkim_selector = default dkim_private_key = /etc/exim/dkim.private.key dkim_canon = relaxed .ifdef _HAVE_TLS_RESUME tls_resumption_hosts = * .endif#+end_src
    Aaaand all of this fails to send email because free (my internet provider) won't let you add PTR record on IPv6 and without one gmail & co just drop your email :'( This would work if you just wanted to interact with other self-hosted folk.

    [2026-04-18 Sat 23:25]
    Fallback to IPv4 then because free will let you add a PTR record for that.
    So maxipass.at now has IPv6 & IPv4 web records, but mail is only advertised on IPv4 so it can get through to gmail & co.

    I replaced the AAAA record with this one:

    #+begin_example mail.maxipass.at. 10800 IN A 82.65.216.189#+end_example
    Then I had to convince exim to use only the local IPv4 that is forwarded as my public IP address by my router when sending email:

    #+begin_src conf local_interfaces = <; 127.0.0.1 ; \ 192.168.1.101 ; \ # public facing IP 10.42.0.7 # my vpn # ... dnslookup: driver = dnslookup domains = ! +local_domains transport = remote_smtp ipv4_only = true # <- (sad ipv6 noizes) ignore_target_hosts = <; 0.0.0.0 ; 127.0.0.0/8 ; ::1 no_more # ... remote_smtp: driver = smtp dkim_domain = maxipass.at dkim_selector = default dkim_private_key = /etc/exim/dkim.private.key dkim_canon = relaxed .ifdef _HAVE_TLS_RESUME tls_resumption_hosts = * .endif interface = 192.168.1.101 # <- this (also add this to smarthost_smtp if needed)#+end_src
    And now gmail deigns to talk to us.
    You can dig through of-course-i-still-love-you.scm, mail.scm & exim.conf for the final result.

    You can also send me an email so I didn't do all of this for nothing: webmaster@maxipass.at