Setup Email Server From Scratch Debian #2 - 02 LAMP Install
We believe in data independence, and support others who want data independence.
Debian Email From Scratch version 2 finished 2025-07-30.
We are still adding to it but it all works!
##############
# FAMP Setup #
##############
# LAMP stands for Linux Apache MySQL PHP ... a stack of servers and languages used
# to provide dynamic database driven website services. They are required for the
# mail stack and are used to run postfixadmin, and roundcube webmail. We'll set
# this up first before starting on the actual mail stack installation.
###################
# INSTALL MARIADB #
###################
apt update -y && apt upgrade -y
apt-get install mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
systemctl status mariadb
mysql_secure_installation
# You can leave the mysql root password blank if you are concerned it might be
# forgotten but then the database is only as secure as the root account. In
# this case it is a good idea to use unix socket and disallow root remotely.
# Do not restrict mysql to unix socket if remote access is needed
# Do restrict root login to local connections
# If you do not have any untrusted users on the system you do not need reset
# default root password and can leave it blank.
Switch to unix_socket authenitcaiton: n
Change root password: n
Remove anonymous users: Y
Disallow root login remotely: Y
Remove test database and access to it: Y
Reload privilege tables now: Y
# Login as root user, for the full command there is no space after -p and before the password
# If your password was blank just use 'mysql'
# mysql -u root -p<myqlrootpassword>
mysql
# Create an admin account with a password as below, replace secretpassword with your password
GRANT ALL ON *.* TO 'admin'@'localhost' IDENTIFIED BY 'secretpassword' WITH GRANT OPTION;
FLUSH PRIVILEGES;
# You can use either
# For collation general_ci is faster but may not sort as well for all languates, unicode_520 is more precise
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
nano /usr/local/etc/mysql/conf.d/server.cnf
# ---
[server]
lower_case_table_names=1
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
# this is only for the mysqld standalone daemon
[mysqld]
lower_case_table_names=1
..... farther down
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
... farther ...
[mariadb]
lower_case_table_names=1
character-set-server = utf8mb4
# collation-server = utf8mb4_unicode_520_ci
collation-server = utf8mb4_general_ci
# ---
systemctl restart mariadb
# Here is how to create a database and a user with access to only that database
mysql
create database mydatabase;
GRANT ALL ON mydatabase.* TO 'myuser'@'localhost' IDENTIFIED BY 'mysecretpassword' WITH GRANT OPTION;
flush privileges;
systemctl restart mariadb
# INSTALL PERL DBD::mysql - usefull if running perl scripts or perl cgi's that need access to modify or repair
# the database. If you only write php scripts this is optional.
apt-get install libdbd-mysql-perl
# INSTALL APACHE WEB SERVER
apt-get install apache2 -y
systemctl start apache2
systemctl enable apache2
systemctl status apache2
# INSTALL PHP - minimal install
apt install php libapache2-mod-php php-cli php-fpm php-json php-mysql php-zip
apt install php-gd php-mbstring php-curl php-xml php-pear php-bcmath
# Configure PHP /etc/php/8.2/apache2/php.ini /etc/php/8.2/cli/php.ini
# If your PHP version is different replace 8.2 with current version below...
# These values can affect max upload and file size when sending attachments with roundcube.
# Use fpm only if using php with nginx, we are using apache not nginx.
nano /etc/php/8.2/apache2/php.ini
nano /etc/php/8.2/cli/php.ini
# ---
max_execution_time = 600
max_input_time = 1000
max_input_vars = 3000
memory_limit = 256M
post_max_size = 48M
upload_max_filesize = 32M
# ---
a2enconf php8.2-fpm
a2enmod proxy_fcgi setenvif rewrite ssl
# if you want cgi enabled
a2enmod cgi
systemctl reload apache2
# Use a web browser and go to ...
http://okdeb.com
It Works!
# Make a virtual hosts for website and for mail hosts, 2 configuration files 4 virtual containers.
# Create web server directories and test files
mkdir -p /var/www/okdeb/html
mkdir -p /var/www/okdeb/mail
mkdir -p /var/www/okdeb/mx
mkdir -p /var/www/okdeb/cgi-bin
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/html/test.php
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/mail/test.php
echo '<?php print "<!DOCTYPE html lang=\"en\"><html><head><title>It Works!</title></head><body><h1>PHP Works!</h1></body></html>"; ?>' > /var/www/okdeb/mx/test.php
echo '<?php phpinfo(); ?>' > /var/www/okdeb/html/info.php
chmod 750 /var/www/okdeb
chmod 750 /var/www/okdeb/html
chmod 750 /var/www/okdeb/mail
chmod 750 /var/www/okdeb/mx
chmod 750 /var/www/okdeb/cgi-bin
chmod -R o-rwx /var/www/okdeb/html
chmod -R o-rwx /var/www/okdeb/mail
chmod -R o-rwx /var/www/okdeb/mx
chmod -R o-rwx /var/www/okdeb/cgi-bin
chown -R root:www-data /var/www/okdeb
# These VirtualHost definitions can be separated or combined with all 4 VirtualHost
# definitions together. With a few hosts separate files would be better, but with
# a large number of virtual hosts fewer files is preferable.
# A valid http configuration is needed to generate lecerts, and apache in won't run
# in https mode without the certs. Setup the virtual http hosts, generate the
# letsencrypt certificates, then enable https virtual hosts with the certificates.
cd /etc/apache2/sites-available
nano /etc/apache2/sites-available/okdeb.conf
# ---
<VirtualHost _default_:80>
ServerName okdeb.com
ServerAlias www.okdeb.com
ServerAlias 15.204.113.148
ServerAlias [2604:2dc0:202:300::3645]
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
DocumentRoot /var/www/okdeb/html/
<Directory /var/www/okdeb/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
# # If you want to run cgi's uncomment these and the handler
ScriptAlias /cgi-bin/ "/var/www/okdeb/cgi-bin/"
<Directory "/var/www/okdeb/cgi-bin">
AllowOverride None
Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</VirtualHost>
nano /etc/apache2/sites-available/mx.okdeb.conf
# ---
<VirtualHost *:80>
ServerName mx.okdeb.com
ServerAlias mail.okdeb.com
ServerAlias autoconfig.okdeb.com
ServerAlias autodiscover.okdeb.com
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
DocumentRoot /var/www/okdeb/mx/
<Directory /var/www/okdeb/mx/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Alias /mail "/var/www/okdeb/mail/"
<Directory /var/www/okdeb/mail/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
#RewriteEngine on
#RewriteCond %{SERVER_NAME} =mail.okdeb.com [OR]
#RewriteCond %{SERVER_NAME} =mx.okdeb.com
#RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
# ---
cd /etc/apache2/sites-enabled
rm /etc/apache2/sites-enabled/000-default.conf
rm /etc/apache2/sites-enabled/default-ssl.conf
ln -s ../site-available/okdeb.conf
ln -s ../site-available/mx.okdeb.conf
apachectl restart
# Test
http://okdeb.com/test.php
http://okdeb.com/info.php
http://mail.okdeb.com/test.php
http://mx.okdeb.com/mail/test.php
##########################
# Setup SSL with certbot #
##########################
# you will need a virtual host for certbot
apt install certbot python3-certbot-apache
# Certbot needs a virtual host setup for the domain, but we added that above.
# Certbot may need module rewrite to be enabled.
# Cron did not appear to be installed and certbot will need cron
apt-get install cron
systemctl status cron
systemctl enable cron.service
systemctl start cron.service
# Create certificates for web and mail hosts, each cert has 2 hosts
# New versions of postfix and Dovecot support SNI, server name indication so
# we'll set this up in case we want to host multiple mail domains.
# Here are some suggested names ...
<ul>
<li> mail.domain.tld
<li> imap.domain.tld
<li> smtp.domain.tld
<li> mx.domain.tld
<li> mx1.domain.tld
<li> mx1.domain.tld
</ul>
# Autoconfig and autodiscover are used to help mail clients automatically
# discover mail server setttings. The autodiscover SRV records must be on a
# https web host amd can be directly to the main mail servers hostname. We'll
# setup autoconfig and autodiscover at the end of setting up Dovecot and
# Thunderbird mail client but thinking ahead we'll generate the certs and setup
# the directories now.
# If website and mail web services are put together it can reduce the number of
# directories and certs. Separating regular web and mail services allows
# administrators to change one without breaking the other. It is good to reduce
# the attack surface somewhat by keeping things like postfixadmin and roundcube
# out of the main website structure and only enabling https.
# You could make just one certificate for everything, or separate as shown here.
# The simplest solution is to point all the MX records for multiple domains to
# one or two "main" hostnames. To host multiple virtual domains with a cert for
# only 2 hostnames set the MX records in the virtual domain(s) to the
# hostname(s) of the primary mail server. The A and AAAA records for these mx
# hosts can be the same or separate machines, we'll add a second here pointing
# to the same IPv4 and IPv6 address as a placeholder for setting up a backup mx
# server at a later date. The second MX record pointing to the same server also
# reduces delivery delay when postgrey and postscreen spam filtering and enabled.
# Example DNS for okdeb.com - mail sent to user@okdeb.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
# Example DNS for domain2.com - mail sent to user@domain2.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
# Example DNS for domain3.net - mail sent to user@domain3.net
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
# In this case you could ... and use this one cert for everything, eg in apache
# configuation and /etc/postfix/main.cf and /etc/dovecot/conf.d/10-ssl.conf
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name okdeb.com -d okdeb.com,www.okdeb.com,mx.okdeb.com,mail.okdeb.com,autodiscover.okdeb.com
# You only have one mail host, 2 MX records, and 1 certificate for everything. Autoconfig
# and autodiscover point to one mx.host and only this host shows up in the imap and
# smtp connection parameters for the mail client(s).
# To use separate mx hosts each domain, mx records will be pointed to thier own
# mx hostname. These MX hosts will also need their own A and AAAA records. In
# this case the postfix and dovecot cert will need all mx hostnames for all
# domains serviced. Customers like this option better as each domain is personalized,
# but setup is more complicated.
# Example DNS for okdeb.com - mail sent to user@okdeb.com
MX @ mx.okdeb.com 0
MX @ mail.okdeb.com 10
# Example DNS for domain2.com - mail sent to user@domain2.com
MX @ mx1.domain2.com 0
MX @ mx2.domain2.com 10
# Example DNS for domain3.net - mail sent to user@domain3.net
MX @ imap.domain3.net 0
MX @ smtp.domain3.net 10
# This only affects what hostnames show up in the mail client, eg Thunderbird under
# connection settings. Autoconfigure can be customized for each domain.
# Configure for separate mx hosts for each virtual domain, start with the first
# mx host. We have switch from using a common cert to using SNI for postfix and
# dovecot. We'll cover virtual domains later.
# Make regular website cert
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name okdeb.com -d okdeb.com,www.okdeb.com
# Make cert for postfix, dovecot, autodiscoer, and mx/mail website.
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name mx.okdeb.com -d mx.okdeb.com,mail.okdeb.com,autodiscover.okdeb.com
# When you want to add or remove hosts from a cert rerun certbot without the unwanted
# hostname or with the new hostnames, certbot will prompt to update the cert.
certbot certonly -a apache --agree-tos --redirect --hsts --staple-ocsp --email postmaster@okdeb.com --cert-name mx.okdeb.com -d mx.okdeb.com,smtp.okdeb.com,imap.okdeb.com,autodiscover.okdeb.com
# List certificates
certbot certficates
# Revoke and delete certificates
certbot revoke --cert-name <certname>
certbot delete --cert-name <certname>
# Make sure the auto renew script is being run by cron
cat /etc/cron.d/certbot
# Setup SSL for website and mail website
nano /etc/apache2/sites-available/ssl-okdeb.conf
# ---
<VirtualHost _default_:443>
ServerName okdeb.com
ServerAlias www.okdeb.com
ServerAlias 15.204.113.148
ServerAlias [2604:2dc0:202:300::3645]
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/okdeb.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/okdeb.com/privkey.pem
DocumentRoot /var/www/okdeb/html/
<Directory /var/www/okdeb/html/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch "\.(?:cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
# If you want cgi
ScriptAlias /cgi-bin/ "/var/www/okdeb/cgi-bin/"
<Directory "/var/www/okdeb/cgi-bin">
# AllowOverride All
AllowOverride None
Options +ExecCGI -Indexes -MultiViews +SymLinksIfOwnerMatch
Require all granted
</Directory>
</VirtualHost>
# ---
nano /etc/apache2/sites-available/ssl-mx.okdeb.conf
# ---
<VirtualHost *:443>
ServerName mx.okdeb.com
ServerAlias mail.okdeb.com
ServerAlias autoconfig.okdeb.com
ServerAlias autodiscover.okdeb.com
ServerAdmin postmaster@okdeb.com
CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
ErrorLog ${APACHE_LOG_DIR}/error.log
DirectoryIndex index.php index.html
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mx.okdeb.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mx.okdeb.com/privkey.pem
DocumentRoot /var/www/okdeb/mx/
<Directory /var/www/okdeb/mx/>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
Alias /mail "/var/www/okdeb/mail"
Alias "/Autodiscover/Autodiscover.xml" "/var/www/okdeb/mail/Autodiscover.xml/index.php"
<Directory /var/www/okdeb/mail/>
DirectorySlash Off
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
<FilesMatch "\.(?:cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory /var/www/okdeb/cgi-bin>
SSLOptions +StdEnvVars
</Directory>
</VirtualHost>
# ---
# Enable https hosts and restart apache
cd /var/apache2/site-enabled
ln -s ../sites-available/ssl-okdeb.conf
ln -s ../sites-available/ssl-mx.okdeb.conf
apachectl restart
# Test - we test https://mx.okdeb.com/mail to confirm php is enabled for Autodiscover.xml/index.php
https://okdeb.com/test.php
https://okdeb.com/info.php
https://mx.okdeb.com/test.phyp
https://mx.okdeb.com/mail/test.php
# For security remove the php script
rm /var/www/okdeb/html/info.php
# It is a good idea to remove the other php scripts as well before going live.
/var/www/okdeb/html/test.php
/var/www/okdeb/mail/test.php
/var/www/okdeb/mx/test.php
# Optionally create and test cgi
nano /var/www/okdeb/cgi-bin/test.cgi
# ---
#!/usr/bin/perl
print "Content-type: text/html\n\n";
print "Hello this a cgi script that can't parse variables.\n";
# ---
chmod 755 /var/www/okdeb/cgi-bin/test.cgi
# Test your cgi if using perl cgi's
http://okdeb.com/cgi-bin/test.cgi
# Remove it for security
rm /var/www/okdeb/cgi-bin/test.cgi
# Postfix is a popular smtp server and we'll continue with postfix setup on the next page...