Updating PHP on an AWS Lightsail WordPress Stack – Version 2

I’ve decided to rewrite the previous version of this article. It’s been cloned and copied loads of times, and to be honest it got bloated. I also prefer this new way.

This guide aims to only take you through the steps I use now when a PHP version update is needed for my AWS Lightsail Bitnami WordPress install.

No fluff. Limited details. Just 7 steps and 3 optional ones.

You follow this guide at your own risk. Back up your site first. The older version of this article is still valid if there isn’t enough information for your needs here.

The main change here is we’re going to use a very simple maintenance plugin which shows a message in the short time required between moving to the new install and importing the old blog backup. This does away with the potential complexity of the Lightsail IP address being saved to the database as part of the migration.

Ready?

Read more: Updating PHP on an AWS Lightsail WordPress Stack – Version 2

This guide assumes you have a static IP on your current Lightsail instance. If you don’t, this guide isn’t for you.

1. Create a new AWS Lightsail instance

Match the instance type of the current setup unless you know otherwise. Choose a name you’ll remember for the following steps. For this guide, we’ll use New Instance, and Old Instance for our instance names.

2. Tweak the New Install’s PHP settings

Choose to ‘connect using SSH’ to New Instance.

Run cat bitnami_application_password and take a note of the password for later

Run sudo nano /opt/bitnami/php/etc/php.ini

Use ctrl-w to trigger a search, or look manually for the partial max_size and change the value from 80MB to whatever you need. It’s temporary, go wild.

Use ctrl-w to trigger a search, or look manually for the partial max_file and change the value from 80MB to whatever you need. It’s temporary, go wild.

Run sudo /opt/bitnami/ctlscript.sh restart to restart Apache and apply the new upload limit settings

Keep this window open for use in step 6.

3. Get migrate and maintenance ready

Open the New Instance’s wp-admin. If you don’t know how via the IP address, there’s a button at the top of the page in LightSail admin which says ‘Access WordPress Admin’. Log in using the username user and the password that you made a note of in the step 2.

Go to plugins in the navigation

Click enable next to WP migrate (it’s installed by default). If you don’t see it, click ‘Add New’ – search for ‘Migrate’ and install and enable ‘All-in-One WP Migration`

Click the ‘Add New’ button and search for ‘Maintenance’ – Install and enable ‘WP maintenance’

Click ‘WP maintenance’ in the left nav. Change the body text to ‘We’ll be back in a few minutes’, because the default message isn’t ideal. Save.

Enable maintenance mode, and save again. New install now in maintenance mode, although you can still see it because you’re logged in as admin.

4. Export the live site

I do this at step 4 to limit the time between export and import, but if you’re not as bothered, you could save overall time by starting this step first, and doing other steps while the export is taking place.

Open the live site’s wp-admin. Go to plugins. Enable ‘All-in-One WP Migration’, or if required, click ‘Add New’ as above, install and enable it.

Click ‘All-in-One WP Migration’ in the left nav. Click ‘Export to’. Select ‘File’.

When the process has finished, click the download button to do the actual file downloading to your machine.

From step 5 you’ll want to move fast if limiting downtime (length of maintenance message display) is important to you. It took me about 5 minutes from this point for a 350MB WordPress site.

5. Move the live domain to the new site

In Lightsail admin navigation click Networking, then click the name of the static IP associated with the currently live site.

On the manage screen for IP, click to detach it from Old Instance. Then immediately reattach it to the New Instance.

6. Add an SSL Certificate to New Instance

Still got the New Instance SSH window open? Switch to it, or open it again.

Run sudo /opt/bitnami/bncert-tool

Submit your domain name without www. Choose your preferred answers to the questions that follow. Stick in an email address. Agree to the right things with Y / n. Complete the process.

Reload, or open your live domain name in a browser. Keep refreshing, and pretty quickly it should go from not loading, to loading again.

7. Import to New Instance

Open up your live domain wp-admin. Log in using your settings above. So user and password you made a note of.

Open All-in-One WP Migration and choose import. Choose File. Choose the file from your computer.

Wait. It should succeed. Only file size might stop it working, but fingers crossed. It will probably ask if you’re sure.

There you go. The site, file and database are moved to New Instance.

8. Put the settings back

Go back up to step 2 above and reverse the changes to php.ini on New Instance – or don’t. It’s up to you.

Check Setting => General. If your domain name shows as http:// and the field is readonly, you need to edit wp-config.php so in the New Instance SSH window…

Run nano /opt/bitnami/wordpress/wp-config.php find WP_HOME and WP_SITEURL and update to https://

9. Remove Old Instance

You can now ‘Stop’ or even remove old instance. I usually Stop mine, but keep it a few days just in case.

If you messed anything up above, the only change required to go back is step 5 in reverse. Point the static IP back to Old Instance. Insure it’s not Stopped first.

Elastic Beanstalk crons on multi-instance environments

Wow. What a lot of pain.

So let me first confirm what won’t work. Whether Apache 1, Apache 2, leader_only or EB_IS_COMMAND_LEADER testing, you cannot consistenty get exactly one (not zero, or 2+) instances to run a cron job over a long period of time.

The issue is the the leader information isn’t safe. It’s only available during deployments, when you usually first set up the cron job. But when an instance replacement happens—for whatever reason—there is no related deployment, the test to check if the new instance is the leader fails, and you end up with no crons.

So for anything you do at the moment of deployment—database migrations, logging, whatever— that you only want to run once, even on multi-instance environments in an Elastic Beanstalk Application, you can use and trust the leader tests available to you.

But for anything you want to run regularly, on a schedule across weeks and months, you cannot trust those options.

After a lot of research, and trial and error there are two options available to you.

Worker environments

The ‘official’ way is probably via a worker environment. Essentially an instance set up specifically to schedule and run short or long lives processes.

You can learn more about them via the links below, but for the rest of this article we’ll be talking about the alternative option of ‘runtime leader testing’.

https://medium.com/@joelennon/running-cron-jobs-on-amazon-web-services-aws-elastic-beanstalk-a41d91d1c571

Runtime leader testing

This option means running the cron on every instance of an environment, but the initial section of the code being run checks the if the instance id that it’s running on is the first to be returned in the list of all instances. That is, only the first instance id returned by the aws elastic beanstalk api will continue to run the task.

Below the examples are in PHP, but it’s a valid method for other languages too.

We’re assuming you’ve already set up a cron and know how to alter it to make it run on all instances of your environment.

The steps in this guide:

  • Create an IAM user just for this process and give it limited read permissions
  • Add the credentials to EB configuration
  • Write code to check the instance id from the api against the current instance id

Adding the user

  • Open IAM in AWS console
  • Click Users
  • Click Add users
  • Type your User name – we used “[applicationCode]-eb-read-user”
  • Click the ‘Access Key’ access type
  • Click Next: Permissions
  • Click the ‘Attach existing policies directly’ tab
  • Type ‘AWSElasticBeanstalkReadOnly’ and select it
    • NB: This permission actually gives more access than is required and you could trim it further
  • Click Next: Tags
  • Click Next: Review
  • Click Create user
  • Take a copy of the KEY and SECRET

Storing the key and secret in the environment

  • Open your environment
  • Click configuration
  • Click ‘edit’ on the ‘Software’ section
  • Add two new items, one for the key (examples use AWS_KEY) and one for the secret (examples use AWS_SECRET)

The code

So this is php, but the basic idea is:

  • Install the aws library – here we use composer
  • Add some code to call the library
  • Compare the first returned instance id against the id of the instance running the code

Install the aws library

$composer require aws/aws-sdk-php

Add the code to do the check

$client = ElasticBeanstalkClient::factory([
    'credentials' => [
        'key' => $_ENV['AWS_KEY'],
        'secret' => $_ENV['AWS_SECRET'],
    ],
    'region'  => '[[your region]]',
    'version' => 'latest'
]);

Replace [[your_region]] above with your own

$result = $client->describeEnvironmentResources([
    'EnvironmentName' => '[[your_environment_name]]'
]);

Then load in the current instance’s id

$currentId = file_get_contents("http://instance-data/latest/meta-data/instance-id");

Then compare the API’s first result to the current one. If the same, so something, otherwise don’t.

if ($currentId == $result['EnvironmentResources']['Instances'][0]['Id'])
{
    // do something. Only one instance will get here
}
else
{
    // don't do anything. All other instances will be here
}

Other useful resources:

https://rotaready.com/blog/scheduled-tasks-elastic-beanstalk-cron

Check AWS Lightsail Bitnami WordPress PHP version

Before you spend time upgrading to a new lightsail instance in the hope of your PHP version meeting WordPress minimum requirements, it’s useful to know if the version available is high enough.

You can check which PHP version you’re going to end up with using the following steps:

  • Open your lightsail home page
  • Click the Create Instance button
  • Check the ‘Linux/Unix’ option is selected
  • Look for the ‘WordPress’ option and make a note of the number displayed underneath (Something like a.b.c-d)

Ok, next we’ll check the Bitnami WordPress changelog

  • Open the changelog file
  • Search for a matching number from your note above
  • Check for a bullet which begins Updated php to
  • If no match, keep reading down the file until you find the first match for Updated php to
  • That’s the current PHP version you’ll get if you spin up a new lightsail wordpress install

If it’s the same or higher than the message being shown inside WordPress you can go ahead. If it’s not then you need to set a reminder to check back regularly on the lightsail creation page to see when the version updates.

AWS don’t use the very latest version available from Bitnami.

The final thing to consider… strangely is if WordPress supports the PHP version you’ll be upgrading to. WordPress maintain a list of which versions of PHP they support (properly and in beta).


Work in a software agency where you struggle to delivery consistently to your unique clients and unique projects? I specialise in that challenge!

Follow me on X (formerly Twitter), Threads for insights, on Medium for thought pieces, or read about my agency focused workshops (UK only) for more information.

WHM: List Largest Emails In An Account With Subject

I recently needed to find the largest emails in various email accounts and share the size and subject. Not if it was a single email account which I had access too there are likely much simpler options using webmail or an email client to get the same outcome.

It looks like this hasn’t been written about before so I pieced together various tutorials to end up with the final solution.

This guide assumes you know what you’re doing on the command line prompt and that you have the permissions to view the files and folders below. It also assumes you have the permissions to go poking around in people’s email accounts, so be sure to ask for that before you start outputing private email subjects from their accounts

The first step is to check you have the following results for a list of files in the chosen CPanel account’s mail folder:

$ls -a /home/[account]/mail

The above should show you a folder for each domain, but hopefully also a symlink for each account. In my case:

.email@example_com

The above actually points to example.com/email but we’ll be using it in our code.

This code also expects emails to be stored in files ending with ,Sab or ,RSab or ,S or ,RS so check that too.

So here is the full example which I’ll then break down below. You’ll need to update it to suit your needs:

find /home/[account]/mail/.email\@example_com/ -type f ( -iname "*,Sab" -or -iname "*,RSab" -or -iname "*,S" -or -iname "*,RS" ) -size +1M -exec grep "Subject: " {} \; -printf '%s B - ' | sort -nr | head -10

First we use the find command to list files which match our email file name (end with 4 options) and have a file size of greater than 1M:

find /home/[account]/mail/.email\@example_com/ -type f ( -iname "*,Sab" -or -iname "*,RSab" -or -iname "*,S" -or -iname "*,RS" ) -size +1M

We then trigger an exec on that which will eventually return their subject as part of the output, and also print their file size in bytes (%s) followed by a B and a hyphen with -printf

-exec grep "Subject: " {} \; -printf '%s B - '

We then sort the list:

sort -nr

and finally only return the first 10:

head -10