How to install Amazon Linux 2023 for WordPress

Amazon AWS Linux 2023, php8.1, mariadb10.5, php-fpm, phpMyAdmin, Certbot SSL, Postfix, WordPress

My Dovecot articles show how to install these packages and configurations, but it is easier to do it from this article when not using Dovecot – which is more likely.

Currently missing phpMyAdmin, mariadb – please see the Dovecot installation for those sections until updated here

Part A configurations

When I do a terminal shell login, I type:
$ sudo su
# set -o vi

When I refer to “vi” you may have another preferred editor. Do not cut and paste my comments in square brackets. Replace “Australia/Brisbane” with your own country/city. If a few things are not working, not only look at error messages, make use of forums, but reboot the instance.

[log in as root: sudo su, then set -o vi for the vi editor]
[for a fresh/new instance:]

echo "vm.swappiness=10" >> /etc/sysctl.conf
echo "vm.vfs_cache_pressure=200" >> /etc/sysctl.conf
sysctl -w vm.swappiness=10
sysctl -w vm.vfs_cache_pressure=200
dd if=/dev/zero of=/swapfile bs=1024 count=1048576
mkswap /swapfile
chmod 600 /swapfile
swapon /swapfile
echo "/swapfile swap swap defaults 0 0" >> /etc/fstab
free -m

dnf update
dnf upgrade

dnf check-release-update

[repeat these steps till no upgrades are left. Do the update before adding the packages below.
e.g. dnf update --releasever=2022.0.20221019]


[log back in as root]

dnf install -y httpd httpd-tools mod_ssl 
dnf install -y php php-common php-pear wget php-mysqli php-devel php-mbstring
dnf install -y php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache
dnf install -y gd libzip-devel httpd-devel kernel-devel php-gd postfix

- If using shell scripts acessing ImageMagick:
dnf install -y ImageMagick ImageMagick-devel
There is no php-ImageMagick at time of writing. I think it is possible to hunt around on how to isntall it for php.
I no longer use memcached, php-memcached or php-apcu as there are no packages in Linux 2022/2023. This reduces some configuration hassle.
If there were, you would update the inbound rules of the EC2 instance security group for memecached and then dnf install memcached php-memcached and configure the last line of /etc/sysconfig/memcached.
I have older notes on this.
- We need to install ZipArchive (php-zip) for various apps that may be used in WordPress that require zip. We will install this here as well...

dnf -y install pcre-devel gcc zlib zlib-devel
pecl install zip
pecl channel-update

[Now edit /etc/php.ini and after the Dynamic section (as shown below) add
Note that in Linux2 we coud simply do yum install php-zip and that was all we had to do. If we had put into php.ini there would be an error in /etc/httpd/log/error_log:]

vi /etc/php.ini
; Dynamic Extensions ;

[Save and exit the editor, then restart httpd.]

In an earlier version of linux I had to get crontab from fedora, but not so now.
wget -O epel.rpm –nv \
rpm -ihv --nodeps ./epel-release-latest-8.noarch.rpm
Note: The script advises to execute /usr/bin/crb enable but I am not sure why as crontab is working without that. You could do it anyway.
/usr/bin/crb enable
Enabling CRB repo
CRB repo is disabled

dnf install -y cronie cronie-anacron

[use your country/city:]

a="Australia/Brisbane";export a;echo $a
ln -sf /usr/share/zoneinfo/$a /etc/localtime

Change /etc/bashrc to your preferred method of PS1.
I use this: (comment out the existing line - exit the shell a little further below.)
# [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ "
    [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\ \w]\\$ "

usermod -a -G apache ec2-user

[exit and log back in as root]

chown -R ec2-user:apache /var/www
chmod 2775 /var/www && find /var/www -type d -exec sudo chmod 2775 {} \;
find /var/www -type f -exec sudo chmod 0664 {} \;
cd /var/www
chown apache html
chgrp apache html
chmod 2775 html

dnf -y install mariadb105
dnf -y install mariadb105-server

[for certbot/let'sencrypt and PIP (rather than snapd!):]
[do not install these if using a paid certifcate]
dnf install python3
dnf install python3-devel
dnf install augeas-libs
python3 -m venv /opt/certbot/
/opt/certbot/bin/pip install --upgrade pip
/opt/certbot/bin/pip install certbot certbot-apache
ln -s /opt/certbot/bin/certbot /usr/bin/certbot

vi /etc/php.ini

date.timezone = Australia/Brisbane
max_execution_time = 300
max_input_time = 600
max_input_vars = 2500
post_max_size = 50M
upload_max_filesize = 50M
max_file_uploads = 20
memory_limit = 256M

[save and exit - you may have other values for country/city and sizes, but I'd keep 256M.
Input vars value is so larger WordPress menu lists will display.]

vi /etc/php.d/10-opcache.ini


[save and exit]

vi /etc/selinux/config

# SELINUX=permissive

[save and exit]

cp -p /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf.o
vi /etc/php-fpm.d/www.conf

cp -p www.conf www.conf.bak

; pm = dynamic
pm = ondemand
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
pm.process_idle_timeout = 10s;
pm.max_requests = 500
php_admin_value[memory_limit] = 256M

[save and exit - there is a semicolon at the end of one of the above lines]

vi /etc/httpd/conf/httpd.conf

[after Listen 80 add:]

KeepAlive On
MaxKeepAliveRequests 50
KeepAliveTimeout 5

[in the <Directory "/var/www/html"> section:]

AllowOverride All

[in the <IfModule dir_module> section after it:]

DirectoryIndex index.php index.html

[fix the ServerName to your domain:]


[If intending to add IP2Location for country blocking, add these lines at the end of the file:]

<IfModule mod_ip2location.c>
        IP2LocationEnable On
        IP2LocationDetectProxy On
        IP2LocationSetmode ALL
        IP2LocationDBFile /home/ec2-user/ip2location/IP2LOCATION-LITE-DB1.BIN

[save and exit]

vi /etc/httpd/conf.modules.d/00-mpm.conf

[we do not use http/2 on small instances]

LoadModule mpm_prefork_module modules/
#LoadModule mpm_worker_module modules/

[append these for performance: - remember, these square brackets do not go into the edited file - they are my way of adding notes to explain things.]

<IfModule mpm_prefork_module>
StartServers 2
MinSpareServers 2
MaxSpareServers 5
MaxRequestWorkers 125
ServerLimit 125
MaxConnectionsPerChild 0

[save and exit]

vi /etc/httpd/conf.modules.d/00-proxy.conf

[typicall heartbeat has caused continuous error log messages]

# LoadModule lbmethod_heartbeat_module modules/

[save and exit]

Add a new file, /etc/httpd/conf.d/phpMyAdmin.conf
Use your own static IP address (otherwise edit it every time you use phpMyAdmin for your dynamic address). Replace with your IP address. This lets you access the phpMyAdmin database GUI.
Note: we are not configuring anything complex with httpd.

vi /etc/httpd/conf.d/phpMyAdmin.conf

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin

<Directory /usr/share/phpMyAdmin/>
   AddDefaultCharset UTF-8

   <IfModule mod_authz_core.c>
     # Apache 2.4
       Require ip
       Require ip ::1
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Deny from All
     Allow from
     Allow from ::1

<Directory /usr/share/phpMyAdmin/setup/>
   <IfModule mod_authz_core.c>
     # Apache 2.4
       Require ip
       Require ip ::1
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Deny from All
     Allow from
     Allow from ::1

<Directory /usr/share/phpMyAdmin/setup/frames/>
    Order Deny,Allow
    Deny from All
    Allow from None

[save and exit]

Configure /etc/httpd/conf.d/ssl.conf
Note, until we are ready to install SSL and restart httpd, I move the file to ssl.conf.o. When I am ready I move it to ssl.conf.
If using certbot, you create the basic entries in the file and comment out the certificate lines, rename the file and start httpd so that ssl is not running. Then rename the file back to ssl.conf so that when certbot tries to install the certificate, ssl.conf is present. Then when you run the certbot -w command for the and names it will work. After that command, you run certbot --apache, and then edit the ssl.conf file to fix the entries at the bottom of the file, to replace the -001 entries back to what they should be, and add an alias for www. Then you can go to /etc/letsencrypt and remove the -001 directories.
My certbot scripts show how you can renew every 30 days and verify the certificates. You should do your first -w command appended wtih --dry-run.

You should not log into phpMyAdmin to create a WordPress database and user until SSL is configured.
If using a paid certificate, you have to learn about how it works, and then reference the .cert and .key files in ssl.conf.

[below, at the top of the file, use your own domain name. I will already have a CNAME record in the DNS configs for pointing to]

vi /etc/httpd/conf.d/ssl.conf

<VirtualHost *:80>
Redirect permanent /
RewriteEngine on
RewriteCond %{SERVER_NAME}
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]


SSLEngine on
#SSLProtocol all -SSLv3
#SSLProxyProtocol all -SSLv3

SSLProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2
SSLProxyProtocol -SSLv2 -SSLv3 -TLSv1 -TLSv1.1 +TLSv1.2

# note: the next two long lines must have no carriage return line feeds. Please check to make sure.


SSLHonorCipherOrder on

SSLCompression off
SSLInsecureRenegotiation Off
SSLSessionTickets Off
SSLOpenSSLConfCmd ECDHParameters secp384r1
SSLOpenSSLConfCmd Curves secp384r1

# we comment out the following lines only if using certbot, otherwise we change them to our own certificates from Comodo/Sectigo or whoever. The .key file can be used every year, and the .crt is the original .crt appeneded with other "bundled" entires except the root certificate. It is easy to create these files with an editor. Sometimes the provider gives a bundled file but it may or may not have the root certificate in it. You want to remvoe references to the root. A test on shows if you have an issue.

# SSLCertificateKeyFile /etc/pki/tls/private/localhost.key         <-------- USE YOUR OWN, e.g. mydomain_au.key and uncomment the lines if you have Comodo/Sectigo and not certbot.
# SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt      <-------- USE YOUR OWN, e.g. mydomain_au.crt

# just before the last line of </VirtualHost>, certbot should add these lines during its configurations, and if not you can manually add and check the .pem files exist. It is no magic as you can tar file the letsencrypt directory and re-install on another instance without problems if you upgrade your EC2 instance using the ame domain name.

# only have these following lines if using certbot from Let's Encrypt

SSLCertificateFile /etc/letsencrypt/live/
SSLCertificateKeyFile /etc/letsencrypt/live/
Include /etc/letsencrypt/options-ssl-apache.conf

[save and exit]

Part B configurations

Configuring Postfix – we use this to send internal emails.

cd /etc/postfix

Add the following lines to a new file sasl_passwd, using the square brackets as shown and the e-mail region. I use Oregon. There is no e-mail region in Australia.

You will have previously created SMTP credentials from the SES console. Use these where it says SMTPUSERNAME:SMTPPASSWORD below.

cd /etc/postfix
vi sasl_passwd


[save and exit - use the square brackets as shown above]

systemctl stop postfix;systemctl disable postfix;ps -ef|grep postfix

postconf -e "relayhost = []:587" \
"smtp_sasl_auth_enable = yes" \
"smtp_sasl_security_options = noanonymous" \
"smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd" \
"smtp_use_tls = yes" \
"smtp_tls_security_level = encrypt" \
"smtp_tls_note_starttls_offer = yes"

[Enter the above lines with the \ then press RETURN KEY to execute them. Remember, these lines show Oregon as the region. If you use North Virginia you would need that region.]

postmap hash:/etc/postfix/sasl_passwd
chmod 0600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
postfix start; sudo postfix reload; postfix flush

[Now do a test e-mail, then disable postfix for security reasons and only call it from shell scripts]

sendmail -f
From: admin <>
Subject: Postfix Test
This is a test message from AWS Postfix and SES


[It should have sent without errors. If not, clean up the /var/log and fix the error. If you are in sandbox mode, use the verified email address you created in SES, and check your DNS records.
It could also be you see errors in Cloudwatch logs in Oregon region if you perhaps made mistakes in your Lambda or SES setups. These details are as per a spearate article.]

systemctl disable postfix
postfix stop

[We stop postfix for security. We can use it in our shell scripts to send alerts.]

[Configure mariadb:]

systemctl start mariadb

Enter current password for root (enter for none): 
OK, successfully used password, moving on...

Switch to unix_socket authentication [Y/n] n

Change the root password? [Y/n] Y
(nominate your database password)
Y for the remaining questions

systemctl stop mariadb
systemctl start mariadb
systemctl enable mariadb
systemctl enable httpd
systemctl enable php-fpm

php -v

[This will show version 8.1 or above]

[You can check the version of Mariadb with: dnf list|grep mariadb]

[Configuring phpMyAdmin…]

cd /usr/share

tar xvf .....  
[where ..... is the downloaded file.
Then delete the tar.gz file, then use the Unix command to move the directory to phpMyAdmin, e.g.: mv yourfile phpMyAdmin]

cd phpMyAdmin
mkdir tmp
chmod 777 tmp
cp -p

Search for the blowfish line. Do a Google search on blowfish phpmyadmin generator.
I use:[insert_php]echo%20$code;[/insert_php] from
Paste the generated value into the blowfish value.
Then after SaveDir as shown below, add TEMPDir...

$cfg['SaveDir'] = '';
$cfg['TEMPDir'] = '/tmp';

Restart httpd - recall we may not have SSL running, so you should not really log into phpMyAdmin at this stage.
You can check the interface is ready with
As a note, you can view your PHP settings with
I have a separate article on using phpMyAdmin.

[Please see my separate articles on IP2location and S3FS (NFS), CDN if you wish to use those capabilities.]

dnf update

[I like to reboot:]


For more on SSL installations, please see my article at:


You may now reboot the system and verify SSL is working, then as per my other articles, create a database and user in phpMyAdmin, use FileZilla (or Putty) to upload WordPress, unzip it and move the files to /var/www/html.

Then begin installation with and enter the values you want which creates the wp-config.php file.

You can upload your themes to /var/www/html/wp-content/themes.


Once WordPress files are uploaded, you must change their ownership before installing WordPress.

Use the following shell script…


cd /var/www/html

chown -R apache *
chgrp -R apache *
find . -type d -exec chmod 2775 {} \;
find . -type f -exec chmod 0664 {} \;
if [ -f "./.htaccess" ] ; then
chown apache .htaccess
chgrp apache .htaccess
chmod 664 .htaccess
chmod 777 *.sh
chown root
chgrp root
chmod 770

[save and exit the editor, then run the script]

chmod 775
ls -la

After the install you will have a basic .htaccess file you can view with:

ls -la

cat .htaccess

This file can have additional security added such as:

Use your own domain name below. Use your instance’s Amazon static IP address (shown as and your own static IP address (shown as .yyy.yyy.yyy.yyy) in the examples. I have included a couple of entries for IP2Location if that is configured, showing blocks to Russia and China.

.htaccess errors will stop the web page display, or may give a blank page. Just fix the file until the page works.

RewriteEngine on
RewriteCond %{HTTPS} off
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$$1 [R,L]

RewriteRule ^(.*)$ [L]
RewriteRule ^(.*)$ [L]

Options -Indexes

RewriteRule ^wp-admin/install\.php$ - [F]
RewriteRule ^wp-admin/includes/ - [F]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F]
RewriteRule ^wp-includes/theme-compat/ - [F]
RewriteRule ^wp\-content/uploads/.*\.(?:php[1-7]?|pht|phtml?|phps)$ - [NC,F]
RewriteRule ^wp\-content/themes/.*\.(?:php[1-7]?|pht|phtml?|phps)$ - [NC,F]

RewriteCond %{QUERY_STRING} https?: [OR]
RewriteCond %{QUERY_STRING} (<|%3C)script(>|%3E) [NC,OR]
RewriteCond %{QUERY_STRING} mosConfig_[a-zA-Z_]{1,21}(=|%3D) [NC,OR]
RewriteCond %{QUERY_STRING} base64_decode\( [NC,OR]
RewriteCond %{QUERY_STRING} %24&x [NC,OR]
RewriteCond %{QUERY_STRING} (encode|localhost|loopback) [NC,OR]
RewriteCond %{QUERY_STRING} (concat|insert|union|declare) [NC,OR]
RewriteCond %{QUERY_STRING} %[01][0-9A-F] [NC]
RewriteCond %{QUERY_STRING} !^loggedout=true
RewriteCond %{QUERY_STRING} !^action=jetpack-sso
RewriteCond %{QUERY_STRING} !^action=rp
RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_
RewriteCond %{HTTP_REFERER} !^http://maps\.googleapis\.com
RewriteRule ^.* - [F]

allow from all

# deny from entries can go anywhere from here... An example is shown.

# attacks - msn
deny from

<Files wp-login.php>
        order deny,allow
        allow from yyy.yyy.yyy.yyy
        deny from all

<Files xmlrpc.php>
        order deny,allow
        allow from
        allow from yyy.yyy.yyy.yyy
        deny from all

<Files wp-cron.php>
        order deny,allow
        allow from yyy.yyy.yyy.yyy
        deny from all
<Files admin-ajax.php>
        order allow,deny
        allow from all
        satisfy any
<Files wp-config.php>
        Order allow,deny
        allow from yyy.yyy.yyy.yyy
        Deny from all
<Files error_log>
        Order allow,deny
        allow from yyy.yyy.yyy.yyy
        Deny from all
<files .htaccess>
        <IfModule mod_authz_core.c>
                Require all denied
        <IfModule !mod_authz_core.c>
                Order allow,deny
                Deny from all
<files readme.html>
        <IfModule mod_authz_core.c>
                Require all denied
        <IfModule !mod_authz_core.c>
                Order allow,deny
                Deny from all

# Disable Directory Browsing
Options -Indexes

If you install a caching plugin, such as W3 Total Cache, adding Browser Cache will add important caching instructions to your .htaccess file.
It is however possible to manually add those same entries (less any branding or specific plugin entries) but it is easier to let the plugin do it.

Finally, I append values to the /var/www/html/wp-config.php file, as shown, after the line: require_once ABSPATH . ‘wp-settings.php’;

The e-mail private password key is used with the SMTP WP plugin. Use your own SES/IAM values as required.

SMTPPASSWORD is yur own IAM value. The public key would be entered into the plugin settings.

vi /var/www/html/wp-config.php

define('WP_MEMORY_LIMIT', '256M');
define('DISALLOW_FILE_EDIT', true);
define('AUTOSAVE_INTERVAL', 300);
/** define('DISABLE_WP_CRON', true); */
define( 'WPMS_ON', true );

[save and exit - these are appended to the file at the end]
Using MS Exchange for Postfix

If you have connected your domain to MS Exchange, then the above postfix configurations need modifications.

sasl_passwd uses something like this, if your domain was


Then hash the file as above with postmap.

Then modify /etc/postfix/ and restart the service.

smtp_tls_security_level = may

[make sure you have your certificates listed, e.g. smtpd_tls_cert_file = /etc/pki/tls/certs/domain_au.crt smtpd_tls_key_file = /etc/pki/tls/private/domain_au.key if using Comodo type certificates for]

relayhost = []:25

[where this is a copy of your DNS entry in Route53 (or wherever) and we use port 25 only.]

[Then test like this after systemctl stop postfix; systemctl start postfix;
where me@domainau is your primary or alias in MS Exchange.]

sendmail -f
Subject: postfix test
This is a postfix test

[then check:]


cat /var/log/mail.log  [you may need to view it a few times if timeouts are involved etc.]

You can now send emails from shell scripts. This does no effect the WordPress contact form setups that use the SMTP Mail Plugin’s “Other SMPT”
settings going through the AWS SES relay without MS Exchange involved.

Start typing and press Enter to search