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

Updating PHP on an AWS Lightsail WordPress Stack

In October 2024 I rewrote this guide. The updated version contains less information, but it simplified to 7 steps with limited downtime (unless your install is huge) and limited chance of failure.

Feel free to read the rest of this article if you like a broad knowledge of things before you start.


Updated 27th September 2020 to consider restart of all services where restarting just apache isn’t enough, and also to share choice when it comes to import first vs SSL first as you may lose permalinks. Thanks to Peter and Simon in the comments.

Updated 25th January 2021 to add an initial paragraph clarifying that you can’t ‘choose’ a PHP version on Lightsail and to further clarify that you must first check which version of PHP a new AWS Lightsail instance will be built with to confirm it will resolve the message being displayed in WordPress.

Updated 11th May 2021 to confirm that snapshots cannot be used to update the PHP version of a Lightsail Bitnami instance.

Updated 22nd September 2022 to add a link to a guide to check which PHP version you’ll get if you spin up a new Lightsail WordPress instance. Also added a note about WordPress supported PHP versions.

Updated 7th September 2023 to add a mention of the option to use Bitnami’s bncert tool to create an SSL certificate


NB: If you’re hoping for a way to click ‘update php’ or ‘use X version of PHP’ in Lightsail, it doesn’t (at time of writing) exist. This guidance will walk you though creating a new Lightsail instance with a newer version of PHP installed and moving everything over to it.

The AWS Lightsail service is great. Click a few buttons and you have a powerful VPS with WordPress up and running for only a few dollars a month. The Bitnami integration allows you to choose from a whole set of Stacks to include upon setup. The LAMP option allows a competent PHP developer to run almost anything on Lightsail.

But after a while running the Lightsail VPS the setup is going to get stale, and because Bitnami bundles everything up, it’s not recommended to go poking around with only a few parts of it.

That’s the challenge I was faced with. Logging into the WordPress admin console a message is displayed which is telling me WordPress would prefer a newer version of PHP please. My version was old and not receiving security updates.

I read a fair bit about my options, but after looking at the choices I decided the best thing was to do an export and import onto a new Lightsail instance.

Continue reading

Yii upgrade service

Yii 1.1 is currently in maintenance mode following Yii 2 being released way back in May 2013.

Support and bug fixes for Yii 1 were only provided until December 31, 2016 and security fixes and PHP 7 compatibility ends December 31, 2020.

That means that right now the only reason updates to Yii 1 are being created and made available are for security issues found in the existing codebase, and from 2021 it’s mothballed completely.

If you’re maintaining a Yii 1 codebase it’s time to start thinking about how to migrate away from Yii completely, or upgrade your codebase to Yii 2.

There is an official guide aimed at supporting the upgrade process, but it is unlikely to get you from Yii 1 to Yii 2 without significant pain and head scratching.

Converting from Yii 1 to Yii 2 is completely unlike developing code for either framework, and you’d want to source a Yii upgrade professional rather than getting an inexperienced developer or team involved with a large or mission critical codebase.

Personally I’ve now upgraded four codebases from Yii 1 to Yii 2. All the upgrades have been different. All follow a similar path, and the repeatable process I’ve created, but each has its own edge cases, bespoke widgets and complexities.

The work takes many days per codebase before testing and perfection can happen, and where an existing development team are involved with ongoing development or support of the Yii 1 system, it’s best handled as a team effort. Acceptance that the process isn’t anywhere close to just flicking a switch or downloading a patch is required from day one.

At this point I’m considering two next steps.

Firstly, if your company has significant budget and is in need of support moving from Yii 1 to Yii 2, I’d be interested in hearing from you. You may want a supportive voice helping your existing developers to make the move. You may also be looking to hand over a Yii 1 codebase and get it back ready for some testing and iteration.

Secondly, I’m considering sharing some or all of the process I’ve created for manually moving a codebase over from Yii 1 to Yii 2, the gotchas, the process and how to update specific things within Yii 1, where it isn’t as simple as a like

Feel free to send me a direct message on Twitter @harrybailey or leave a comment below if you’d like to contact me.

Ping a url or script using IFTTT (with no repercussions)

Update: This actually doesn’t work as I’d hoped. Because flickr reports an error each time, eventually IFTTT disables the recipe after a few hours of calls. Back to the drawing board.

I’ve been trying to get IFTTT (if this then that) to send a call to a php script file when a trigger goes off for months. The problem wasn’t getting it to work in the first place, but for it not to leave any artifacts hanging around, or repercussions as I call them. I wanted the script to be triggered and that to be the end of it, with no files being created or errors being recorded.

An example of the problem would be the IFTTT Google Docs channel. You can upload a file to google docs from a url. You can define that url to be your php script and then tell your script to return a 404 (after it’s done its coding goodness) but when you view Google Docs a new file has still been created. If you want to use this for a large number of pings, you’re going to end up with a folder full of pointless files and a waste of your disc space. The Evernote and WordPress channels are a similar story. Even when you attempt to fail the call to your url (with a 404 or 503) they still create a note or post.

Continue reading