diff --git a/src/content/docs/ispmail-trixie/156-overview.mdx b/src/content/docs/ispmail-trixie/156-overview.mdx
new file mode 100644
index 0000000..dfe5b97
--- /dev/null
+++ b/src/content/docs/ispmail-trixie/156-overview.mdx
@@ -0,0 +1,24 @@
+---
+title: Big picture
+lastUpdated: 2025-08-24
+slug: ispmail-trixie/big-picture
+sidebar:
+ order: 156
+---
+
+import "@splidejs/splide/css";
+import { Splide, SplideSlide } from "astro-splide";
+
+I have created a slideshow to help you understand the process of receiving an email from a remote mail server. Hopefully
+it helps to get a better understanding of the steps involved in receiving email. We will prepare the necessary
+configuration in the next chapters.
+
+Use the arrows to navigate between slides:
+
+
+ {Array.from({ length: 24 }, (_, i) => i + 1).map((i) => (
+
+
+
+ ))}
+
diff --git a/src/content/docs/ispmail-trixie/157-dns.mdx b/src/content/docs/ispmail-trixie/157-dns.mdx
index a041d43..da064b4 100644
--- a/src/content/docs/ispmail-trixie/157-dns.mdx
+++ b/src/content/docs/ispmail-trixie/157-dns.mdx
@@ -8,32 +8,12 @@ sidebar:
import { Aside } from "@astrojs/starlight/components";
-import "@splidejs/splide/css";
-import { Splide, SplideSlide } from "astro-splide";
-
import { Steps } from "@astrojs/starlight/components";
import Box from "../../../components/Box.astro";
import StepListReceive from "../../../components/StepListReceive.astro";
-I have created a slideshow to help you understand the process of receiving an email from a remote mail server. Use the
-arrows to navigate between slides:
-
-
- {Array.from({ length: 24 }, (_, i) => i + 1).map((i) => (
-
-
-
- ))}
-
-
----
-
-Let's tackle the receiving of emails step by step:
-
-## DNS
-
As shown in the slideshow, you need to have proper DNS records set up. Say that you want to receive emails for the
diff --git a/src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx b/src/content/docs/ispmail-trixie/160-postfix.mdx
similarity index 86%
rename from src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx
rename to src/content/docs/ispmail-trixie/160-postfix.mdx
index 80ab197..ec7c254 100644
--- a/src/content/docs/ispmail-trixie/160-receive-emails-postfix.mdx
+++ b/src/content/docs/ispmail-trixie/160-postfix.mdx
@@ -1,7 +1,7 @@
---
-title: "Receive emails: Postfix"
+title: "Postfix"
lastUpdated: 2025-08-20
-slug: ispmail-trixie/receive-emails-postfix
+slug: ispmail-trixie/postfix
sidebar:
order: 160
---
@@ -48,7 +48,7 @@ SELECT "yes" FROM virtual_domains WHERE name='example.net'
Run the following code in your shell to create a configuration file creating that mapping:
```sh
-cat > /etc/postfix/virtual-mailbox-domains.cf << EOF
+cat > /etc/postfix/mariadb-virtual-mailbox-domains.cf << EOF
user = mailserver
password = SECOND-PASSWORD-HERE
hosts = 127.0.0.1
@@ -82,7 +82,7 @@ Apparently, there aren't any more. So the email will go to `john@example.org`.
Run this code to create the appropriate mapping file:
```sh
-cat > /etc/postfix/virtual-alias-maps.cf << EOF
+cat > /etc/postfix/mariadb-virtual-alias-maps.cf << EOF
user = mailserver
password = SECOND-PASSWORD-HERE
hosts = 127.0.0.1
@@ -98,7 +98,7 @@ The query now gets all the `destination` email addresses from the database for a
The last mapping we need is a query to find valid mailboxes. Without further ado:
```sh
-cat > /etc/postfix/virtual-mailbox-maps.cf << EOF
+cat > /etc/postfix/mariadb-virtual-mailbox-maps.cf << EOF
user = mailserver
password = SECOND-PASSWORD-HERE
hosts = 127.0.0.1
@@ -120,12 +120,12 @@ sufficient here.
You have created the config files for the three mappings. Now you just need to tell Postfix to use them:
```sh
-postconf virtual_mailbox_domains=mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
-postconf virtual_mailbox_maps=mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
-postconf virtual_alias_maps=mysql:/etc/postfix/mysql-virtual-alias-maps.cf
+postconf virtual_mailbox_domains=mysql:/etc/postfix/mariadb-virtual-mailbox-domains.cf
+postconf virtual_mailbox_maps=mysql:/etc/postfix/mariadb-virtual-mailbox-maps.cf
+postconf virtual_alias_maps=mysql:/etc/postfix/mariadb-virtual-alias-maps.cf
-chown root:postfix /etc/postfix/*.cf
-chmod o= /etc/postfix/*.cf
+chown root:postfix /etc/postfix/mariadb-*.cf
+chmod o= /etc/postfix/mariadb-*.cf
```
`postconf` is a command that changes configuration in `/etc/postfix/main.cf` and applies them instantly. You don't have
@@ -139,9 +139,9 @@ access. After all a database password is found in these files.
Give the mappings a quick test using the `postmap -q` command (_q_ stands for _query_):
```sh
-postmap -q example.org mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
-postmap -q jack@example.org mysql:/etc/postfix/mysql-virtual-alias-maps.cf
-postmap -q john@example.org mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
+postmap -q example.org mysql:/etc/postfix/mariadb-virtual-mailbox-domains.cf
+postmap -q jack@example.org mysql:/etc/postfix/mariadb-virtual-alias-maps.cf
+postmap -q john@example.org mysql:/etc/postfix/mariadb-virtual-mailbox-maps.cf
```
These three commands use the three mapping files (`*.cf`) to query your three database tables. The result should show:
diff --git a/src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx b/src/content/docs/ispmail-trixie/165-dovecot.mdx
similarity index 86%
rename from src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx
rename to src/content/docs/ispmail-trixie/165-dovecot.mdx
index 4316531..5ef97cc 100644
--- a/src/content/docs/ispmail-trixie/165-deliver-emails-dovecot.mdx
+++ b/src/content/docs/ispmail-trixie/165-dovecot.mdx
@@ -1,7 +1,7 @@
---
-title: "Deliver emails: Dovecot"
-lastUpdated: 2025-08-22
-slug: ispmail-trixie/deliver-emails-dovecot
+title: "Dovecot"
+lastUpdated: 2025-08-24
+slug: ispmail-trixie/dovecot
sidebar:
order: 165
---
@@ -9,19 +9,7 @@ sidebar:
import { Aside } from "@astrojs/starlight/components";
import StepListReceive from "../../../components/StepListReceive.astro";
-## About LMTP
-
-
-
-As explained in the previous section, Postfix speaks SMTP and receives the email from the internet. Postfix could even
-save the email to a mailbox on disk. But instead we will use Dovecot for the final delivery. Actually Dovecot's main
-purpose is to let users fetch their email using the IMAP protocol. But it provides additional features we can use as
-well.
-
-So we need tell Postfix to hand over the incoming email to Dovecot. The communication between Postfix and Dovecot will
-happen using LMTP – the [local mail transfer protocol](https://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol).
-LMTP is a variant of SMTP with fewer features. It is meant for email communication between internal services that trust
-each other.
+
In this chapter we will mainly configure Dovecot so that it knows how to deliver incoming emails.
@@ -75,6 +63,8 @@ located, who owns them and what is their layout? Set this:
mail_driver = maildir
mail_home = /var/vmail/%{user | domain}/%{user | username}
mail_path = ~/Maildir
+mail_uid = vmail
+mail_gid = vmail
```
This looks more sophisticated than with versions of Dovecot before 2.4. What it means:
@@ -87,9 +77,12 @@ This looks more sophisticated than with versions of Dovecot before 2.4. What it
- **mail_path**: Within the _home_ directory the actual mailbox will live in a subdirectory called `Maildir`. The reason
is that we will store other data in the user's _home_ directory as well that should not conflict with the mailbox
directory.
+- **mail_uid**/**mail_gid**/: All mailboxes are stored on disk under the owner _vmail_.
### 10-master.conf
+TODO: not needed. /var/run/dovecot/auth-userdb is created automatically.
+
This configuration file at `/etc/dovecot/conf.d/10-master.conf` deals with Dovecot's services.
So most settings are sane here and do not have to be changed. However one change is required in the “service auth”
@@ -120,7 +113,17 @@ ssl_server_cert_file = /etc/letsencrypt/live/mail.example.org/fullchain.pem
ssl_server_key_file = /etc/letsencrypt/live/mail.example.org/privkey.pem
```
-Next let’s take a look at how Dovecot knows about users and their passwords:
+### 20-lmtp.conf
+
+There is one setting in the `/etc/dovecot/conf.d/20-lmtp.conf` that will mess up the recipient's email address in our
+setup. So please edit this file and comment out the `auth_username_format` line:
+
+```
+protocol lmtp {
+ #auth_username_format = %{user | username}
+}
+
+```
### auth-sql.conf.ext
@@ -142,7 +145,7 @@ userdb sql {
}
passdb sql {
- query = SELECT password FROM virtual_usrs where email='%{user}'
+ query = SELECT password FROM virtual_users where email='%{user}'
}
```
@@ -220,8 +223,8 @@ That should give you:
```
field value
-uid
-gid
+uid vmail
+gid vmail
home /var/vmail/example.org/john
mail_path /var/vmail/example.org/john/Maildir
```
diff --git a/src/content/docs/ispmail-trixie/167-lmtp.mdx b/src/content/docs/ispmail-trixie/167-lmtp.mdx
new file mode 100644
index 0000000..8158f65
--- /dev/null
+++ b/src/content/docs/ispmail-trixie/167-lmtp.mdx
@@ -0,0 +1,155 @@
+---
+title: LMTP
+lastUpdated: 2025-08-24
+slug: ispmail-trixie/lmtp
+sidebar:
+ order: 167
+---
+
+import { Aside } from "@astrojs/starlight/components";
+import StepListReceive from "../../../components/StepListReceive.astro";
+
+
+
+Glad to see that you are still with me. We are very close to receiving our first email. If you feel lost, please review
+the [slideshow](/ispmail-trixie/big-picture/) from earlier in this guide.
+
+As explained in the previous section, Postfix speaks SMTP and receives the email from the internet. Postfix could even
+save the email to a mailbox on disk. But instead we will use Dovecot for the final delivery. Actually Dovecot's main
+purpose is to let users fetch their email using the IMAP protocol. But it provides additional features we can use as
+well.
+
+So we need tell Postfix to hand over the incoming email to Dovecot. The communication between Postfix and Dovecot will
+happen using LMTP – the [local mail transfer protocol](https://en.wikipedia.org/wiki/Local_Mail_Transfer_Protocol).
+LMTP is a variant of SMTP with fewer features. It is meant for email communication between internal services that trust
+each other.
+
+## Dovecot listens to LMTP
+
+There's nothing to do for you here. I just want to tell you how it works. By default Dovecot has this section in its
+`/etc/dovecot/conf.d/10-master.conf` file:
+
+```
+service lmtp {
+ unix_listener lmtp {
+ }
+}
+```
+
+So Dovecot has already put a socket into `/var/run/dovecot/lmtp` where it is ready to receive LMTP connections. A socket
+is similar to a TCP port but it's only available for other processes running on same system. So nothing to do.
+
+## Postfix talks to Dovecot using LMTP
+
+The “_virtual_transport_” in Postfix defines the service to use for delivering emails to the local system. Dovecot has
+created a socket file and is ready to listen to incoming LMTP connections. We just need to tell Postfix to send emails
+there. So please run:
+
+```sh
+postconf virtual_transport=lmtp:unix:/var/run/dovecot/lmtp
+```
+
+The syntax looks crazy, but it’s actually simple. You just told Postfix to use the LMTP protocol. And that we want to
+use a UNIX socket on the same system (instead of a TCP connection). And the socket file is located at
+`/var/run/dovecot/lmtp`.
+
+## Enable server-side mail rules
+
+TODO: move to optional chapter
+
+## Send a test mail locally
+
+Finally we are ready for our first email. Using the `swaks` command you can create a test email to `john@example.org`
+and send it to Postfix:
+
+```sh
+swaks --server localhost --to john@example.org
+```
+
+If all goes as planned, you will see an SMTP dialog like this:
+
+```
+=== Trying localhost:25...
+=== Connected to localhost.
+<- 220 mail ESMTP Postfix (Debian)
+ -> EHLO mail
+<- 250-mail
+<- 250-PIPELINING
+<- 250-SIZE 10240000
+<- 250-VRFY
+<- 250-ETRN
+<- 250-STARTTLS
+<- 250-ENHANCEDSTATUSCODES
+<- 250-8BITMIME
+<- 250-DSN
+<- 250-SMTPUTF8
+<- 250 CHUNKING
+ -> MAIL FROM:
+<- 250 2.1.0 Ok
+ -> RCPT TO:
+<- 250 2.1.5 Ok
+ -> DATA
+<- 354 End data with .
+ -> Date: Sun, 24 Aug 2025 21:08:59 +0000
+ -> To: john@example.org
+ -> From: root@mail
+ -> Subject: test Sun, 24 Aug 2025 21:08:59 +0000
+ -> Message-Id: <20250824210859.021444@mail>
+ -> X-Mailer: swaks v20240103.0 jetmore.org/john/code/swaks/
+ ->
+ -> This is a test mailing
+ ->
+ ->
+ -> .
+<- 250 2.0.0 Ok: queued as 2742B221FA
+ -> QUIT
+<- 221 2.0.0 Bye
+=== Connection closed with remote host.
+```
+
+If you see a `250 2.0.0 Ok: queued as…" near the end of this dialog, then Postfix has received the email successfully.
+
+Check if Postfix managed to pass the email to Dovecot:
+
+```sh
+journalctl -n 10 -u dovecot
+```
+
+The line you are looking for looks like:
+
+```
+Aug 24 21:36:22 mail dovecot[23214]: lmtp(john@example.org)<…><…>: msgid=<20250824213622.023221@mail>: saved mail to INBOX
+```
+
+Does it say `saved mail to INBOX`? Awesome. The email should be visible in John's home directory. Check it:
+
+```sh
+find /var/vmail/example.org/john
+```
+
+Does it look like this?
+
+```
+/var/vmail/
+/var/vmail/example.org
+/var/vmail/example.org/john
+/var/vmail/example.org/john/Maildir
+/var/vmail/example.org/john/Maildir/maildirfolder
+/var/vmail/example.org/john/Maildir/new
+/var/vmail/example.org/john/Maildir/new/1756071382.M198387P23222.mail,S=635,W=655
+/var/vmail/example.org/john/Maildir/cur
+/var/vmail/example.org/john/Maildir/tmp
+/var/vmail/example.org/john/Maildir/dovecot-uidvalidity.68ab85d6
+/var/vmail/example.org/john/Maildir/dovecot.index.log
+/var/vmail/example.org/john/Maildir/dovecot-uidvalidity
+/var/vmail/example.org/john/Maildir/dovecot.index.cache
+/var/vmail/example.org/john/Maildir/dovecot-uidlist
+/var/vmail/example.org/john/Maildir/dovecot.list.index.log
+```
+
+The important file here is `…/Maildir/new/1756071382.M198387P23222.mail,S=635,W=655`. The numbers will look differently
+on your system. But this is the actual mail that got delivered. You can view it with `cat` and will find the test email
+you just sent using `swaks`.
+
+Don't worry about the other files like `maildirfolder` or the files that sound like `dovecot…`. Those help Dovecot keep
+track of the mailbox. You can safely ignore them.