Directory Layout

In the following documentation, the variable ${COURIER_HOME}is set to Courier's installation directory, and ${ARCH} is set to the target platform type, such as "i586-linux-gnu":

${COURIER_HOME} is the main installation directory, everything lives under here.
${COURIER_HOME}/ARCH is a soft link to ${ARCH}
${COURIER_HOME}/${ARCH}/bin - binary files
${COURIER_HOME}/${ARCH}/lib - library files
${COURIER_HOME}/bin - mostly soft links to arch/bin
${COURIER_HOME}/lib - mostly soft links to arch/lib
${COURIER_HOME}/man - manual pages
${COURIER_HOME}/local/config - local configuration
${COURIER_HOME}/local/msgs - mail queue - messages and control files
${COURIER_HOME}/local/msgq - mail queue - control files, sorted by next scheduled delivery attempt time
${COURIER_HOME}/local/tmp - temporary directory

All directories can be softlinks, or mount points, EXCEPT that local/msg[sq] and local/tmp must be on the same filesystem.

Ownership and permissions.

This documentation assumes that Courier is installed as user 'mail', group 'mail'. Unless otherwise specified below, everything is owned by Courier's user/group. Global read/execute permissions on everything. Write permissions to user and group.

In local/msg[sq] the userid of the control and the message file are that of the user who created the message.


courierctl scripts - these scripts start and stop courierd, and performs other administrative functions.
courierctl.start scripts - these scripts start courierd. They do NOT start any input module daemons, that must be done separately. Note that this command only begins the startup process. courierd may fail to start for some reason, you will need to check your system logs to verify that courierd is properly running.

courierctl.stop - this command stops courierd. courierd is stopped cold, interrupting any pending deliveries.

courierctl.restart - this command restarts courierd by sending it a SIGHUP. courierd will suspend starting any new deliveries, then wait for all current deliveries to finish, then restart itself. This action is necessary after making certain configuration changes.

courierd - the main scheduling daemon. The heart of Courier is just a raw scheduler that figures out when to attempt to deliver messages that are in the queue. There will actually be two courierd processes running, at the same time. courierd is started and stopped by courierctl.


The courierd scheduling daemon is transport neutral, and external modules are used to submit messages to courierd, and attempt to deliver messages at regular intervals.  The term 'input module' refers to an external module that submits messages to courierd, and 'output module' refers to an external module that delivers messages. The term 'transport module', or a plain 'module' refers to both an input module and an output module for the same platform, which usually come together. For example, Courier typically receives and sends mail via ESMTP, however, it is possible to adjust the default configuration so that ESMTP is only used to receive mail. For sending, mail gets delivered using some other protocol.

Output module are started by courierd, and communicate with courierd via pipes. Output module are notified which messages must be delivered, and to which addresses. Courier may send multiple messages to the same output module, without waiting for an acknowledgement that an attempted delivery for previous messages has been made.

E-mail addresses that appear in the message's headers, and which are listed in the message's delivery envelope, are stored in a "canonical" format. E-mail messages that come from an input module have their addresses rewritten from the transport-specific format to the canonical format. E-mail messages which are sent to be delivered to an output module have their addresses rewritten to the canonical format.

The canonical format is the format for E-mail addresses that is used by Courier's system. Therefore, messages that are delivered to local mailboxes do not undergo header or address rewriting.

Messages are submitted to the Courier server by running the ${COURIER_HOME}/bin/submit, which takes care of rewriting addresses from the transport format to canonical format. When an output module receives a message to deliver from Courier's queue, the E-mail addresses in the message will be in the canonical format, and the output module is responsible for rewriting addresses in the headers into the transport-specific format. The output module can take advantage of a set of library functions which automate this process.

The rewrite library

Each transport module comes with a small library file which provides functions to rewrite addresses between the canonical format and the transport specific format. Also, the library provides a function that examines a canonical E-mail address and indicates if the address can be delivered to by the output module. When attempting to deliver a message, Courier calls each transport module's delivery function, until it finds one that accepts this E-mail address for delivery. Then, Courier notifies the output module to deliver the message to this address.

On platforms that support dynamically-opened shared libraries, Courier can be compiled to use shared libraries. This allows rewriting functions to be modified, and have the changes take effect without fully shutting down Courier. Otherwise, they can be compiled as regular, statically linked libraries.

The submit program

submit is used to submit a message into Courier's scheduling queue. submit is not invoked directly. submit does not have any global read or execute permissions, only user and group permissions, therefore it must be invoked by a process that executes at least as group mail. submit provides the functionality most transports will need in order to submit messages into the queue. Submit accepts several options, and one argument: the name of the input module submitting the message. submit also reads the environment, therefore the environment variables must be scrubbed before invoking submit. submit communicates via standard input and output with the module to read the identify of the sender and the recipients of an E-mail message, and the message contents. submit enforces message size limits and may reject the sender or any recipient. Options are:
-expn=address - do not accept any message, just attempt to 'expand' this address.

-vrfy=address - do not accept any message, just attempt to 'verify' this address.

-verp - use VERP-encoded envelope sender address.

submit opens the shared libraries, reads the E-mail addresses of the sender and the recipient, reads the message itself, and rewrites all addresses to the canonical format by running the module's address rewriting function in the shared library. submit may perform other adjustments, such as adding the Date: or the Message-ID: header, or adding MIME headers. submit also checks if any recipient is a system-defined alias, and adds the alias's list of addresses to the recipient list. submit will also try to remove duplicate E-mail addresses from the recipient list, wherever possible.  Wherever possible, submit will reject E-mail addresses that it knows are not valid.

mailq - display current mail queue for the running user (or the entire queue, if invoked as root). Since individual control files are owned by the originating user. mailq simply prints out the contents of every control file it can open.

-s - sort messages by starting time. With many messages, this can eat up memory, and CPU cycles. The list is not sorted, by default.
reformime - standalone program that can be used by module to implement some header rewriting. I am borrowing the reformime tool from the maildrop package, which contains complete documentation for reformime.

That's it. Other program modules come as a part of plug-in transport module. For example, the SMTP transport doesn't have to be installed, resulting in Courier being used to just deliver mail on a single system. Or, only the SMTP output module can be installed, so that the machine will be able to send mail via SMTP, but not receive it. Or, only the SMTP input module can be installed, so that the machine can receive mail via SMTP, but not send it (which also means that bounces can't be delivered, so they double-bounce to postmaster).

Environment variables used by submit

The submit program also reads the following variables from the environment:

SPAMDATAFILTER=pathname - run this spam filter. After receiving this message, run the indicated program. In addition to the environment variables inherited by submit, submit sets MODULE to the name of the input module, before running the spam filter. Please note that the program is run with the user/group id of the submit process!

MIME=option - sets options for mime rewriting. The options are: none - no mime rewriting whatsoever; 7bit - convert 8bit mime sections to quoted-printable; 8bit - convert quoted-printable to 8bit.

NOADDMSGID - if set, submit will not add a Message-Id: header, if the message does not have it.

NOADDDATE - if set, submit will not add a Date: header, if the message doesn't have it.

SIZELIMIT - maximum size of a message in bytes. Larger messages are rejected. If this environment variable is not set, Courier will read the control/sizelimit file. If control/sizelimit does not exist, Courier will use "0". "0" is used to specify that there is no maximum message size limit.


Each message in the queue is represented by two files, the control file, and the message file. The message file contains the actual message, headers and body. The message is stored in the 'local' format, that is, the addresses in the headers are rewritten into the format used by the local system. The control file contains ancillary information about the message: the sender, the recipient, and its current status. The control file is called "Cnnnn" or "Cnnnn.tttttt", and the message file is called "Mnnnn". In both cases, nnnn represents the inode number of the control file, so that the inherent uniqueness of inodes is used to generate unique filenames. 'tttttt' is the time when the the next delivery attempt for the message is due. tttttt is the message's next scheduled attempted delivery date, expressed as number of seconds used by the time() system call. More on that later.