
Managing a mail server manually gets painful fast. The moment you need to handle more than one domain or a handful of mailboxes, editing flat config files and reloading Postfix for every change stops being practical and starts causing mistakes. That is exactly the problem PostfixAdmin solves. This guide walks you through how to install PostfixAdmin on Fedora 43 from scratch, covering every layer of the stack: Postfix as the mail transfer agent, MariaDB as the database backend, Apache and PHP as the web layer, and PostfixAdmin as the browser-based control panel that ties them all together.
Every step below includes a “WHY” explanation. You will understand what each command actually does to your system, not just copy and paste blindly. That approach saves hours of debugging and makes you a better sysadmin in the process.
What this guide covers:
- Setting up a fully qualified hostname
- Installing and configuring Postfix
- Installing MariaDB and creating the PostfixAdmin database
- Installing Apache with all required PHP extensions
- Downloading and deploying PostfixAdmin
- Writing a secure
config.local.php - Creating an Apache VirtualHost
- Configuring SELinux and firewalld (the steps most tutorials skip)
- Running the web-based setup wizard
- Verifying your install works end-to-end
- Troubleshooting the five most common failure points
Prerequisites
Before you run a single command, confirm all of the following are in place.
System requirements:
- Fedora 43 server (fresh install preferred) with at least 2GB RAM and 20GB disk space
- Root access or a user account with full
sudoprivileges - A static IP address — dynamic IPs are blacklisted by the majority of receiving mail servers
DNS requirements (critical — configure these before starting):
- An A record pointing your mail hostname (e.g.,
mail.yourdomain.com) to your server’s IP - An MX record for your domain pointing to
mail.yourdomain.com - A PTR (reverse DNS) record matching your FQDN — without this, most major mail providers will reject your outbound email as suspected spam
Ports that must be reachable:
25(SMTP — mail transfer between servers)80/443(HTTP/HTTPS — PostfixAdmin web interface)143/993(IMAP / IMAPS)110/995(POP3 / POP3S)
Knowledge baseline:
- Basic Linux command-line navigation
- Familiarity with editing files using
nanoorvim - A domain name you control and can update DNS records for
Step 1: Update Your Fedora 43 System and Set the Hostname
Update All Packages
Start with a clean, fully updated system. Fedora moves fast, and stale packages cause dependency conflicts during installation.
sudo dnf update -y && sudo dnf upgrade -y
WHY: Package updates pull in security patches, updated library versions, and any fixes to packages like openssl or glibc that Postfix and MariaDB depend on at compile time. Installing server software on top of outdated packages is a recipe for subtle, hard-to-diagnose failures.
If a kernel update was included, reboot before continuing:
sudo reboot
WHY the reboot: When a new kernel is installed, the running kernel in memory does not match the installed one. Some services including network drivers behave differently or fail to load until the new kernel is active.
Install Utility Packages
sudo dnf install wget nano net-tools -y
Set Your Fully Qualified Domain Name (FQDN)
sudo hostnamectl set-hostname mail.yourdomain.com
Open /etc/hosts and add your server’s IP alongside its hostname:
sudo nano /etc/hosts
Add this line (replace with your actual IP and domain):
YOUR_SERVER_IP mail.yourdomain.com mail
Confirm the FQDN is set correctly:
hostname -f
Expected output:
mail.yourdomain.com
WHY set the FQDN now: Postfix reads the hostname at startup and embeds it in SMTP banners, Received headers, and bounce messages. If hostname -f returns something like localhost.localdomain, receiving servers will flag your outbound mail as spoofed or misconfigured. Fix this first so everything downstream uses the correct identity.
Step 2: Install and Configure Postfix on Fedora 43
Install Postfix
Fedora 43 may include Postfix by default, but run the install command anyway to confirm you have the latest version:
sudo dnf install postfix postfix-mysql -y
WHY postfix-mysql: This adds the MySQL/MariaDB lookup table support to Postfix. Without it, Postfix cannot query your MariaDB database for virtual domain and mailbox information later in the setup.
Configure main.cf
Open the primary Postfix configuration file:
sudo nano /etc/postfix/main.cf
Find and set the following directives (replace yourdomain.com with your actual domain throughout):
myhostname = mail.yourdomain.com
mydomain = yourdomain.com
myorigin = $mydomain
inet_interfaces = all
inet_protocols = ipv4
mydestination = $myhostname, localhost.$mydomain, localhost
mynetworks = 127.0.0.0/8
home_mailbox = Maildir/
smtpd_banner = $myhostname ESMTP
WHY inet_interfaces = all: This tells Postfix to listen on every network interface on your server, which is required to accept incoming mail from external senders. The default value localhost means Postfix only accepts mail from the same machine.
WHY set mydestination carefully: This directive lists which domains Postfix delivers mail locally rather than forwarding. If you add your main domain here AND configure virtual domains for it later, Postfix will try to deliver mail twice and reject it. Keep mydestination limited to the hostname and localhost entries shown above.
Enable and Start Postfix
sudo systemctl enable --now postfix
Verify it is running:
sudo systemctl status postfix
Expected output (look for the active (running) line):
● postfix.service - Postfix Mail Transport Agent
Loaded: loaded (/usr/lib/systemd/system/postfix.service; enabled)
Active: active (running) since ...
Step 3: Install MariaDB and Create the PostfixAdmin Database
Install MariaDB
sudo dnf install mariadb-server -y
Before starting MariaDB, set the character encoding to utf8mb4. Create a configuration file:
sudo nano /etc/my.cnf.d/charset.cnf
Add the following:
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
[client]
default-character-set = utf8mb4
WHY utf8mb4 before first start: Standard utf8 in MySQL/MariaDB only supports 3-byte characters, which excludes emoji and certain international domain name characters. The utf8mb4 charset supports the full 4-byte Unicode range. Set this before any data is created because converting an existing database to utf8mb4 is significantly more complex.
Now enable and start MariaDB:
sudo systemctl enable --now mariadb
Secure MariaDB
Run the security script to harden your database server:
sudo mysql_secure_installation
When prompted:
- Set a strong root password
- Remove anonymous users: Yes
- Disallow root login remotely: Yes
- Remove test database: Yes
- Reload privilege tables: Yes
WHY this matters: A fresh MariaDB install includes an anonymous user account and a test database. These exist for convenience during development, not for production. Leaving them in place means anyone who can reach your database socket can log in without credentials.
Create the PostfixAdmin Database and User
Log into MariaDB as root:
sudo mysql -u root -p
Run the following SQL commands:
CREATE DATABASE postfixadmin CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'postfixadmin'@'localhost' IDENTIFIED BY 'YourStrongPassword123!';
GRANT ALL PRIVILEGES ON postfixadmin.* TO 'postfixadmin'@'localhost';
FLUSH PRIVILEGES;
EXIT;
WHY a dedicated database user: Giving PostfixAdmin access through the MariaDB root account violates the principle of least privilege. If the PostfixAdmin config file is ever exposed, an attacker with root database access can destroy every database on your server, not just the mail data. A scoped user limits the damage to one database.
WHY FLUSH PRIVILEGES: MariaDB caches grant tables in memory. Without flushing, the new user permissions may not activate until the next service restart, causing PostfixAdmin to fail with an “access denied” error even though the SQL commands ran successfully.
Step 4: Install Apache Web Server and PHP with Required Extensions
Install Apache and PHP Modules
sudo dnf install httpd php php-mysqlnd php-mbstring php-imap php-xml php-intl php-json php-gd -y
WHY each PHP extension:
php-mysqlnd: The native MySQL driver for PHP. Without this, PostfixAdmin cannot open a connection to MariaDB.php-mbstring: Required for multibyte string operations used when processing international email addresses.php-imap: Validates IMAP mailbox connections during PostfixAdmin’s setup checks.php-xmlandphp-intl: Required for PostfixAdmin’s internationalization layer and XML-based configuration parsing.php-gd: Handles image generation for CAPTCHA functionality in PostfixAdmin’s user registration flow.
WHY Apache over Nginx here: Apache handles PostfixAdmin’s .htaccess rewrite rules natively through mod_rewrite. Nginx requires additional FastCGI pass configuration to achieve the same result. For a PostfixAdmin-only setup, Apache is the simpler and less error-prone choice.
Adjust PHP Settings
Open the PHP configuration file:
sudo nano /etc/php.ini
Find and update these values:
memory_limit = 128M
upload_max_filesize = 20M
post_max_size = 20M
max_execution_time = 300
date.timezone = Asia/Jakarta
Replace Asia/Jakarta with your server’s actual timezone.
Enable and Start Apache
sudo systemctl enable --now httpd
Step 5: Download, Deploy, and Configure PostfixAdmin
Download PostfixAdmin from GitHub
PostfixAdmin is not available in Fedora’s default package repositories. Download it directly from the official GitHub releases page:
cd /var/www/html
sudo wget https://github.com/postfixadmin/postfixadmin/archive/refs/tags/postfixadmin-3.3.15.tar.gz
sudo tar -xzf postfixadmin-3.3.15.tar.gz
sudo ln -s postfixadmin-postfixadmin-3.3.15 postfixadmin
WHY use a symlink: The symlink postfixadmin points to the versioned directory. When you upgrade to a newer PostfixAdmin release in the future, you extract the new version alongside the old one and update the symlink to point at it. Your Apache config does not need to change at all.
Create the templates_c Directory
sudo mkdir -p /var/www/html/postfixadmin/templates_c
WHY this directory is required: PostfixAdmin uses the Smarty PHP templating engine. Smarty compiles your HTML templates into PHP files the first time they are requested and stores those compiled files in templates_c for faster loading on subsequent requests. Without this directory, PostfixAdmin throws a fatal error immediately on first page load.
Set File Ownership and Permissions
sudo chown -R apache:apache /var/www/html/postfixadmin
sudo chmod -R 755 /var/www/html/postfixadmin
sudo chmod -R 775 /var/www/html/postfixadmin/templates_c
WHY apache:apache ownership: The Apache web server on Fedora runs as the apache user. If PostfixAdmin files are owned by root, Apache cannot write compiled templates to templates_c and cannot create or update configuration caches. The result is a broken interface with cryptic permission-denied errors in /var/log/httpd/error_log.
Create the Local Configuration File
Do not edit config.inc.php directly. Instead, create config.local.php, which PostfixAdmin merges on top of the defaults:
sudo nano /var/www/html/postfixadmin/config.local.php
Add the following content:
<?php
$CONF['configured'] = true;
$CONF['database_type'] = 'mysqli';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'YourStrongPassword123!';
$CONF['database_name'] = 'postfixadmin';
$CONF['mail_server'] = 'localhost';
$CONF['encrypt'] = 'dovecot:SHA512-CRYPT';
$CONF['dovecotpw'] = '/usr/bin/doveadm pw';
$CONF['domain_path'] = 'NO';
$CONF['domain_in_mailbox'] = 'YES';
$CONF['fetchmail'] = 'NO';
WHY config.local.php instead of editing the original: PostfixAdmin’s upstream developers designed this separation intentionally. When you upgrade PostfixAdmin, the new release overwrites config.inc.php with updated defaults. Your local file remains untouched because the upgrade does not know it exists. This pattern prevents you from losing your database credentials or custom settings during every upgrade.
WHY $CONF['configured'] = true: This flag is a deliberate safety lock in PostfixAdmin’s code. The application will not process any requests or display the setup wizard until this is explicitly set. It prevents partial or accidental configurations from silently running on a server.
WHY dovecot:SHA512-CRYPT for password encryption: MD5 password hashing has been broken since 2004. SHA512-CRYPT is the current Dovecot-compatible standard. It is computationally expensive to brute-force, which matters because your mail users’ passwords will be stored in this database.
Step 6: Configure Apache VirtualHost for PostfixAdmin
Create a dedicated Apache configuration file for PostfixAdmin:
sudo nano /etc/httpd/conf.d/postfixadmin.conf
Add the following:
<VirtualHost *:80>
ServerName mail.yourdomain.com
DocumentRoot /var/www/html/postfixadmin/public
<Directory /var/www/html/postfixadmin/public>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog /var/log/httpd/postfixadmin-error.log
CustomLog /var/log/httpd/postfixadmin-access.log combined
</VirtualHost>
WHY DocumentRoot points to /public: PostfixAdmin 3.x restructured its directory layout so that only the public/ subdirectory is meant to be web-accessible. Files above that directory, including config.local.php which contains your MariaDB password, live outside the web root. Pointing Apache at the public/ subdirectory means those sensitive files are never reachable through a browser, even if file permissions are misconfigured.
WHY AllowOverride All: PostfixAdmin ships with .htaccess files that control URL routing. With AllowOverride None (the Apache default for security), those .htaccess rules are completely ignored and every page beyond the root returns a 404 error.
Restart Apache to load the new configuration:
sudo systemctl restart httpd
Step 7: Configure Firewalld and SELinux
This step is the most frequently skipped part of any Fedora mail server tutorial, and skipping it is responsible for the majority of failed installations.
Configure Firewalld
Open the necessary ports:
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --add-service=smtp --permanent
sudo firewall-cmd --add-service=imap --permanent
sudo firewall-cmd --add-service=pop3 --permanent
sudo firewall-cmd --reload
Confirm the active rules:
sudo firewall-cmd --list-all
WHY: Fedora 43 enables firewalld by default with a restrictive policy that blocks all inbound traffic except SSH. Without explicitly opening these ports, external mail clients cannot connect to Postfix or the PostfixAdmin web interface, even if both services are running and correctly configured.
Configure SELinux Booleans
sudo setsebool -P httpd_can_network_connect_db 1
sudo setsebool -P httpd_can_sendmail 1
sudo setsebool -P httpd_can_network_connect 1
WHY httpd_can_network_connect_db is critical: Fedora 43 ships with SELinux in Enforcing mode. Without this boolean, SELinux silently blocks Apache from making TCP connections to MariaDB. PostfixAdmin will display a database connection error that looks exactly like wrong credentials or a typo in config.local.php, when the real cause is an SELinux access vector cache denial. This is the number one reason people on StackOverflow say PostfixAdmin “doesn’t work on Fedora/CentOS/RHEL.” Ubuntu and Debian tutorials never mention this because those distros default to AppArmor, which does not enforce the same restriction.
You can confirm SELinux is making denials by checking:
sudo grep "denied" /var/log/audit/audit.log | grep httpd
The -P flag in setsebool -P makes the change persistent across reboots.
Step 8: Run the PostfixAdmin Web Setup Wizard
Open a browser and navigate to:
http://mail.yourdomain.com/setup.php
What the Setup Wizard Checks
The wizard runs a requirements checklist before doing anything else. All items must show a green checkmark before you proceed:
- PHP version compatibility
- All required PHP extensions loaded
- Database connection successful
templates_cdirectory writableconfig.local.phppresent with$CONF['configured'] = true
If any item is red, fix it before continuing. The most common red items are a missing PHP extension (recheck your dnf install command) or a templates_c permission problem.
Generate and Store the Setup Password Hash
Type a strong setup password into the field on the wizard page and click Generate password hash. The page will display a hash string that looks like:
$2y$10$abcdefghijklmnopqrstuvwxyz123456789...
Copy that entire string. Open config.local.php and add:
sudo nano /var/www/html/postfixadmin/config.local.php
$CONF['setup_password'] = 'paste_your_hash_here';
WHY this hash exists: The setup page can create administrator accounts with full control over your mail server. Without a password hash protecting it, any visitor to setup.php could create their own admin account. This design forces you to prove you have write access to config.local.php before you can use the setup wizard.
Create Your Superadmin Account
Return to setup.php, enter your setup password, and complete the form to create the first superadmin account. Use an email address and a strong, unique password that is different from both your database password and your setup password.
After the account is created, restrict access to setup.php:
sudo chmod 440 /var/www/html/postfixadmin/public/setup.php
WHY restrict setup.php after setup: Leaving it fully accessible after installation means a logged-in attacker or a misconfigured server could allow unauthorized users to create rogue administrator accounts. Chmod 440 prevents Apache from executing it while keeping it readable for future reference.
Step 9: Verify Your PostfixAdmin Installation
Log into the PostfixAdmin interface at:
http://mail.yourdomain.com/
Use the superadmin credentials you just created. Once inside:
- Navigate to Domain List and add a test domain (e.g.,
testdomain.com). - Go to Mailbox and create a test virtual mailbox (e.g.,
test@testdomain.com). - Confirm the mailbox appears in the list.
Test Postfix Mail Acceptance
Install swaks for CLI mail testing:
sudo dnf install swaks -y
Send a test message to your new virtual mailbox:
swaks --to test@testdomain.com --server localhost
Expected output:
-> EHLO mail.yourdomain.com
<- 250-mail.yourdomain.com
...
-> MAIL FROM:<>
<- 250 2.1.0 Ok
-> RCPT TO:<test@testdomain.com>
<- 250 2.1.5 Ok
WHY test before going live: You do not want to discover a misconfigured mydestination or a missing database grant after your MX records are already pointing real mail at this server. Test locally first with swaks, then update your DNS records.
Check Postfix logs for any errors:
sudo journalctl -u postfix -f
Troubleshooting Common Issues
Error: “Database connection failed” on setup.php
Cause: Either incorrect credentials in config.local.php or SELinux is blocking Apache from connecting to MariaDB.
Fix:
# Check SELinux boolean
sudo getsebool httpd_can_network_connect_db
# If it returns "off", enable it
sudo setsebool -P httpd_can_network_connect_db 1
# Then verify database credentials directly
mysql -u postfixadmin -p postfixadmin
Error: PostfixAdmin Pages Return 404
Cause: AllowOverride is not set to All in your Apache VirtualHost, so .htaccess routing rules are being ignored.
Fix: Confirm your VirtualHost in /etc/httpd/conf.d/postfixadmin.conf contains AllowOverride All, then restart Apache:
sudo systemctl restart httpd
Error: “templates_c is not writable”
Cause: The templates_c directory is owned by root or has permissions that block the apache user.
Fix:
sudo chown -R apache:apache /var/www/html/postfixadmin/templates_c
sudo chmod -R 775 /var/www/html/postfixadmin/templates_c
Error: Mail Rejected by External Servers (“550 Permanent Failure”)
Cause: Missing PTR (reverse DNS) record, or your IP is on a blocklist due to a missing SPF record.
Fix: Contact your VPS or hosting provider to set a PTR record matching your FQDN. Add an SPF TXT record to your domain’s DNS:
v=spf1 mx a ip4:YOUR_SERVER_IP ~all
Error: Postfix “Relay Access Denied” for Your Own Domain
Cause: The domain you created in PostfixAdmin is listed in mydestination inside /etc/postfix/main.cf. This creates a conflict between local delivery and virtual mailbox delivery.
Fix: Remove your virtual domain from mydestination. That directive should only contain your hostname and localhost entries:
sudo nano /etc/postfix/main.cf
# Set to:
# mydestination = $myhostname, localhost.$mydomain, localhost
sudo systemctl restart postfix
Congratulations! You have successfully installed PostfixAdmin. Thanks for using this tutorial for installing the latest version of PostfixAdmin on Fedora 43 Linux. For additional help or useful information, we recommend you check the official PostfixAdmin website.