Tracing the path of an incoming mail message is one of the fundamental ways in which one diagnoses one's qmail setup. This requires understanding exactly what qmail does with each message. This information is all contained within the documentation, but a lot of newbies seem to have trouble grasping it all.

If you're wondering how qmail works at a very high level, you should start by looking at "the big picture" at http://www.nrg4u.com/ -- this will help you get a bird's eye view of what's going on.

If you don't understand any of the concepts in this document (like envelopes), please look at http://wooledge.org/~greg/mail.html first.

Ultimately, qmail is a set of running processes and configuration files. The first process in which we're interested when tracking incoming mail is qmail-smtpd.

qmail-smtpd is the qmail SMTP daemon, which listens on port 25 for incoming mail. Actually, it's tcpserver (or possibly inetd, or possibly xinetd) that listens on port 25, and which spawns a new instance of qmail-smtpd every time a new connection is established. That's an important distinction, because qmail-smtpd reads its configuration files every time it's started. Since a new qmail-smtpd is started on every connection, that means you never need to restart anything just because you changed qmail-smtpd's configuration files.

If you haven't patched qmail, then there's really only one configuration file for qmail-smtpd that you need to worry about right now: rcpthosts. This file contains your local (and virtual) domain names, one per line. qmail-smtpd reads this and compares the incoming mail's envelope recipients (one by one) to the contents of rcpthosts. Any recipient whose domain is not listed in rcpthosts is rejected. Any recipient whose domain is listed, is accepted.

If you've applied patches to qmail-smtpd for things like SMTP AUTH, then they may be relevant here; but that's beyond the scope of this page.

When qmail-smtpd has accepted the message, it invokes qmail-queue to put the message into the queue. It's a new message, so it hasn't been sorted into its category yet. qmail-send will do that within a few seconds, hopefully.

qmail-send looks at the message recipients, and for each recipient, it categorizes whether this is a local destination or a remote destination. It uses two config files to decide this: locals and virtualdomains. locals is just a list of domain names, one per line. locals is consulted before virtualdomains, and it has priority! If the recipient's domain is listed in locals, then it's a local destination.

If the recipient's domain is not listed in locals, but is listed in virtualdomains (which has a slightly more complex syntax), then it's still a local delivery, but first, the address is rewritten. We'll get to that in a moment.

If the recipient's domain isn't in either of these files, it's a remote destination, and the message gets chucked into the outgoing bin. (qmail-remote will handle that one. We won't worry about outgoing mail for now.)

Once we know a message is local, it gets chucked into the local bin. When that bin is processed, the only thing we need to worry about is the username. Why is that? Because all of the virtual domain magic has already been taken care of during the rewriting process!

Example: let's suppose we have an incoming message for <bob@vdom.com>. tcpserver answers the phone, fires up qmail-smtpd, which reads rcpthosts. vdom.com is listed in rcpthosts, so qmail-smtpd accepts the message. It calls qmail-queue, which queues it up.

qmail-send looks in the "new message" bin, and sees this one. It's for someone at vdom.com, which is not in locals. So it looks in virtualdomains, and sees this line:

vdom.com:tammy

So this is a virtual domain. And furthermore, it belongs to a user named tammy. qmail-send rewrites the message envelope, effectively saying "this message is for tammy-bob". And it sticks the message into the "local delivery" bin.

When qmail-lspawn sees the message in the local deliver bin, it looks for the username, and sees "tammy-bob". Now, the hyphen ("-") is special here -- it separates the actual username (tammy) from the extension (bob). So we really look for a user named tammy.

Qmail looks for users according to a very well-defined procedure. First, it looks in /var/qmail/users/cdb which is generated from /var/qmail/users/assign (a human-readable text file). The details of the format of this file are a bit tricky -- see the man pages (man qmail-users and man qmail-getpw).

If we don't see tammy in users/cdb then we'll ask the operating system whether tammy is a user.

If tammy isn't a user in either of those places, the message is given to the user alias.