How to install Amazon Linux 2022 AL22 & WordPress

 In Amazon AWS

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

This article is based on years of work. I hope it may assist some. It is not the only way to configure WordPress of course.

Important Notes 2023

If installing Comodo (or Sectigo) SSL please see my post at:
It is very easy to generate files using the openssl req command as shown at the top of that post, and how we have to add a new entry into the CAA record for sectigo.

Letsencrypt certificates appear not to be working on current versions of Linux2, but do work on Linux2022. I have not worked out why my sites have the problem.

Part A – Basic Configurations & Package installations

These steps are the first stage for configurations on Linux 2022 and WordPress.

We assert that you have already worked out how to install AL22 on a t4 instance (suitable for my audience) that is freshly up and running, and that you have learnt how to set up the general Amazon environment for things like IAM, SES, DNS and so on. ( I have notes on this website, with screen shots, on those tasks.)

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

You may do otherwise. 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.

These configurations are slightly different to AWS Linux2.

If a few things are not working, not only look at error messages, make use of forums, but reboot the instance.

The cut & paste line items below are via the Enlighter plugin. I am able to use this plugin on the current Jupiter theme, but was not able to use it on the Avada theme. (I do not use JupiterX)

Basic Setups
[log in as root: sudo su]

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 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 no longer use memcached as there is no php-memcahced.
If there were, you would update the inbound rules of the EC2 instance security group and dnf install memcahced php-memcached and configure the last line of /etc/sysconfig/memcached.
- 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.]

wget -O epel.rpm –nv \

rpm -ihv --nodeps ./epel-release-latest-8.noarch.rpm
dnf install -y cronie cronie-anacron

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

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 {} \;

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

[for certbot/let'sencrypt and PIP rather than snapd]
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

dnf check-release-update
[repeat these steps till no upgrades are left.
e.g. dnf update --releasever=2022.0.20221019]

[log back in as root]

Part B – Next Configurations

These steps will set up the following:


Linux 2022 at time of writing does not have php-memcached, php-ImageMagick, php-acpu, or certbot. Linux 2022 automatically installs required packages that had to be manually installed in Linux2.

Note that mariadb is provisioned at v10.5 (at time of writing) so there is no need to upgrade from v5 as was the case in Linux2.

We will assume Let’s Encrypt & Certbot for the SSL certificate. We install PIP to do this. We choose not to install snapd.
(Otherwise refer to my other materials for installing Comodo Positive DL certificates.)

Part B - Next Configurations

Use the vi editor, nano or other on the following files…


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

[You may have other values]




OPTIONS="-l -U 0,::1"


# SELINUX=permissive


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

[I realise there is a semicolon at the end of one of the above lines]


(use your own domain name)

[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


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

LoadModule mpm_prefork_module modules/
#LoadModule mpm_worker_module modules/

[append these for performance:]

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


[typicall heartbeat has caused continuous error log messages]

# LoadModule lbmethod_heartbeat_module modules/

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.

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


Note, until we are ready to install SSL and restart httpd, I move the file to ssl.bak. When I am ready I move it to ssl.conf.

Certbot appends lines, so I will show what it looks like at the end of the file. Check the conf.d directory for spurious ssl config files if certbot configs go wrong and delete them. We don’t want multiple domainname-001 type files hanging around by mistake as certbot installs can be messy.

Note: you can run http:// while installing mariadb further below, but have your own checklist t remind you of configs t0 complete. You should not log into phpMyAdmin until SSL is configured.

[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]

<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 if using certbot, otherwise we change them to our own certificates from Comodo or Sertigo. It is easy to create these files with an editor, but you may be using bundled files as well.
# SSLCertificateKeyFile /etc/pki/tls/private/localhost.key
#SSLCertificateChainFile /etc/pki/tls/certs/server-chain.crt 

# just before the last line of </VirtualHost>, certbot should add these lines during its configurations, and if not you can 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.

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

You may notice some differences to Linux2.

Part C – Database, Postfix, phpMyAdmin

These steps will set up the following:


Part C - Next Configurations

Configuring Postfix – we use this to send internal emails.


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



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

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 start mariadb
systemctl enable mariadb
systemctl enable httpd
systemctl enable php-fpm
systemctl enable memcached 

[Memcached is optional. You do not have to install memacached.]

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

SSL must be installed. You can reboot your server after it is in place.

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

Part D – Free Certbot SSL

These steps will set up the free certbot certificates. The above instructions already installed PIP for installing certbot code.

Certbot installs to me are a mess, but they work. One has to fix things up or re0run commands when things go askew. The main thing is to get the certificates for mydomain and www.mydomain into the letsencrypt directories and the entries in the ssl.conf file.

Certbot will ask for your e-mail address – any email is ok. You can use the same address for all your domains if you like.

My notes above show that the ssl.conf file will use Comodo certificates in /etc/pki/tls/private and certs directories if not using certbot. It takes some testing to work out what the contents of the .key and .cert files need to be, and how to generate them.

SSL - certbot

DNS records:

[These values must be in your Route53 (or other provider's) CAA records:]

0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issuewild ";"

If using Comodo, use these: (I have not used Sertigo so cannot give those configs)]

0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issue ""
0 issuewild ";"

Install the certificates:

This is fiddly…

I like to start httpd without ssl.conf (move it to ssl.conf.o) then copy the ssl.conf.o file to ssl.conf after httpd starts, so that certbot can access it.

Then make sure ssl.conf has all the correct entries before running certbot for ssl (e.g. configure it for httpd as shown in this article).

Then run the certbot command as shown, (of course without the dry run when it says there is no failure).
Then run:

certbot –apache

Then edit the ssl.conf file to remove the entries and delete them from /etc/letsencrypt/live, and past the entries below into the ssl.conf file. This does work!!

[Use your own domain name. First use the dry run to test, then run it again without the --dry-run option:]

/usr/bin/certbot -v certonly -d -d --webroot -w /var/www/html --dry-run

[Certbot has lots of instructions you can experiment with, but at the end of the day, you want the above dry run to work, and you want to see entries in ssl.conf. e.g.:
ServerAlias mydomain,au
SSLCertificateFile /etc/letsencrypt/live/
SSLCertificateKeyFile /etc/letsencrypt/live/
Include /etc/letsencrypt/options-ssl-apache.conf

[If you have othe ssl files created by certbot in /etc/httpd/conf.d, you can move them so they are not accessed by Apache (httpd) and try again.
For isntance, when testing, you possibly may try certbot --apache.
If you have multiple attempts, you may have too many certificate files, ending in sequence numbers. Just delete those references and files.
All you need to end up with is the above ssl.conf entries, and /etc/letsencrypt/live/, then the only files under that directory are cert.pem  chain.pem  fullchain.pem  privkey.pem  README.
There is no complication here. Just end up with these values and files.]

[I see no need to have the DNS plugins certbot describes. We cannot install the plugin on AL22, but we could on Linux2. To what purpose would we need it?]

[Set up crontab scripting to renew the certificates very 60 days. If longer, you get annoying e-mails. Remember to delete certificates and deregister the domain name if you ever decide to remove them.
Simply type certbot --help to see the options.]

crontab -e
15 1 * * * /home/ec2-user/ >/dev/null 2>&1

[save and exit the editor after adding the above line]

crontab -l

cd /home/ec2-user

[use your own domain name below]


c1=`head -1 /home/ec2-user/certbot.dat`
let c1=$c1+1
if [ "$c1" = "60" ] ;
echo "0" > /home/ec2-user/certbot.dat
echo "Certbot Renewal" $d >> /home/ec2-user/info.log
# sudo /usr/bin/certbot -v certonly -d -d --webroot -w /var/www/html
sudo /usr/bin/certbot -v renew --webroot -w /var/www/html
sudo /usr/bin/systemctl restart httpd >/dev/null 2>&1
sudo /usr/bin/openssl x509 -noout -dates -in /etc/letsencrypt/live/ >> /home/ec2-user/info.log
echo "Certbot day $c1 of 60" >> /home/ec2-user/info.log
echo $c1 > /home/ec2-user/certbot.dat

[save and exit the above new file
Note: renew option is required for the shell script. I have tested using the DNS plugin ok, but am waiting to confirm the above line in the script will work for the webroot option. 24th Jan 2023]

touch info.log

[If you just created the certificates, you will use the numeral 0 on the file below, representing the first of the 60 days.]

vi certbot.dat
[save and exit the editor]

chown root *sh *log *dat
chgrp ec2-user *sh *log *dat
chmod 777 *sh *log *dat

[Of course you may use 775 on the shell scripts if you wish]

Test your certificates at
You want an A rating, not an A+.

If you want the business name in the SSL certificate, you need to purchase one. The lowest cost certificate is a Positive DV certificate.
If you want multiple subdomains, that is another cost, and requires use of the openssl command to generate them unless the SSL provider has all those setups done for you.

Part E – Install WordPress

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.

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

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


certbot is tricky, but it does work.
Some clues…

Try /usr/bin/certbot -v certonly first, with httpd running without ssl.conf, then cp -p ssl.conf.o to ssl.conf with the correct entries for SSL and no certificates referenced in it from /etc/pki.
Then run certbot –apache.
If ssl.conf does not exist, it created ssl-le-ssl.conf, so you then delete that and manually cp -p ssl.conf.o to ssl.conf and add the certbot SSL lines to the file, and change any mistakes from and delete any such files from /etc/letsencrypt. This is the general approach.
ssl-le-ssl.conf will cause too many redirects if it exists and you put your normal configs into ssl.conf.

You can also do certbot -dergister for the domain name if it existed once before and you are re-configuring it and having major issues.

Also, you need /var/www/html to have owner and group as apache.

My older notes for php7.4 are:

To upgrade mariadb to v10, run the commands from here, then do the secure install:


Start typing and press Enter to search