This article shows the Linux 2023 packages we install & configure as used by our WordPress servers.
Linux 2023 Packages
Installation and Configuration details are always changing. These are okay at time of writing.
We use more than a vanilla installation for WordPress. Our packages include:
Nginx 1.29 or above
Maria DB v11
phpMyAdmin (too manage the databases)
php, and php-fpm used to process php from Nginx
memcached
opcache (we never recommend apcu)
php-* packages
network and other various utilities such as jp, dig, whereis, ipcalc, dos2unix
ImageMagick
nftables
The AWS-SDK – to send email alerts instead of using Postfix, and access to s3 buckets if the same is programmed in shell scripts such as making file/data backups to buckets.
Optionally the package for use of EC2 EFS disk – handy if you need some temporary extra space
We are able to install NFS software for mounting an S3 bucket but prefer not to
We do not install AWS credentials any more in ~/.aws, and although we can use a credential in the WP MAIL SMTP plugin, we prefer not to.
This article is based on a t4g.micro ARM architecture Linux 2023 instance.
Please see the article on disk swap space first.
We will go forward on the basis that Amazon AWS resources will be accessed via an attached EC2 IAM Role, with Linux access via the aws-sdk package using php or bash shell scripts.
Your EC2 Instance Security Group
Your EC2 Security Group(s) will need these settings. You attach these to the Instance. These can be modified.
HTTP TCP 80 ::/0 HTTP TCP 80 0.0.0.0/0 HTTPS TCP 443 ::/0 HTTPS TCP 443 0.0.0.0/0 Custom TCP TCP 11211 127.0.0.0/16 --> for memcached SSH TCP 22 YOUR_IP --> e.g. your broadband provider's ip address, limiting who can access SSH. Do not leave open to the public as this will flood the resources. NFS TCP 2049 0.0.0.0/0 --> if mounting an EFS disk
Initial Steps
See our article on Disk Swap Space – required before adding packages.
Add your timezone: (e.g. Brisbane)
a="Australia/Brisbane";export a;echo $a ln -sf /usr/share/zoneinfo/$a /etc/localtime date
Modify /etc/bashrc to show your domain name as used with the terminal SSH prompt.
Replace MYDOMAIN with your own name - any name is fine] vi /etc/bashrc # [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@\h \W]\\$ " [ "$PS1" = "\\s-\\v\\\$ " ] && PS1="[\u@MYDOMAIN: \w]\\$ " [save and exit]
Log out, and back in to see the change.
Fix SELINUX:
vi /etc/selinux/config # SELINUX=permissive SELINUX=disabled [save and exit]
We now update the system to a current level of the OS:
dnf update dnf check-release-update --> For example: dnf upgrade --releasever=2023.7.20250512 - a long time on a nano instance if there are many prior months of updates having occured.
journalctl and dmesg system files
It is helpful to review any memory errors using “dmesg -T” or “journalctl -e”.
The journal can become too huge to work with. You can configure it to empty every 3 days, as an example.
To do this, uncomment the following line with the value you’d like:
vi /etc/systemd/journald.conf
MaxRetentionSec=3d
You could follow this manually with: “journalctl –vacuum-time=3d”
You cannot use scripts to clean these logs.
To empty the dmesg messages, “dmesg -C”.
dmesg -T shows any errors with the date and time.
--> at the top of the file: cd /etc/ssh vi ssh_config ClientAliveInterval 120 ClientAliveCountMax 360 [save and exit] vi sshd_config #PermitRootLogin prohibit-password PermitRootLogin yes ClientAliveInterval 120 ClientAliveCountMax 360 #ClientAliveInterval 0 #ClientAliveCountMax 3 [save and exit] systemctl daemon-reload systemctl reload sshd systemctl restart sshd --> If you get this error: (unlikely) [sss_cache] [sysdb_domain_cache_connect] (0x0010): DB version too old [0.22], expected [0.23] for domain implicit_files! Higher version of database is expected! In order to upgrade the database, you must run SSSD. Removing cache files in /var/lib/sss/db should fix the issue, but note that removing cache files will also remove all of your cached credentials. Could not open available domains the fiix this as follows: cd /var/lib/sss/db rm * sss_cache -E <--
Linux 2023 dnf Packages
At time of writing, we use php8.4. If you need an earlier version for software compatibility, use 8.2 in the lines below.
dnf search php
--> This will show the available versions.
dnf install php8.4
php -v
PHP 8.4.14 (cli) (built: Oct 21 2025 19:23:55) (NTS gcc aarch64)
Copyright (c) The PHP Group
Built by Amazon Linux
Zend Engine v4.4.14, Copyright (c) Zend Technologies
with Zend OPcache v8.4.14, Copyright (c), by Zend Technologies
-->
If installing an earlier version, it is still possible to knock it out and find php is at the current version. You would need to use AI and internet articles to get back to your older version.
I personally do not like upgrading php levels, e.g. from 8.2 to 8.4, but it can be done. You need to ensure you have not wamring messages hanging around.
I also do not like upgrading Debian OS, e.g. from Debian 11 to Debian 13, but again, it can be done.
<--
From this point on, installation will usually vary depending on how developers change the packages.
You need problem solving and research skills. Don’tt worry if it seems a bit messy. If things completely fail, (unlikely) you can delete the instance and start again.
NEVER ACCEPT AN INSTANCE THAT IS INITIALLY PERFORMING SLOWLY WHEN INSTALLING PACKAGES
An instance is basically a hard disk volume – you can check it is 100% available in the EC2 Console’s Volumes menu.
If it is 100% available but is too slow while working with it, delete it and start a new instance. We do experience hardware and network failures at times. Also check the IP address is not blacklisted (see mxtoolbox.com).
After you have installed your domain and DNS entries, you can check if there is a health issue with domain name resolving using this command:
getent hosts mydomain.com – use your own domain name
xxx.xxx.xxx.xxx mydomain.com – should be listed with your instance public IP address.
Sometimes we see error messages showing us packages we need to install first. Some messages should not be acted on, for instance, php.ini should not need .so files added to it. It is okay to use the isntall command on previously installed packages, or to delete and install again, or to “reinstall”.
dnf install -y php-pear --> we will assume for EC2 Linux 2023, no need to specify php8.4-SOMEPACKAGE, that it knows how to do so with php-SMEPACKAGE instead. dnf install -y php-mysqli php-mbstring php-cli php-pdo php-fpm php-json php-mysqlnd php-opcache dnf install -y gd libzip-devel kernel-devel php-gd dnf install -y cronie cronie-anacron dnf -y install pcre-devel gcc zlib zlib-devel dnf update dnf search mariadb --> At time of writing, we use mariadb1011 - some differences to prior db commands we may have been used to using in shell scripts. dnf install -y mariadb1011 dnf install -y dnf install -y mariadb1011-server --> keep in mind there will be dfferences to a Debian installation. Debian would need php8.4-cli instead of php-cli as an example, if wanting to use 8.4. dnf -y install libjpeg-turbo-utils php-devel php-zip pecl install zip pecl channel-update pecl.php.net --> Don't worry about error messages. We do not add .so entries into php.ini for extensions, but in this case we do. We have .so calls in config files in /etc/php.d instead - check there are no duplicates later when running systemctl status commands. We do not need to add extension=zip.so; to php.ini <-- --> Install certbot for Let's Encrypt SSL dnf -y install python3 python3-devel augeas-libs python3 -m venv /opt/certbot/ /opt/certbot/bin/pip install --upgrade pip /opt/certbot/bin/pip install certbot dnf -y install letsencrypt whereis certbot certbot: /usr/bin/certbot /opt/certbot/bin/certbot --> If we see a warning about Python v3.9 being sunset, it is not our concern, but that of the developers.
We are not configuring just yet, only installing packages.
We now install memcached – this is surely going to change over time, but here it is for now: (we must use memcached with nginx)
cd /home/ec2-user
--> There are two entries below for php8.4. You must edit for your own php version if different. Ignore error messages.
dnf -y install php8.4-devel php-pear gcc && \
pear update channels && \
pecl update channels && \
\
# build + install igbinary for use by php-memcached
pecl install igbinary && \
echo "extension=igbinary.so" | sudo tee /etc/php.d/20-igbinary.ini && \
\
# build + install msgpack for use by php-memcached
pecl install msgpack && \
echo "extension=msgpack.so" | sudo tee /etc/php.d/20-msgpack.ini && \
\
# build + install php-memcached
dnf install -q -y memcached-devel memcached \
libmemcached-awesome-devel libmemcached-awesome \
zlib-devel zlib cyrus-sasl cyrus-sasl-devel \
libevent libevent-devel && \
pecl install --configureoptions \
'with-zlib-dir="no" \
with-system-fastlz="no" \
with-libmemcached-dir="no" \
enable-memcached-igbinary="yes" \
enable-memcached-msgpack="yes" \
enable-memcached-json="yes" \
enable-memcached-protocol="yes" \
enable-memcached-sasl="yes" \
enable-memcached-session="yes"' memcached && \
echo "extension=memcached.so" | sudo tee /etc/php.d/25-memcached.ini && \
\
# clean up php dev tools and the dnf cache
dnf remove -y gcc php8.4-devel php-pear libzip-devel \
memcached-devel libmemcached-awesome-devel zlib-devel \
cyrus-sasl-devel libevent-devel && \
dnf autoremove -y && dnf clean all && rm -rf /var/cache/dnf
--> Then run:
pear update-channels
pecl update-channels
--> Now run these commands to fix errors:
dnf install -q -y memcached-devel libmemcached-awesome-devel zlib-devel cyrus-sasl-devel libevent-devel
/usr/bin/yes 'no' | pecl install --configureoptions 'enable-memcached-igbinary="yes" enable-memcached-msgpack="yes" enable-memcached-json="yes" enable-memcached-protocol="yes" enable-memcached-sasl="yes" enable-memcached-session="yes"' memcached
--> Run this command to add the .so file to php if it is not already listed in /etc/php.d
echo 'extension=memcached.so' > /etc/php.d/41-memcached.ini
--> Verify memcached is present:
cd /
find . -name memcached.so -print
--> You will see:
./usr/lib64/php8.4/modules/memcached.so
--> enable memcached
systemctl enable memcached
Install additional packages:
cd /home/ec2-user dnf install -y nftables dnf install -y nftables-devel dnf install -y jp dnf install dnsutils -y dnf install ipcalc -y dnf install dos2unix -y dnf install whois -y --> For use of aws-sdk: dnf install composer -y --> Verify installation composer --version --> after nginx is installed, you want /var/www to be chmod 2775; chown nginx:nginx, and then install the aws-sdk: cd /var/www mkdir aws-sdk cd aws-sdk composer require aws/aws-sdk-php --> This will create a vendor/ directory with the SDK and an autoload.php file. Composer will handle all dependencies automatically. --> the aws-sdk package can be accessed from other directories using soft links, e.g.: cd /var/www/html ln -s /var/www/aws-sdk aws-sdk ls -l --> for graphics dnf update -y dnf install -y ImageMagick ImageMagick-devel dnf install -y php-devel php-pear gcc pecl install imagick --> check /etc/php.d has a file with extension=imagick.so in it, and if not: sudo tee /etc/php.d/30-imagick.ini > /dev/null <<EOF ; Enable the ImageMagick extension module extension=imagick.so EOF dnf install php-igbinary -y dnf install php-msgpack -y systemctl restart php-fpm systemctl enable php-fpm php -m | grep imagick php -v
Please reboot your server from the EC2 Console
After installing so many packages, reboot the server and check everything is fine with:
php -v
dnf update
It is best to reboot again after installing and configuring mariadb, php configs, phpMyAdmin, opcache, and Nginx.
mariadb, opcache, memcached, php configurations
We usually install a package, and then configure it.
This is the first step for mariadb:
systemctl start mariadb --> We are still using mysql_ but this may change to a slightly different command set later. *** Important Note*** some installations now require "mariadb-secure-installation" and use of the work mariadb instead of mysql such as mysqlcheck <-- mysql_secure_installation ["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] --> Note that we now start and enable all our services. If httpd is not loading SSL correctly, you need to problem solve. systemctl stop mariadb systemctl start mariadb systemctl enable mariadb --> you will see: Created symlink /etc/systemd/system/mysql.service → /usr/lib/systemd/system/mariadb.service. Created symlink /etc/systemd/system/mysqld.service → /usr/lib/systemd/system/mariadb.service. Created symlink /etc/systemd/system/multi-user.target.wants/mariadb.service → /usr/lib/systemd/system/mariadb.service.
These are recommended configurations for a t4g.micro instance:
cd /etc cp -p my.cnf my.cnf.bak vi my.cnf [mysqld] # --- Core memory --- innodb_buffer_pool_size = 256M innodb_buffer_pool_instances = 1 aria-pagecache-buffer-size = 32M # --- Disable legacy query cache (saves RAM) --- query_cache_type = 0 query_cache_size = 0 # --- Connection & thread control --- max_connections = 20 thread_cache_size = 20 # --- Per-connection buffers (keep small!) --- tmp_table_size = 16M max_heap_table_size = 16M sort_buffer_size = 1M join_buffer_size = 512K read_buffer_size = 128K read_rnd_buffer_size = 256K # --- Table & index --- table_open_cache = 200 key_buffer_size = 8M # --- InnoDB safety & IO --- innodb_log_file_size = 64M innodb_log_buffer_size = 8M innodb_flush_log_at_trx_commit = 2 innodb_file_per_table = 1 innodb_flush_method = O_DIRECT innodb_flush_neighbors = 1 innodb_io_capacity = 200 # --- Disable binary logging --- skip-log-bin # --- Logging --- log_error=/var/log/mariadb-error.log log_warnings=9 [client-server] !includedir /etc/my.cnf.d [save and exit]
Let’s add OOM settings for mariadb:
--> likely the nano editor. Go to the space below the first comments and add the lines below. To save, type CTRL-O, ENTER, CRTL-X to exit systemctl edit mariadb [Service] Restart=on-failure OOMScoreAdjust=-1000 RestartSec=5s Environment="MYSQLD_OPTS=" Environment="_WSREP_NEW_CLUSTER=" [save and exit] systemctl daemon-reload systemctl restart mariadb cat /etc/systemd/system/mariadb.service.d/override.conf --> check the error log is okay: /var/log/mariadb/mariadb.log
We need “systemctl restart mariadb” to have changes put into effect.
Check the status is ok:
systemctl status -l mariadb
● mariadb.service - MariaDB 10.11 database server
Loaded: loaded (/usr/lib/systemd/system/mariadb.service; enabled; preset: disabled)
Drop-In: /etc/systemd/system/mariadb.service.d
└─override.conf
Active: active (running) since Sun 2026-01-25 15:52:00 AEST; 16h ago
Docs: man:mariadbd(8)
https://mariadb.com/kb/en/library/systemd/
Main PID: 84144 (mariadbd)
Status: "Taking your SQL requests now..."
Tasks: 8 (limit: 999)
Memory: 54.7M
CPU: 6.689s
CGroup: /system.slice/mariadb.service
└─84144 /usr/libexec/mariadbd --basedir=/usr
Jan 25 15:51:59 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Starting mariadb.service - MariaDB 10.11 database server...
Jan 25 15:51:59 ip-172-31-34-206.ap-southeast-2.compute.internal mariadb-prepare-db-dir[84109]: Database MariaDB is probably initialized in /var/lib/mysql already, nothing is done.
Jan 25 15:51:59 ip-172-31-34-206.ap-southeast-2.compute.internal mariadb-prepare-db-dir[84109]: If this is not the case, make sure the /var/lib/mysql is empty before running mariadb-prepare-db-dir.
Jan 25 15:52:00 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Started mariadb.service - MariaDB 10.11 database server.
We need memcached (not memcache) for Nginx configurations and WordPress plugins that may work better as well.
Your EC2 Securtity Group needs to open the memcached port. Use these settings:
Custom TCP TCP 11211 127.0.0.0/16 memcached
memcached
--> this may change in the future if dnf install memcached is available. However: (use your correct php version below, such as 8.4) Ignore error messages.
cd /home/ec2-user
dnf -y install php8.4-devel php-pear gcc && \
pear update channels && \
pecl update channels && \
\
# build + install igbinary for use by php-memcached
pecl install igbinary && \
echo "extension=igbinary.so" | sudo tee /etc/php.d/20-igbinary.ini && \
\
# build + install msgpack for use by php-memcached
pecl install msgpack && \
echo "extension=msgpack.so" | sudo tee /etc/php.d/20-msgpack.ini && \
\
# build + install php-memcached
dnf install -q -y memcached-devel memcached \
libmemcached-awesome-devel libmemcached-awesome \
zlib-devel zlib cyrus-sasl cyrus-sasl-devel \
libevent libevent-devel && \
pecl install --configureoptions \
'with-zlib-dir="no" \
with-system-fastlz="no" \
with-libmemcached-dir="no" \
enable-memcached-igbinary="yes" \
enable-memcached-msgpack="yes" \
enable-memcached-json="yes" \
enable-memcached-protocol="yes" \
enable-memcached-sasl="yes" \
enable-memcached-session="yes"' memcached && \
echo "extension=memcached.so" | sudo tee /etc/php.d/25-memcached.ini && \
\
# clean up php dev tools and the dnf cache
dnf remove -y gcc php8.4-devel php-pear libzip-devel \
memcached-devel libmemcached-awesome-devel zlib-devel \
cyrus-sasl-devel libevent-devel && \
dnf autoremove -y && dnf clean all && rm -rf /var/cache/dnf
--> Then run:
pear update-channels
pecl update-channels
--> Now run these commands to fix errors:
dnf install -q -y memcached-devel libmemcached-awesome-devel zlib-devel cyrus-sasl-devel libevent-devel
/usr/bin/yes 'no' | pecl install --configureoptions 'enable-memcached-igbinary="yes" enable-memcached-msgpack="yes" enable-memcached-json="yes" enable-memcached-protocol="yes" enable-memcached-sasl="yes" enable-memcached-session="yes"' memcached
--> Run this command to add the .so file to php ONLY if you do not see it in /etc/php.d .ini files
echo 'extension=memcached.so' > /etc/php.d/41-memcached.ini
--> Verify memcached is present:
cd /
find . -name memcached.so -print
--> You will see:
./usr/lib64/php8.4/modules/memcached.so
systemctl enable memcached
memcached configuration:
cd /etc/sysconfig vi memcached PORT="11211" USER="memcached" MAXCONN="1024" CACHESIZE="64" OPTIONS="-l 127.0.0.1 -U 0,::1" [save and exit] systemctl restart memcached ps -ef|grep memcached
opcache configuration:
cd /etc/php.d ls vi 10-opcache.ini zend_extension=opcache opcache.enable=1 opcache.enable_cli=1 opcache.memory_consumption=128 opcache.interned_strings_buffer=16 opcache.max_accelerated_files=10000 opcache.validate_timestamps=1 opcache.revalidate_freq=2 realpath_cache_size = 4096K realpath_cache_ttl = 600 opcache.blacklist_filename=/etc/php.d/opcache*.blacklist opcache.huge_code_pages=0 [save and exit]
These are my recommended php.ini configurations.
If the https strict use is an issue, omit it. (We are not behind a proxy service like Cloudflare’s option.)
--> I prefer 256M as the PHP memory. Complex web pages may need more - typically the WordPress editing suddenly gives a blank page with an error message. A small instance has memory demands, fo 512M can cause issues. cd /etc cp -p php.ini php.ini.bak vi php.ini date.timezone = Australia/Brisbane session.cookie_httponly = 1 session.cookie_secure = 1 session.cookie_samesite = "Strict" session.sid_length = 48 session.sid_bits_per_character = 6 session.use_strict_mode = 1 max_execution_time = 60 max_input_time = 60 max_input_vars = 2800 memory_limit = 256M post_max_size = 128M upload_max_filesize = 128M ; session.save_handler = files session.save_handler = memcached session.save_path = "127.0.0.1:11211" [save and exit]
There are several security configurations above. Check these are already configured:
session.use_trans_sid = 0
session.use_strict_mode = 1
We make sure nginx sees the enforced use of https from the above configuration, with “fastcgi_param HTTPS on;” (refer to the nginx.conf file)
Configure /etc/php-fpm.d/www.conf. This applies to an “on demand” php server with small resources on a t4.micro server.
--> we will change pm = dynamic to ondemand and comment out a couple of lines as a result.
--> note the value for listen = /run/php-fpm/www.sock -- we use this in nginx.conf - debisn uses a different socket.
cd /etc/php-fpm.d
cp -p www.conf www.conf.bak
vi www.conf
user = nginx
group = nginx
; listen.owner = apache
; listen.group = apache
; listen.mode = 0660
listen.acl_users = nginx
pm = ondemand
pm.max_children = 6
; pm.start_servers = 5
; pm.min_spare_servers = 5
; pm.max_spare_servers = 35
pm.process_idle_timeout = 10s;
pm.max_requests = 300
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_admin_value[memory_limit] = 256M
php_admin_value[disable_functions] = exec,passthru,system
php_admin_flag[allow_url_fopen] = off
php_value[session.save_handler] = memcached
php_value[session.save_path] = 127.0.0.1:11211
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
[save and exit]
cd ..
--> place lines anywhere around the emergency lines in the file. Lets us refresh php without errors, and used in crontab.
--> Do not duplicate the process_control_timeout entry.
vi php-fpm.conf
emergency_restart_threshold = 10
emergency_restart_interval = 1m
process_control_timeout = 60s
[save and exit]
systemctl restart php-fpm
--> check it is enabled:
systemctl enable php-fpm
systemctl status -l php-fpm
● php-fpm.service - The PHP FastCGI Process Manager
Loaded: loaded (/usr/lib/systemd/system/php-fpm.service; enabled; preset: disabled)
Active: active (running) since Mon 2026-01-26 08:35:02 AEST; 2s ago
Main PID: 128828 (php-fpm)
Status: "Ready to handle connections"
Tasks: 1 (limit: 999)
Memory: 10.6M
CPU: 38ms
CGroup: /system.slice/php-fpm.service
└─128828 "php-fpm: master process (/etc/php-fpm.conf)"
Jan 26 08:35:02 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Starting php-fpm.service - The PHP FastCGI Process Manager...
Jan 26 08:35:02 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Started php-fpm.service - The PHP FastCGI Process Manager.
We now add the OOM restart settings if memory crashes (can be seen in error logs like:
dmesg -T (use -C to clear the logs)
jounrnalctl -e (use another option for the archived logs)
/var/log/php-fpm/error.log, and mariadb’s log
systemctl edit php-fpm.service [Service] MemoryMax=975M MemoryHigh=950M [save and exit - either nano or vi depending on the OS} --> fyi: systemctl edit php8.4-fpm.service would be used for Debian php8.4 --> After editing, reload php: systemctl daemon-reload systemctl restart php-fpm systemctl status -l php-fpm
If we had an instance with 2GB RAM, we could use 1500M as a larger value.
When we upgrade the packages, some PHP entries can be lost. This should protect them:
vi /etc/tmpfiles.d/php-fpm-nginx.conf # /etc/tmpfiles.d/php-fpm-nginx.conf d /var/lib/php/session 0770 nginx nginx - d /var/lib/php/opcache 0770 nginx nginx - d /var/lib/php/wsdlcache 0770 nginx nginx - [save and exit] systemd-tmpfiles --create
If we wish to dedicate a www.conf pool to editing WordPress, I have shown in nginx.conf (or other domain include files) how you can have another php pool, with an example where we copy www.conf to admin.conf, and rename the socket to something like admin.sock in this file. Then in nginx.conf we allocate the pool to WordPress.
We cannot have overly large values for our configurations or we will freeze. WIth larger values you see how much snappier things can be, but they will crash.
Use AI to compare our configurations for a larger EC2 instance, and adjust.
phpMyAdmin package and configurations
CHANGE NOTES
We can now install with dnf install phpmyadmin but there are some differences. See further below.
There are two ways to install phpMyAdmin.
If the dnf package is available you can use dnf -y phpmyadmin.
I have always used the classic method:
cd /usr/share
wget https://www.phpmyadmin.net/downloads/phpMyAdmin-latest-all-languages.tar.gz
ls
--> untar the file your downloaded:
tar xvf .....
--> e.g.: tar xvf phpMyAdmin-latest-all-languages.tar.gz
rm phpMyAdmin-latest-all-languages.tar.gz
mv phpMyAdmin-5.2.3-all-languages phpMyAdmin
(Use the ls command to get the correct file names)
<--
--> Then delete the tar.gz file, then use the Unix command to move the new directory to phpMyAdmin, e.g.: mv untared_file phpMyAdmin
cd phpMyAdmin
mkdir tmp
chmod 777 tmp
cp -p config.sample.inc.php config.inc.php
vi config.inc.php
--> After SaveDir, add TempDir, and the permissions line (DisabledIS line) if later you find you cant edit users or export a database
$cfg['SaveDir'] = '';
$cfg['TempDir'] = '/tmp';
$cfg['Servers'][$i]['DisableIS'] = true;
--> We need to edit the blowfish line.
php -r "echo bin2hex(random_bytes(32));"
--> Then paste this into the blowfish double quotes. It only needs to be a random string at least 32 characters in length. I never use ! characters.
We need to fix permissions from apache to nginx:
--> these may change depending on the OS and installation method cd /var/lib/php ls -l chgrp nginx opcache chgrp nginx session chgrp nginx wsdlcache ls -l
We should not need entries in nginx.conf, but we can add .htpasswd as a second login method if we wish.
We want memcached set in both www.conf and php.ini, and that /etc/php.d has the .ini file with extension=memcached.so in it, and that extension=memcached.so does actually exist.
Here are the changes we need for the dnf method:
-->
Installing the phpmyadmin package has some differences from the older wget method.
We have two directories to review:
cd /etc
ls -l
/etc/phpmyadmin
--> change -rw-r----- 1 root www-data 529 Apr 6 11:56 config-db.php to nginx group:
chgrp nginx config-db.php
cd /var/lib/phpmyadmin
/var/lib/phpmyadmin]# ls -l
total 8
-rw-r----- 1 root www-data 68 Apr 6 11:56 blowfish_secret.inc.php
drwxr-xr-x 2 www-data www-data 4096 Sep 4 2025 tmp
--> Change www-data to nginx (note: previously we did "apt remove *apache2*")
chown nginx:nginx tmp
chgrp nginx blowfish_secret.inc.php
--> Create the blowfish string:
php -r 'echo substr(str_shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"), 0, 32), PHP_EOL;'
--> Add it to the file blowfish_secret.inc.php (remove the existing string)
Install NGINX
Please see our separate article on configuring Nginx. Here we only show how to add the package.
If you use “dnf search nginx”you should be able to see the default package level.
If it is not 1.29 or higher, we use this manual scripting:
--> Install the current Stable version of Nginx:
dnf install -y yum-utils
--> In the vi editing below, we must include the lines that have [....] in them.
--> See: https://nginx.org/en/linux_packages.html#Amazon-Linux
vi /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/amzn/2023/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/amzn/2023/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
priority=9
[save and exit]
yum-config-manager --enable nginx-mainline
--> Now install nginx, and use the Enter key when prompted a few times...
dnf install -y nginx
--> nginx -t should prove it is installed:
nginx -t
--> Nginx can try to start too quickly. See: https://serverfault.com/questions/1042526/open-run-nginx-pid-failed-13-permission-denied. The fix is:
mkdir -p /etc/systemd/system/nginx.service.d
vi /etc/systemd/system/nginx.service.d/override.conf
[Service]
ExecStartPost=/bin/sleep 0.1
[save and exit]
systemctl daemon-reload
systemctl restart nginx
systemctl enable nginx
--> Check no error:
systemctl status -l nginx
--> This is the sort of thing you will see:
● nginx.service - nginx - high performance web server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: disabled)
Drop-In: /etc/systemd/system/nginx.service.d
└─override.conf
/usr/lib/systemd/system/nginx.service.d
└─php-fpm.conf
Active: active (running) since Sat 2026-01-24 14:31:59 AEST; 24s ago
Docs: http://nginx.org/en/docs/
Main PID: 16324 (nginx)
Tasks: 3 (limit: 999)
Memory: 3.0M
CPU: 9ms
CGroup: /system.slice/nginx.service
├─16324 "nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf"
├─16325 "nginx: worker process"
└─16326 "nginx: worker process"
Jan 24 14:31:59 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Starting nginx.service - nginx - high performance web server...
Jan 24 14:31:59 ip-172-31-34-206.ap-southeast-2.compute.internal systemd[1]: Started nginx.service - nginx - high performance web server.
--> When we configure Nginx, like mariadb, there can be messages that seem like a warning that are actually fine.
--> If "php -v" gives a warning, or the log file (/var/log/php-fpm/error.log) you should really aim to fix those.
After nginx is installed, make sure you have /var/www/html where www and html are chmod 2775, and nginx;nginx ownership.
When the webstie is running, you can place a phpinfo.php file into your root, e.g. /var/www/html and check the PHP configuration shows your php.ini changes, opcache, and session.save_handler using memcached:
cd /var/www/html vi info.php <?php phpinfo();?> [save and exit] chmod 664 i* chown nginx:nginx i* https://mydomain.com/info.php mv info.php info.pgp.bak
Make sure your services are enabled so they kick in on a reboot:
systemctl enable nginx systemctl enable php-fpm systemctl enable mariadb systemctl enable memcached
Helpful shell scripts
Restart the services:
cd /home/ec2-user vi restart.sh #!/bin/sh free -m sudo systemctl stop nginx sudo systemctl stop memcached sudo systemctl stop mariadb sudo systemctl stop php-fpm sleep 0.5 sudo systemctl start php-fpm sudo systemctl start mariadb sudo systemctl start memcached sudo systemctl start nginx sudo swapoff -a sleep 1 sudo swapon -a sudo systemctl status -l nginx sudo systemctl status -l memcached sudo systemctl status -l mariadb sudo systemctl status -l php-fpm free -m df -hT exit [save and exit] chmod 777 restart.sh; chown root:ec2-user restart.sh --> test it out: ./restart.sh
Reload the services (does not interrupt transactions and only refreshes swap space if able)
cd /home/ec2-user
vi reload.sh
#!/bin/sh
free -m
sudo /usr/bin/mariadb-admin refresh >/dev/null 2>&1
sudo /usr/bin/systemctl start mariadb >/dev/null 2>&1
m=""
m=`ps -ef|grep php|grep master`
if [ "$m" != "" ] ;
then
okay=""
else
sudo /usr/bin/systemctl start php-fpm
fi
sudo /usr/bin/systemctl reload php-fpm >/dev/null 2>&1
sudo /usr/bin/systemctl start php-fpm >/dev/null 2>&1
sudo /usr/bin/systemctl restart memcached >/dev/null 2>&1
sudo /usr/sbin/nginx -s reload >/dev/null 2>&1
# in case nginx was down
sudo /usr/bin/systemctl start nginx >/dev/null 2>&1
mem=$(LC_ALL=C free | awk '/Mem:/ {print $4}')
swap=$(LC_ALL=C free | awk '/Swap:/ {print $3}')
echo RAM memory: $mem
echo Disk swap : $swap
if [ $mem -lt $swap ] ;
then
echo "ERROR: not enough RAM to write swap back, so nothing done" >&2
exit 1
else
swapoff -a &&
sleep 1
swapon -a
fi
free -m
exit
[save and exit]
chmod 777 reload.sh; chown root:ec2-user reload.sh
--> test it:
./reload.sh
Use the “free -m” command to review the results.
WordPress WP API, WP Crontab, Image Compression, Defender, W3TC
REVIEW THIS AFTER NGINX and WORDPRESS IS INSTALLED AND WORKING FOR YOUR DOMAIN
If intending to use the WordPress api (wp commands):
cd /home/ec2-user curl -L -o wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp wp --info ls /usr/local/bin/wp
If you add this line to wp-config.php in WordPress:
define( ‘DISABLE_WP_CRON’, true );
then you can run cron jobs from crontab with entries like this: (note, this may not work with some themes and plugins)
-> Use your own domains and paths. # WordPress cron via WP-CLI (staggered) */15 * * * * flock -n /tmp/wpcron_site1.lock /usr/bin/php /usr/local/bin/wp cron event run --due-now --path=/var/www/html > /dev/null 2>&1 */15 * * * * sleep 120 && flock -n /tmp/wpcron_site2.lock /usr/bin/php /usr/local/bin/wp cron event run --due-now --path=/var/www/MYNEXTDOMAIN.COM > /dev/null 2>&1 --> You can manually test, e.g. /usr/bin/php /usr/local/bin/wp cron event run --due-now --path=/var/www/html
You can check these commands manually to see if any issues after your WordPress installation is reasonably complete.
You may not necessarily need an image optimiser and file size plugin.
You can restrict image sizes by placing this php script in the ./wp-content/mu-plugins directory (create the directory if not there). It also makes sure you can upload font files to the media library.
--> This is best to return to after nginx and WordPress is installed
cd ./wp-content/mu-plugins
vi image-sizes.php
<?php
/**
* Limit generated image sizes to standard ones.
*/
add_filter( 'intermediate_image_sizes_advanced', function( $sizes ) {
// Keep only these standard sizes
$allowed = [ 'thumbnail', 'medium', 'medium_large', 'large' ];
foreach ( $sizes as $size => $dimensions ) {
if ( ! in_array( $size, $allowed, true ) ) {
unset( $sizes[ $size ] );
}
}
return $sizes;
} );
add_filter( 'jpeg_quality', function( $arg ) {
return 100; // optional: max JPEG quality
} );
add_filter( 'wp_editor_set_quality', function( $quality, $mime_type ) {
return ( 'image/webp' === $mime_type ) ? 100 : $quality;
}, 10, 2 );
function allow_font_uploads($mimes) {
$mimes['ttf'] = 'font/ttf';
$mimes['otf'] = 'font/otf';
$mimes['woff'] = 'font/woff';
$mimes['woff2'] = 'font/woff2';
$mimes['eot'] = 'font/eot';
$mimes['svg'] = 'font/svg';
return $mimes;
}
add_filter('upload_mimes', 'allow_font_uploads');
[save and exit]
chmod 664 *
chown nginx:nginx *
ls -l
systemctl restart php-fpm
If using the W3TC caching plugin, do not enable gzip compression as this is a double up (nginx does it) and slows performance.
If using the Defender plugin, database entries get ridiculously long.
Use this approach, or check with AI on how to truncate leaving so many days worth of entries:
--> Example for two sites. I think you may be able to use /home/ec2-user/.my.cnf as well - not tested. Use your own AAA/BBB database name values and table names of abc_, def_ - see your tables or what prefix in wp-config.php <-- cd ~ vi .my.cnf [client] user=root password=xxxxxxxxxxxx [client_site1] database=AAA [client_site2] database=BBB [save and exit] chmod 600 ~/.my.cnf --> in crontab: # Clean Defender Plugin ssa_defender_antibot entries 5am every 7 days 0 5 */7 * * /usr/bin/mysql AAA -e "TRUNCATE TABLE abc_defender_antibot;" >/dev/null 2>&1 5 5 */7 * * /usr/bin/mysql BBB -e "TRUNCATE TABLE def_defender_antibot;" >/dev/null 2>&1 --> Where AAA, BBB are the database names, and abc_ def_ are the wp-config.php table prefixes. This is a way to preserve the previous 7 days in the tables: mysql -uDBUSER -pDBPASS DBNAME -e "DELETE FROM wp_defender_antibot WHERE date_time < NOW() - INTERVAL 7 DAY;" Don't need to restart anything. <--
Check all is well with dnf update, then it is a good time to reboot the server and check services are running.
CHANGE NOTES
Note that on Debian 13 we use:
apt -y install python3.13 python3.13-venv
python3 -V
Python 3.13.5
This prevents the certbot warning of phython becoming deprecated
