Setup Email Server From Scratch Debian #2 - 02 LAMP Install

01 Server Setup <- Intro -> 03 Postfix SMTPD

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...

01 Server Setup <- Intro -> 03 Postfix SMTPD