Virtual Mailbox Administration

Posted on 13 2026

The source material this page replaces covers ViMbAdmin: a PHP-based virtual mailbox administration tool that was positioned as a modern alternative to PostfixAdmin. The situation has reversed. ViMbAdmin’s last commit was in 2022 and the project shows no signs of continued development. Installing abandoned software on a mail server is not a good idea. PostfixAdmin is actively maintained, has a large community, and integrates cleanly with Postfix and Dovecot.

This page covers PostfixAdmin as the replacement for ViMbAdmin. The database schema is compatible with what the rest of the mail server section expects, and the Postfix and Dovecot query files reference the same table structure.

What PostfixAdmin does

PostfixAdmin is a web-based administration interface for managing:

  • Virtual domains: the domains for which the mail server accepts and delivers mail
  • Mailboxes: individual mail accounts with passwords and quotas
  • Aliases: address redirections, including catch-all addresses
  • Domain admins: accounts that can manage a subset of domains without superadmin access
  • Vacation/autoresponder: out-of-office message configuration per mailbox

It does not handle Postfix or Dovecot configuration files. Those are managed separately. PostfixAdmin manages the database that Postfix and Dovecot query when routing mail.

Container setup

PostfixAdmin runs in its own LXC container, separate from the Postfix and Dovecot containers. It is a PHP application served by nginx and php-fpm.

Clone the base container template and configure for PostfixAdmin:

# On the Proxmox host
pct clone 100 120 --hostname mail-admin --full
pct start 120

Inside the container, update the hostname:

hostnamectl set-hostname mail-admin.yourdomain.net
sed -i 's/base-template/mail-admin/g' /etc/hosts

Prerequisites

sudo apt install -y \
    nginx \
    php8.3-fpm \
    php8.3-imap \
    php8.3-mbstring \
    php8.3-mysql \
    php8.3-xml \
    php8.3-intl \
    mariadb-client

Database

PostfixAdmin stores its data in a MariaDB database. The mail server’s MariaDB instance is in a separate database container. Create the PostfixAdmin database and user:

On the database server:

CREATE DATABASE postfixadmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'postfixadmin'@'10.1.0.x' IDENTIFIED BY 'strong-password-here';
GRANT ALL ON postfixadmin.* TO 'postfixadmin'@'10.1.0.x';

-- Read-only user for Postfix and Dovecot queries
CREATE USER 'mailserver'@'10.1.0.x' IDENTIFIED BY 'different-strong-password';
GRANT SELECT ON postfixadmin.* TO 'mailserver'@'10.1.0.x';
FLUSH PRIVILEGES;

Replace 10.1.0.x with the IP addresses of the PostfixAdmin container, Postfix container, and Dovecot container respectively. Each service should connect with only the permissions it needs.

Installation

Download the latest PostfixAdmin release:

cd /tmp
curl -L https://github.com/postfixadmin/postfixadmin/archive/refs/tags/postfixadmin-3.3.13.tar.gz \
    -o postfixadmin.tar.gz
tar -xzf postfixadmin.tar.gz
sudo mv postfixadmin-postfixadmin-3.3.13 /var/www/postfixadmin
sudo chown -R www-data:www-data /var/www/postfixadmin

Check the latest release version at https://github.com/postfixadmin/postfixadmin/releases before downloading.

Create the templates cache directory:

sudo mkdir -p /var/www/postfixadmin/templates_c
sudo chown www-data:www-data /var/www/postfixadmin/templates_c

Configuration

Create the PostfixAdmin configuration file:

sudo tee /var/www/postfixadmin/config.local.php << 'EOF'
<?php

// PostfixAdmin configuration
// See config.inc.php for all available options

// Database connection
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = '10.1.0.x';  // database server address
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'strong-password-here';
$CONF['database_name'] = 'postfixadmin';
$CONF['database_port'] = '3306';

// PostfixAdmin URL
$CONF['postfix_admin_url'] = 'https://mail-admin.yourdomain.net';

// Encryption for mailbox passwords
// Use SHA512-CRYPT to match Dovecot's expected scheme
$CONF['encrypt'] = 'SHA512-CRYPT';

// Mail server hostnames shown in setup wizard emails
$CONF['smtp_server'] = 'mail.yourdomain.net';
$CONF['smtp_port'] = '587';
$CONF['imap_server'] = 'mail.yourdomain.net';
$CONF['imap_port'] = '993';

// Minimum password length
$CONF['min_password_length'] = 12;

// Default quotas (in MB, 0 = unlimited)
$CONF['maxquota'] = 4096;  // 4GB default maximum
$CONF['quota'] = 'YES';

// Domain defaults
$CONF['domain_quota'] = 'YES';
$CONF['aliases'] = 50;
$CONF['mailboxes'] = 25;
$CONF['maxquota'] = 4096;

// Show footer
$CONF['show_footer_text'] = 'NO';

// Logging
$CONF['admin_email'] = 'admin@yourdomain.net';

// Setup password (used only for first-time superadmin creation)
// Generate with: php -r "echo password_hash('yoursetuppassword', PASSWORD_DEFAULT);"
$CONF['setup_password'] = 'generated-hash-here';

// Disable vacation (handled separately)
$CONF['vacation'] = 'NO';

EOF

sudo chown www-data:www-data /var/www/postfixadmin/config.local.php
sudo chmod 640 /var/www/postfixadmin/config.local.php

Generate the setup password hash:

php -r "echo password_hash('your-setup-password', PASSWORD_DEFAULT) . PHP_EOL;"

Replace generated-hash-here in the config with the output. Store the plaintext setup password in KeePassXC.

nginx configuration

Create /etc/nginx/sites-available/postfixadmin:

server {
    listen 80;
    server_name mail-admin.yourdomain.net;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name mail-admin.yourdomain.net;

    ssl_certificate /etc/ssl/certs/mail-admin.yourdomain.net.crt;
    ssl_certificate_key /etc/ssl/private/mail-admin.yourdomain.net.key;

    # Internal access only - restrict to internal subnets
    allow 10.0.0.0/8;
    deny all;

    root /var/www/postfixadmin/public;
    index index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT $realpath_root;
    }

    location ~ /\. {
        deny all;
    }
}

Note the allow 10.0.0.0/8; deny all; block: PostfixAdmin should only be accessible from the internal network, never from the public internet. Enable the site:

sudo ln -s /etc/nginx/sites-available/postfixadmin /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

First-time setup

Navigate to the setup URL from the internal network:

https://mail-admin.yourdomain.net/setup.php

PostfixAdmin runs the setup check and creates the database schema automatically. If all checks pass, create the first superadmin account using the setup password configured above.

Once the superadmin is created, the setup URL is no longer needed. The setup page can be disabled by removing or renaming public/setup.php:

sudo mv /var/www/postfixadmin/public/setup.php \
    /var/www/postfixadmin/public/setup.php.disabled

First domain and mailbox

Log in to PostfixAdmin at https://mail-admin.yourdomain.net with the superadmin account.

Add your domain: Domain List > Add Domain. Enter yourdomain.net, set mailbox and alias limits, enable the domain.

Add your first mailbox: Virtual List > Add Mailbox. Enter the full email address, set a strong password, configure the quota.

Add required aliases: Virtual List > Add Alias. RFC requirements include:

AliasDestination
abuse@yourdomain.netadmin@yourdomain.net
postmaster@yourdomain.netadmin@yourdomain.net
hostmaster@yourdomain.netadmin@yourdomain.net
webmaster@yourdomain.netadmin@yourdomain.net

These aliases must exist for RFC compliance and are checked by many mail servers and spam filters.

Postfix query files

With PostfixAdmin’s database populated, configure Postfix to query it. These query files go in the Postfix container at /etc/postfix/sql/.

/etc/postfix/sql/virtual-domains.cf:

hosts = 10.1.0.x
user = mailserver
password = different-strong-password
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'

/etc/postfix/sql/virtual-mailboxes.cf:

hosts = 10.1.0.x
user = mailserver
password = different-strong-password
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'

/etc/postfix/sql/virtual-aliases.cf:

hosts = 10.1.0.x
user = mailserver
password = different-strong-password
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'

/etc/postfix/sql/virtual-email2email.cf:

hosts = 10.1.0.x
user = mailserver
password = different-strong-password
dbname = postfixadmin
query = SELECT username FROM mailbox WHERE username='%s' AND active = '1'

Dovecot query configuration

In the Dovecot container, configure password and user lookups against the PostfixAdmin database.

/etc/dovecot/dovecot-sql.conf.ext:

driver = mysql
connect = host=10.1.0.x dbname=postfixadmin user=mailserver password=different-strong-password

default_pass_scheme = SHA512-CRYPT

password_query = \
    SELECT username AS user, password \
    FROM mailbox \
    WHERE username = '%u' AND active = '1'

user_query = \
    SELECT \
        CONCAT('/var/vmail/', maildir) AS home, \
        'maildir:/var/vmail/' || maildir AS mail, \
        5000 AS uid, \
        5000 AS gid, \
        CONCAT('*:bytes=', quota) AS quota_rule \
    FROM mailbox \
    WHERE username = '%u' AND active = '1'

iterate_query = SELECT username AS user FROM mailbox WHERE active = '1'

Backing up PostfixAdmin

The PostfixAdmin database contains all domain, mailbox, and alias configuration. It must be included in the backup strategy.

Add a database dump to the container’s anacron daily job:

cat > ~/.anacron/cron.daily/backup-postfixadmin << 'EOF'
#!/usr/bin/env bash
mysqldump -h 10.1.0.x -u postfixadmin -p'strong-password-here' postfixadmin \
    | gzip > /var/backups/postfixadmin-$(date +%Y%m%d).sql.gz
find /var/backups -name 'postfixadmin-*.sql.gz' -mtime +7 -delete
EOF
chmod 0700 ~/.anacron/cron.daily/backup-postfixadmin

PostfixAdmin manages the database, not the mail itself. Deleting a mailbox in PostfixAdmin removes the database record but does not remove the mail stored on disk. Clean up the /var/vmail/ directory on the Dovecot server when removing mailboxes if storage recovery is needed.