---
id: Complete Mail Server Setup Guide
aliases: []
tags: []
---

# Complete Mail Server Setup Guide - Arch Linux Edition

## Postfix + Dovecot + MySQL + Notmuch + Encryption on Arch Linux LXC

This is the Arch Linux version of the complete mail server setup.

---

## Part 1: Proxmox LXC Container Setup for Arch Linux

### Download Arch Linux Template

In Proxmox shell:

```bash
pveam update
pveam available | grep arch
pveam download local archlinux-base_20231118-1_amd64.tar.zst
```

Or download manually from: https://uk.lxd.images.canonical.com/images/archlinux/current/amd64/default/

### Create the LXC Container

In Proxmox UI:

- **CT ID**: Choose available ID
- **Template**: archlinux-base
- **Disk**: 20GB minimum
- **CPU**: 2 cores
- **RAM**: 2GB minimum
- **Network**: Bridge, static IP (e.g., 192.168.1.100)
- **Unprivileged**: Yes
- **Features**: `nesting=1` (if you want Docker option later)

Start the container and note the IP address.

### Initial Container Setup

SSH into your Arch LXC container:

```bash
# Initialize pacman keyring (Arch specific)
pacman-key --init
pacman-key --populate archlinux

# Update system
pacman -Syu --noconfirm

# Install essential tools
pacman -S --noconfirm base-devel vim wget curl git sudo openssh

# Set hostname
hostnamectl set-hostname mail.example.com
echo "192.168.1.100 mail.example.com mail" >> /etc/hosts

# Enable time sync
systemctl enable --now systemd-timesyncd
```

### Configure Locale

```bash
# Uncomment your locale in /etc/locale.gen
vim /etc/locale.gen
# Uncomment: en_US.UTF-8 UTF-8

# Generate locale
locale-gen

# Set locale
echo "LANG=en_US.UTF-8" > /etc/locale.conf
```

---

## Part 2: MySQL/MariaDB Database Setup

### Install MariaDB

```bash
pacman -S --noconfirm mariadb mariadb-clients
```

### Initialize MariaDB

```bash
# Initialize the database
mariadb-install-db --user=mysql --basedir=/usr --datadir=/var/lib/mysql

# Start and enable MariaDB
systemctl enable --now mariadb

# Secure installation
mysql_secure_installation
```

During secure installation:

- Set root password: Yes (choose strong password)
- Remove anonymous users: Yes
- Disallow root login remotely: Yes
- Remove test database: Yes
- Reload privilege tables: Yes

### Create Mail Database and Tables

```bash
mysql -u root -p
```

In MySQL prompt:

```sql
CREATE DATABASE mailserver CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

CREATE USER 'mailuser'@'localhost' IDENTIFIED BY '1ChagemaiL';

GRANT ALL PRIVILEGES ON mailserver.* TO 'mailuser'@'localhost';

FLUSH PRIVILEGES;

USE mailserver;

-- Domains table
CREATE TABLE IF NOT EXISTS virtual_domains (
    id INT NOT NULL AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Users table
CREATE TABLE IF NOT EXISTS virtual_users (
    id INT NOT NULL AUTO_INCREMENT,
    domain_id INT NOT NULL,
    password VARCHAR(200) NOT NULL,
    email VARCHAR(120) NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY email (email),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Aliases table
CREATE TABLE IF NOT EXISTS virtual_aliases (
    id INT NOT NULL AUTO_INCREMENT,
    domain_id INT NOT NULL,
    source VARCHAR(100) NOT NULL,
    destination VARCHAR(100) NOT NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (domain_id) REFERENCES virtual_domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- Add your domain
INSERT INTO virtual_domains (name) VALUES ('liphlink.xyz');

-- Verify
SELECT * FROM virtual_domains;

EXIT;
```

**Save your database password securely!**

---

## Part 3: Postfix Installation and Configuration

### Install Postfix

```bash
pacman -S --noconfirm postfix postfix-mysql
```

### Backup Original Configuration

```bash
cp /etc/postfix/main.cf /etc/postfix/main.cf.orig
```

### Configure Postfix Main Settings

Edit `/etc/postfix/main.cf`:

```bash
vim /etc/postfix/main.cf
```

Replace with:

```
# Basic Settings
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain
mydestination = localhost.$mydomain, localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = ipv4

# Compatibility with Dovecot
compatibility_level = 3.6

# Virtual Mailbox Settings
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

# Virtual transport
virtual_transport = lmtp:unix:private/dovecot-lmtp

# Mailbox Location
virtual_mailbox_base = /var/mail/vhosts
virtual_minimum_uid = 100
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000

# SMTP-AUTH Settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname
broken_sasl_auth_clients = yes

# TLS Settings
smtpd_tls_cert_file=/etc/letsencrypt/live/mail.example.com/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.example.com/privkey.pem
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtpd_tls_security_level = may
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_ciphers = high
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_mandatory_ciphers = high

smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

# SMTP Restrictions
smtpd_recipient_restrictions =
    permit_sasl_authenticated,
    permit_mynetworks,
    reject_unauth_destination,
    reject_invalid_hostname,
    reject_non_fqdn_hostname,
    reject_non_fqdn_sender,
    reject_non_fqdn_recipient,
    reject_unknown_sender_domain,
    reject_unknown_recipient_domain,
    reject_rbl_client zen.spamhaus.org,
    reject_rbl_client bl.spamcop.net,
    permit

smtpd_helo_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_invalid_helo_hostname,
    reject_non_fqdn_helo_hostname,
    reject_unknown_helo_hostname,
    permit

smtpd_sender_restrictions =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_non_fqdn_sender,
    reject_unknown_sender_domain,
    permit

# Milter settings (for DKIM)
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters

# Additional Settings
disable_vrfy_command = yes
smtpd_helo_required = yes
message_size_limit = 52428800
```

### Create MySQL Connection Files

**1. Virtual Domains**

```bash
vim /etc/postfix/mysql-virtual-mailbox-domains.cf
```

```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_domains WHERE name='%s'
```

**2. Virtual Mailboxes**

```bash
vim /etc/postfix/mysql-virtual-mailbox-maps.cf
```

```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM virtual_users WHERE email='%s'
```

**3. Virtual Aliases**

```bash
vim /etc/postfix/mysql-virtual-alias-maps.cf
```

```
user = mailuser
password = your_strong_password_here
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM virtual_aliases WHERE source='%s'
```

### Secure MySQL Configuration Files

```bash
chmod 640 /etc/postfix/mysql-*.cf
chown root:postfix /etc/postfix/mysql-*.cf
```

### Configure Postfix Master.cf

Edit `/etc/postfix/master.cf`:

```bash
vim /etc/postfix/master.cf
```

Add these lines at the end:

```
submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_helo_restrictions=
  -o smtpd_sender_restrictions=
  -o smtpd_recipient_restrictions=
  -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
```

### Create Mail Directory Structure

```bash
# Create vmail user and group
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /var/mail/vhosts -m -s /bin/bash

# Create mail directory
mkdir -p /var/mail/vhosts/example.com
chown -R vmail:vmail /var/mail/vhosts
chmod -R 770 /var/mail/vhosts
```

### Test MySQL Connectivity

```bash
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
```

Should return `1` if working.

### Initialize Postfix Aliases

```bash
newaliases
postmap /etc/postfix/virtual
```

---

## Part 4: Dovecot Installation and Configuration

### Install Dovecot

```bash
pacman -S --noconfirm dovecot pigeonhole
```

### Main Dovecot Configuration

Edit `/etc/dovecot/dovecot.conf`:

```bash
vim /etc/dovecot/dovecot.conf
```

Replace with:

```
protocols = imap lmtp sieve
listen = *, ::
dict {
}

!include conf.d/*.conf
!include_try local.conf
```

### Configure Mail Location

Edit `/etc/dovecot/conf.d/10-mail.conf`:

```bash
vim /etc/dovecot/conf.d/10-mail.conf
```

Set these values:

```
mail_location = maildir:/var/mail/vhosts/%d/%n
mail_privileged_group = vmail

namespace inbox {
  inbox = yes

  mailbox Drafts {
    special_use = \Drafts
  }
  mailbox Junk {
    special_use = \Junk
  }
  mailbox Trash {
    special_use = \Trash
  }
  mailbox Sent {
    special_use = \Sent
  }
}

first_valid_uid = 5000
last_valid_uid = 5000
first_valid_gid = 5000
last_valid_gid = 5000
```

### Configure Authentication

Edit `/etc/dovecot/conf.d/10-auth.conf`:

```bash
vim /etc/dovecot/conf.d/10-auth.conf
```

Modify:

```
disable_plaintext_auth = yes
auth_mechanisms = plain login

# Comment out
#!include auth-system.conf.ext

# Uncomment
!include auth-sql.conf.ext
```

### SQL Authentication Backend

Edit `/etc/dovecot/conf.d/auth-sql.conf.ext`:

```bash
vim /etc/dovecot/conf.d/auth-sql.conf.ext
```

```
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}
```

### SQL Connection Configuration

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

```bash
vim /etc/dovecot/dovecot-sql.conf.ext
```

```
driver = mysql
connect = host=127.0.0.1 dbname=mailserver user=mailuser password=your_strong_password_here

default_pass_scheme = SHA512-CRYPT

password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

user_query = SELECT email as user, 'maildir:/var/mail/vhosts/%d/%n' as mail, 5000 AS uid, 5000 AS gid FROM virtual_users WHERE email='%u';

iterate_query = SELECT email AS username FROM virtual_users;
```

Secure it:

```bash
chown root:root /etc/dovecot/dovecot-sql.conf.ext
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
```

### Master Services Configuration

Edit `/etc/dovecot/conf.d/10-master.conf`:

```bash
vim /etc/dovecot/conf.d/10-master.conf
```

Configure services:

```
service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

service pop3-login {
  inet_listener pop3 {
    port = 110
  }
  inet_listener pop3s {
    port = 995
    ssl = yes
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

service imap {
}

service pop3 {
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }

  unix_listener auth-userdb {
    mode = 0600
    user = vmail
    group = vmail
  }

  user = dovecot
}

service auth-worker {
  user = vmail
}

service dict {
  unix_listener dict {
  }
}
```

### SSL Configuration

Edit `/etc/dovecot/conf.d/10-ssl.conf`:

```bash
vim /etc/dovecot/conf.d/10-ssl.conf
```

```
ssl = required
ssl_cert = </etc/letsencrypt/live/mail.example.com/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.example.com/privkey.pem

ssl_min_protocol = TLSv1.2
ssl_cipher_list = ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
ssl_prefer_server_ciphers = yes
```

### Sieve Configuration

Edit `/etc/dovecot/conf.d/90-sieve.conf`:

```bash
vim /etc/dovecot/conf.d/90-sieve.conf
```

```
plugin {
  sieve = file:~/sieve;active=~/.dovecot.sieve
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
}
```

### Set Permissions

```bash
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
```

---

## Part 5: SSL Certificates with Let's Encrypt

### Install Certbot

```bash
pacman -S --noconfirm certbot
```

### Obtain Certificate

Ensure ports 80 and 443 are accessible:

```bash
certbot certonly --standalone -d mail.example.com
```

Follow prompts and enter your email.

### Set Up Auto-Renewal

Create systemd timer:

```bash
systemctl enable --now certbot-renew.timer
```

Create renewal hook:

```bash
mkdir -p /etc/letsencrypt/renewal-hooks/post
vim /etc/letsencrypt/renewal-hooks/post/reload-mail-services.sh
```

```bash
#!/bin/bash
systemctl reload postfix
systemctl reload dovecot
```

Make executable:

```bash
chmod +x /etc/letsencrypt/renewal-hooks/post/reload-mail-services.sh
```

---

## Part 6: OpenDKIM Setup

### Install OpenDKIM

```bash
pacman -S --noconfirm opendkim
```

### Configure OpenDKIM

Edit `/etc/opendkim/opendkim.conf`:

```bash
vim /etc/opendkim/opendkim.conf
```

```
AutoRestart             Yes
AutoRestartRate         10/1h
UMask                   002
Syslog                  yes
SyslogSuccess           Yes
LogWhy                  Yes

Canonicalization        relaxed/simple

ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable

Mode                    sv
PidFile                 /run/opendkim/opendkim.pid
SignatureAlgorithm      rsa-sha256

UserID                  opendkim:mail

Socket                  inet:8891@localhost
```

### Create Directory Structure

```bash
mkdir -p /etc/opendkim/keys/example.com
chown -R opendkim:mail /etc/opendkim
chmod -R 700 /etc/opendkim/keys
```

# Create Configuration Files

**TrustedHosts**:

```bash
vim /etc/opendkim/TrustedHosts
```

```
127.0.0.1
localhost
192.168.1.0/24
*.example.com
```

**KeyTable**:

```bash
vim /etc/opendkim/KeyTable
```

```
mail._domainkey.example.com example.com:mail:/etc/opendkim/keys/example.com/mail.private
```

**SigningTable**:

```bash
vim /etc/opendkim/SigningTable
```

```
*@example.com mail._domainkey.example.com
```

### Generate DKIM Keys

```bash
cd /etc/opendkim/keys/liphlink.xyz
opendkim-genkey -s mail -d liphlink.xyz
chown opendkim:mail mail.private
chmod 600 mail.private
```

### Get Public Key for DNS

```bash
cat /etc/opendkim/keys/liphlink.xyz/mail.txt
```

Save this for your DNS records.

### Start OpenDKIM

```bash
systemctl enable --now opendkim
```

---

## Part 7: Start and Enable Services

### Start All Services

```bash
# Create postfix spool directory for dovecot
mkdir -p /var/spool/postfix/private
chown postfix:postfix /var/spool/postfix/private

# Start services
systemctl enable --now postfix
systemctl enable --now dovecot
systemctl enable --now opendkim

# Check status
systemctl status postfix
systemctl status dovecot
systemctl status opendkim
```

All should show `active (running)`.

---

## Part 8: Notmuch Integration

### Install Notmuch

```bash
pacman -S --noconfirm notmuch
```

### Configure Notmuch for vmail User

```bash
# Switch to vmail user
su - vmail
cd /var/mail/vhosts/liphlink.xyz

# Create user directory if doesn't exist
mkdir -p phil
cd phil

# Initialize notmuch
notmuch setup
```

Prompts:

- Your full name: User Name
- Primary email: user@example.com
- Maildir location: /var/mail/vhosts/example.com/user
- Tags for new messages: unread;inbox
- Tags to exclude: deleted;spam

Index existing mail:

```bash
notmuch new
exit  # Exit vmail user
```

### Create Indexing Script

```bash
vim
```

```bash
#!/bin/bash

for domain in /var/mail/vhosts/*; do
    if [ -d "$domain" ]; then
        for user in "$domain"/*; do
            if [ -d "$user" ]; then
                cd "$user" || continue

                if [ ! -d ".notmuch" ]; then
                    su -s /bin/bash vmail -c "cd '$user' && notmuch new"
                else
                    su -s /bin/bash vmail -c "cd '$user' && notmuch new"
                fi
            fi
        done
    fi
done
```

Make executable:

```bash
chmod +x /usr/local/bin/notmuch-index-all.sh
```

### Set Up Automatic Indexing

Create systemd service:

```bash
vim /etc/systemd/system/notmuch-index.service
```

```ini
[Unit]
Description=Notmuch email indexing
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/notmuch-index-all.sh
User=root

[Install]
WantedBy=multi-user.target
```

Create timer:

```bash
vim /etc/systemd/system/notmuch-index.timer
```

```ini
[Unit]
Description=Run notmuch indexing every 5 minutes

[Timer]
OnBootSec=2min
OnUnitActiveSec=5min

[Install]
WantedBy=timers.target
```

Enable:

```bash
systemctl daemon-reload
systemctl enable --now notmuch-index.timer
```

### Configure Dovecot for Real-time Indexing

```bash
vim /usr/local/bin/notmuch-index-single
```

```bash
#!/bin/bash
if [ -n "$HOME" ]; then
    cd "$HOME" || exit 0
    /usr/bin/notmuch new > /dev/null 2>&1
fi
```

Make executable:

```bash
chmod +x /usr/local/bin/notmuch-index-single
```

Create global sieve:

```bash
mkdir -p /var/mail/vhosts/sieve
vim /var/mail/vhosts/sieve/after.sieve
```

```
require ["vnd.dovecot.pipe", "copy", "environment"];
pipe :copy "notmuch-index-single";
```

Compile:

```bash
sievec /var/mail/vhosts/sieve/after.sieve
chown -R vmail:vmail /var/mail/vhosts/sieve
```

Update Dovecot config:

```bash
vim /etc/dovecot/conf.d/90-sieve.conf
```

Add:

```
plugin {
  sieve = file:~/sieve;active=~/.dovecot.sieve
  sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
  sieve_after = /var/mail/vhosts/sieve/after.sieve
}
```

Restart:

```bash
systemctl restart dovecot
```

---

## Part 9: Email Encryption with Dovecot Mail-Crypt

### Install Required Packages

```bash
# Already included in dovecot package on Arch
pacman -S --noconfirm openssl
```

### Generate Master Encryption Keys

```bash
mkdir -p /var/lib/dovecot/mail-crypt
cd /var/lib/dovecot/mail-crypt

# Generate EC keys
openssl ecparam -name prime256v1 -genkey | openssl pkey -out ecprivkey.pem
openssl pkey -in ecprivkey.pem -pubout -out ecpubkey.pem

# Set permissions
chown vmail:vmail *.pem
chmod 400 *.pem
```

### Configure Mail-Crypt Plugin

Create `/etc/dovecot/conf.d/90-mail-crypt.conf`:

```bash
vim /etc/dovecot/conf.d/90-mail-crypt.conf
```

```
mail_plugins = $mail_plugins mail_crypt

plugin {
  mail_crypt_global_private_key = </var/lib/dovecot/mail-crypt/ecprivkey.pem
  mail_crypt_global_public_key = </var/lib/dovecot/mail-crypt/ecpubkey.pem
  mail_crypt_save_version = 2
  mail_crypt_curve = secp521r1
  mail_crypt_private_password = %w
}
```

Update main mail config:

```bash
vim /etc/dovecot/conf.d/10-mail.conf
```

Find `mail_plugins` and add `mail_crypt`:

```
mail_plugins = mail_crypt
```

Restart Dovecot:

```bash
systemctl restart dovecot
```

### Install PGP/GPG Tools

```bash
pacman -S --noconfirm gnupg
```

### PGP Key Management Script

```bash
vim /usr/local/bin/mail-pgp-manager
```

```bash
#!/bin/bash

USER_EMAIL="$1"
ACTION="$2"

GNUPG_HOME="/var/mail/vhosts/$(echo $USER_EMAIL | cut -d'@' -f2)/$(echo $USER_EMAIL | cut -d'@' -f1)/.gnupg"

case "$ACTION" in
    generate)
        mkdir -p "$GNUPG_HOME"
        chmod 700 "$GNUPG_HOME"
        chown vmail:vmail "$GNUPG_HOME"

        su - vmail -c "gpg --homedir $GNUPG_HOME --batch --gen-key <<EOF
Key-Type: RSA
Key-Length: 4096
Subkey-Type: RSA
Subkey-Length: 4096
Name-Real: $USER_EMAIL
Name-Email: $USER_EMAIL
Expire-Date: 0
%no-protection
%commit
EOF"
        ;;

    export-public)
        su - vmail -c "gpg --homedir $GNUPG_HOME --armor --export $USER_EMAIL"
        ;;

    list)
        su - vmail -c "gpg --homedir $GNUPG_HOME --list-keys"
        ;;

    *)
        echo "Usage: $0 <email> <generate|export-public|list>"
        exit 1
        ;;
esac
```

Make executable:

```bash
chmod +x /usr/local/bin/mail-pgp-manager
```

---

## Part 10: Creating Email Accounts

### Generate Password Hash

```bash
doveadm pw -s SHA512-CRYPT
```

Enter your password. Copy the hash.

### Add User to Database

```bash
mysql -u root -p mailserver
```

```sql
-- Get domain ID
SELECT id FROM virtual_domains WHERE name='liphlink.xyz';

-- Add user (replace hash with your generated hash)
INSERT INTO virtual_users (domain_id, password, email)
VALUES (1, '{SHA512-CRYPT}$6$OWM2H5QRTyR5qLky$n1lDTBB7CcHpMNNtXGe4pwC.OH9/hA6Gen3pwWr9R5AoboJuqg9P/gl..WQXuy82cGizNy3WDEzRNWnevqfbx0', 'phil@liphlink.xyz');

-- Verify
SELECT * FROM virtual_users;

EXIT;
```

```sql
-- Get domain ID
SELECT id FROM virtual_domains WHERE name='liphlink.xyz';

-- Add user (replace hash with your generated hash)
INSERT INTO virtual_users (domain_id, password, email)
VALUES (1, '{SHA512-CRYPT}$6$5ZnV6ungZJDq00k4$UhNVKgXoJp1AvfTMnqyDFtyECG4/5UNd.ZPlQP9OVdeOpgjssJwP0qaOWCoP0AHjL7/zAsgUR4GIY/YczK2hB1', 'liph@liphlink.xyz');

-- Verify
SELECT * FROM virtual_users;

EXIT;
```

### Create Maildir for User

```bash
mkdir -p /var/mail/vhosts/liphlink.xyz/liph/Maildir/{cur,new,tmp}
chown -R vmail:vmail /var/mail/vhosts/liphlink.xyz/liph
chmod -R 770 /var/mail/vhosts/liphlink.xyz/liph
```

---

## Part 11: Firewall Configuration

### Install and Configure UFW

```bash
pacman -S --noconfirm ufw

# Allow SSH first!
ufw allow 22/tcp

# Mail server ports
ufw allow 25/tcp    # SMTP
ufw allow 587/tcp   # Submission
ufw allow 465/tcp   # SMTPS
ufw allow 993/tcp   # IMAPS
ufw allow 143/tcp   # IMAP
ufw allow 80/tcp    # HTTP (Let's Encrypt)
ufw allow 443/tcp   # HTTPS

# Enable
ufw enable
systemctl enable ufw
```

---

## Part 12: Security Hardening with Fail2ban

### Install Fail2ban

```bash
pacman -S --noconfirm fail2ban
```

### Configure Fail2ban

```bash
vim /etc/fail2ban/jail.local
```

```ini
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5

[postfix-sasl]
enabled = true
port = smtp,submission,smtps
logpath = /var/log/mail.log
filter = postfix-sasl

[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,smtps
logpath = /var/log/mail.log
filter = dovecot
```

Enable and start:

```bash
systemctl enable --now fail2ban
```

---

## Part 13: Testing Everything

### Test Postfix

```bash
# Check config
postfix check

# Test MySQL lookups
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
postmap -q user@example.com mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
```

### Test Dovecot Authentication

```bash
doveadm auth test user@example.com
```

### Send Test Email

```bash
# Install mail utility
pacman -S --noconfirm s-nail

# Send test
echo "Test email" | mail -s "Test" phil@liphlink.xyz

# Check logs
journalctl -u postfix -f
journalctl -u dovecot -f
```

ALTER USER 'mailuser'@'localhost' IDENTIFIED BY '1ChagemaiL';
FLUSH PRIVILEGES;
EXIT;

### Test IMAP

```bash
openssl s_client -connect localhost:993 -crlf
```

Then:

```
a LOGIN user@example.com password
b SELECT INBOX
c LOGOUT
```

### Test Notmuch

```bash
su - vmail
cd /var/mail/vhosts/example.com/user
notmuch search tag:inbox
notmuch search from:sender@example.com
exit
```

---

## Part 14: Monitoring and Logs

### View Logs

```bash
# Postfix logs
journalctl -u postfix -f

# Dovecot logs
journalctl -u dovecot -f

# All mail logs
journalctl -u postfix -u dovecot -f

# OpenDKIM logs
journalctl -u opendkim -f
```

### Check Service Status

```bash
systemctl status postfix dovecot opendkim mariadb
```

### Check Mail Queue

```bash
postqueue -p
mailq
```

---

## Part 15: Backup Script

```bash
vim /usr/local/bin/mail-backup.sh
```

```bash
#!/bin/bash

BACKUP_DIR="/root/mail-backup"
DATE=$(date +%Y-%m-%d)

mkdir -p "$BACKUP_DIR"

# Backup configs
tar -czf "$BACKUP_DIR/postfix-$DATE.tar.gz" /etc/postfix/
tar -czf "$BACKUP_DIR/dovecot-$DATE.tar.gz" /etc/dovecot/
tar -czf "$BACKUP_DIR/opendkim-$DATE.tar.gz" /etc/opendkim/

# Backup database
mysqldump -u root -p mailserver > "$BACKUP_DIR/mailserver-$DATE.sql"

# Backup mail (optional - can be large)
# tar -czf "$BACKUP_DIR/vmail-$DATE.tar.gz" /var/mail/vhosts/

# Keep only last 7 days
find "$BACKUP_DIR" -type f -mtime +7 -delete

echo "Backup completed: $DATE"
```

Make executable:

```bash
chmod +x /usr/local/bin/mail-backup.sh
```

Schedule with cron or systemd timer.

---

## Arch Linux Specific Notes

### Package Management

```bash
# Update system
pacman -Syu

# Search packages
pacman -Ss package-name

# Install package
pacman -S package-name

# Remove package
pacman -R package-name
```

### AUR Helper (Optional)

For packages not in official repos:

```bash
# Install yay
pacman -S --noconfirm git base-devel
git clone https://aur.archlinux.org/yay.git
cd yay
makepkg -si
```

### Systemd Journal Size

Limit journal size:

```bash
vim /etc/systemd/journald.conf
```

```ini
[Journal]
SystemMaxUse=500M
```

Restart:

```bash
systemctl restart systemd-journald
```

---

## Quick Reference Commands

### User Management

```bash
# Create user with encryption
doveadm pw -s SHA512-CRYPT
mysql -u root -p mailserver -e "INSERT INTO virtual_users VALUES (1, 'HASH', 'user@example.com');"
mail-pgp-manager user@example.com generate
```

### Service Management

```bash
# Restart services
systemctl restart postfix dovecot opendkim

# View logs
journalctl -u postfix -u dovecot --since "1 hour ago"

# Check mail queue
postqueue -p
```

### Notmuch

```bash
# Manual index
/usr/local/bin/notmuch-index-all.sh

# Check timer
systemctl status notmuch-index.timer
```

---

## Troubleshooting

### Services won't start

```bash
# Check for errors
systemctl status servicename
journalctl -xeu servicename

# Check configuration
postfix check
doveconf -n
```

### Permission issues

```bash
# Fix ownership
chown -R vmail:vmail /var/mail/vhosts
chown -R postfix:postfix /var/spool/postfix
chown -R dovecot:dovecot /etc/dovecot

# Fix permissions
chmod -R 770 /var/mail/vhosts
chmod 600 /etc/dovecot/dovecot-sql.conf.ext
```

### Database connection fails

```bash
# Test connection
mysql -u mailuser -p mailserver

# Check postfix mysql maps
postmap -q example.com mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
```

---

## Advantages of Arch Linux for Mail Server

✅ **Rolling release**: Always latest stable versions
✅ **Minimal bloat**: Only what you install
✅ **Great documentation**: Arch Wiki is excellent
✅ **Performance**: Optimized packages
✅ **Control**: Full customization
✅ **SystemD native**: Modern service management
✅ **Fast package manager**: Pacman is very fast

---

## Next Steps

1. Configure DNS records (see dns-cloudflare-reference.md)
2. Test email delivery with mail-tester.com
3. Set up webmail (Roundcube) if desired
4. Configure backups
5. Monitor deliverability
6. Set up additional security (AppArmor/SELinux)

---

## Additional Resources

- **Arch Wiki Mail Server**: https://wiki.archlinux.org/title/Mail_server
- **Arch Wiki Postfix**: https://wiki.archlinux.org/title/Postfix
- **Arch Wiki Dovecot**: https://wiki.archlinux.org/title/Dovecot

Your Arch Linux mail server is now complete with encryption, notmuch indexing, and all modern security features! 🚀

Enjoy your fully customizable, privacy-respecting email infrastructure on Arch Linux!
