Home / Alt manpages / virtual(8postfix)

  • virtual(8postfix)
  • Postfix admin command
  • linux

Set Up a Postfix Virtual Mailbox and Alias Safely

You will finish with one Postfix virtual mailbox delivered as a Maildir, plus an alias that points at it. The examples use [email protected] and store mail below /var/mail/vhosts. Replace both values before applying anything to a real host.

This guide is based on Postfix 3.8.6, installed from Ubuntu package postfix 3.8.6-1ubuntu0.1. Allow about 20 minutes if Postfix is already installed and the destination domain is under your control. You need root access for the configuration and mailbox ownership steps, a working Postfix installation, and a domain that is already routed to this server.

Safety boundary

The configuration and reload commands change mail service behaviour. Take a copy of the existing files first, and test during a maintenance window. The commands below do not send a test message, so they will not prove that DNS, SMTP acceptance or outbound delivery is correct.

1. Keep the two virtual features separate

The virtual(8) delivery agent writes mail to a mailbox selected by the full recipient address. It does not provide forwarding or out-of-office processing. The virtual(5) table is a separate rewrite layer, applied by Postfix before mail is queued. An alias changes a recipient into one or more recipients; a virtual mailbox is where a final recipient is stored.

That distinction prevents a common error: adding an address to an alias map does not create a mailbox. The destination must either be another valid recipient or a separately configured mailbox.

Checkpoint

Confirm the installed binary and version. These are read-only commands and do not need elevated privileges:

$ dpkg -L postfix | grep '/virtual$'
/usr/lib/postfix/sbin/virtual
$ postconf -h mail_version
3.8.6

2. Choose the mailbox layout

A virtual mailbox path is the value returned by virtual_mailbox_maps joined to virtual_mailbox_base. When that path ends in /, virtual(8) uses qmail Maildir format: one message per file under the Maildir directories. A path without the trailing slash is a Unix mailbox file containing multiple messages and requires locking during delivery.

This guide uses Maildir because individual message files are easier to inspect and do not need application-level locking. Create a dedicated numeric account and group for virtual mailboxes if your hosting design does not already have them. Do not reuse an unrelated login account merely to make a numeric map value convenient.

As root, create the example directory and its Maildir children. This changes ownership and creates directories, so confirm the path first:

# install -d -o vmail -g vmail -m 0750 /var/mail/vhosts/[email protected]/{cur,new,tmp}
# stat -c '%U:%G %a %n' /var/mail/vhosts/[email protected] /var/mail/vhosts/[email protected]/{cur,new,tmp}
vmail:vmail 750 /var/mail/vhosts/[email protected]
vmail:vmail 750 /var/mail/vhosts/[email protected]/cur
vmail:vmail 750 /var/mail/vhosts/[email protected]/new
vmail:vmail 750 /var/mail/vhosts/[email protected]/tmp

The account name and displayed permissions are examples, not requirements from the manpage. Use the account and policy already approved for your host. The important checks are that the directories exist, the delivery UID and GID can write them, and untrusted users cannot read them.

3. Back up the Postfix files

Before editing configuration, save the current files with root privileges. The timestamped copies give you a direct recovery path if a reload exposes a mistake:

# install -m 0600 /etc/postfix/main.cf /etc/postfix/main.cf.before-virtual-mailbox
# install -m 0600 /etc/postfix/master.cf /etc/postfix/master.cf.before-virtual-mailbox
# cp -p /etc/postfix/virtual /etc/postfix/virtual.before-virtual-mailbox 2>/dev/null || true

If you need to undo this guide, restore the relevant copy with install, remove only the map entries you added, rebuild the affected map, and run postfix check before reloading. Do not delete a mailbox directory as part of a routine rollback: it may already contain mail.

4. Add the mailbox maps

Create or edit the files named by these settings. The left side of the mailbox map is the complete address. The right side is relative to virtual_mailbox_base and ends in / to select Maildir delivery:

# /etc/postfix/main.cf
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_domains = example.test
virtual_mailbox_maps = hash:/etc/postfix/virtual_mailboxes
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_alias_maps = hash:/etc/postfix/virtual

# /etc/postfix/virtual_mailboxes
[email protected] [email protected]/

Replace 5000 with the numeric UID and GID used by your virtual-mailbox account. Check them with id, then make sure the directory ownership from step 2 matches. The virtual_minimum_uid default is 100, so a deliberately low system UID can be rejected even when the directory permissions look correct.

Privileged change: edit the files with root privileges, then build the indexed mailbox map:

# id vmail
# postmap /etc/postfix/virtual_mailboxes
# postmap -q '[email protected]' /etc/postfix/virtual_mailboxes
[email protected]/

The query should return the relative path and nothing else. If it is empty, stop here. Check the spelling, the map filename and the generated .db file before reloading Postfix.

5. Add one alias without creating a wildcard

Use the virtual alias table for a specific forwarding rule. This example sends mail addressed to [email protected] to the mailbox configured above:

# /etc/postfix/virtual
[email protected] [email protected]

Blank lines and lines whose first non-whitespace character is # are ignored. A recipient can map to several addresses by separating them with commas. The mapping changes the envelope recipient, not the message headers. Rebuild the alias map and query it before reloading:

# postmap /etc/postfix/virtual
# postmap -q '[email protected]' /etc/postfix/virtual
[email protected]

Do not add an @example.test wildcard casually. The virtual(5) manpage warns that it accepts mail for every recipient in that domain, including nonexistent users, which can create backscatter. Prefer explicit addresses until you have a deliberate recipient-validation design.

6. Check the complete configuration and reload

Ask Postfix to validate its configuration before changing the running service:

# postfix check
# postconf -n | grep -E '^(virtual_|strict_mailbox_ownership)'
# postfix reload

postfix check prints nothing on success on this installation. The second command should show the virtual settings you intend to use. Reloading makes the running service pick up the change; the delivery agent also rereads main.cf as its short-lived processes start, but an explicit reload avoids waiting for that turnover.

If validation fails, do not reload. Correct the reported setting, rerun postfix check, and query both maps again. If a reload causes unexpected behaviour, restore the backed-up configuration, rebuild any changed map, run the check, and reload once more.

7. Verify delivery without guessing

First inspect the service log while arranging a controlled test message from a permitted sender. The exact log command depends on your host, but Postfix records delivery problems through syslog or postlogd. For a message that reaches the mailbox, check the Maildir rather than assuming SMTP acceptance means local delivery succeeded:

# find /var/mail/vhosts/[email protected]/new -maxdepth 1 -type f -printf '%f\n'
# postqueue -p

A new filename under new is evidence that the message reached the Maildir. An empty new directory is not by itself a diagnosis: the message may still be queued, the test may have been rejected earlier, or a client may have moved it to cur. Use the queue output and log entry together.

Remember the lookup order. For virtual(8), an address extension such as [email protected] is checked first, then the base address, then @example.test. For an indexed virtual(5) map, the address and extension forms are also searched in order. A surprising match is often an older, more specific row rather than a failed reload.

Done means

  • virtual_mailbox_maps returns a path ending in / for the full mailbox address.
  • The joined base path exists as a Maildir and is writable by the configured UID and GID.
  • The alias map returns its intended destination and contains no accidental wildcard.
  • postfix check passes before the service is reloaded.
  • A controlled message is visible in new or cur, and the queue and logs explain any delay.
  • Your backup files remain available, and rollback does not delete stored mail.