Updating From ExpressionEngine 2 to ExpressionEngine 6

The process went more smoothly than I’d anticipated, but it was not without some hassles.
ExpressionEngine Logo

I recently experienced a web developer’s worst nightmare — and no, I’m not referring to ensuring IE9 compatibility or making the logo bigger.

Instead, I had to update a website’s really, really old CMS.

The CMS in this case was ExpressionEngine, which I’ve used for numerous personal and professional projects (including, for a time, Opus). For various reasons, however, this particular site had been left to languish for years; it was still running on a version of ExpressionEngine 2 from 2017 (which is equivalent to four decades ago in internet years).

The site still ran just fine — so long as the server was running PHP 5.6. But the site’s hosting provider had recently announced an impending update to PHP 7. While such an update made total sense — PHP 7 comes with significant security and performance improvements over PHP 5.6 — it did mean that the site was at a crossroads, since ExpressionEngine 2’s compatibility with PHP 7 is basically nonexistent.

(In fact, the hosting provider had previously updated the site’s server to PHP 7 without our knowledge — which killed the site. Nothing worked until the server was switched back to PHP 5.6, but the site was clearly running on borrowed time.)

I considered rebuilding the site on a different CMS like Craft, but quickly decided against that given its size and amount of content, all of the custom coding and functionality that it contained, and of course, the client’s budget and timeline. Updating to ExpressionEngine 6 felt like the only proper course, but even that was fraught.

Updating any CMS over several major versions is bound to break things due to deprecated code, outdated functions, and the like. It gets even more complicated when you factor in third-party add-ons; there was no guarantee that the site’s add-ons had ExpressionEngine 6-compatible versions. But as it turned out, updating this particular site to ExpressionEngine 6 went far more smoothly than I’d anticipated — though it required a lot of preparation before I performed the actual update itself.

The following is a breakdown of my process for any other hardy souls who might need to perform a similar update.


First things first: I did a complete backup of the site’s files and database. This would be the first of many such backups. Basically, if you have any doubts before doing anything, back up your site’s files and database. Heck, back up its files and database even if you don’t have any doubts. It’s always better to be safe than sorry. (Just make sure you keep your backups organized so you know which one’s the right one to use should you have to restore the site.)

Given the site’s age, it had built up a lot of cruft, so I deleted any old/unused content, assets, and custom fields. This included images and other uploads — of which there were a lot. The more I deleted now, though, the less I’d have to migrate later. (Note: This is a good thing to do even when not updating a site, but rather, as part of regular maintenance.)

I deleted any unused templates and reviewed the remaining templates to identify any potential issues with their code. While not a huge concern — ExpressionEngine 2’s templating language is pretty similar to ExpressionEngine 6’s — better safe than sorry. (Detecting a theme, yet?)

I audited the site’s third-party add-ons. I wanted to determine which add-ons were no longer being used and could be removed, which add-ons would have to be removed because they were no longer supported or compatible with ExpressionEngine 6, and which add-ons were ExpressionEngine 6-compatible. And if an add-on was ExpressionEngine 6-compatible, I made sure to note any major changes to the add-on (e.g., updated UI, new and/or deprecated template tags).

I set up a “dev” subdomain where I could work on the update without affecting the live site. Honestly, this was probably the most frustrating part of the entire update process. It took chats with multiple hosting support techs over the span of 4 – 5 days to finally get the subdomain up and running.

Once the dev subdomain was running, I uploaded a copy of the live site’s files there. I also cloned the live site’s database and configured the dev site to use that instead. With that, my dev site was finally finished and ready to be worked on.

From this point on, all of my work was done on the dev site while I left the live site happily coasting along in a state of blissful, PHP 5-powered ignorance.

Using the information from the aforementioned add-ons audit, I deleted about half of the site’s third-party add-ons because they were no longer necessary or compatible with ExpressionEngine 6. The “Playa & Matrix Importer” add-on proved really useful here, converting all of the site’s Playa and Matrix fields to ExpressionEngine’s own native Grid and Relationships fields, thus removing the need for those particular add-ons. Kudos to Kevin Cupp and Derek Jones; they did a fantastic job with this tool, which worked liked an absolute charm.

Once I figured out which add-ons were left, I bought new licenses or renewed expired ones for any commercial add-ons.

I downloaded a copy of ExpressionEngine 6 and uploaded the files to the server. Versions before ExpressionEngine 6 required a multi-step process when updating from v2, but with v6, the update path from v2 is very straightforward. Note: I did not upload any add-on files at this point. I only uploaded ExpressionEngine 6-specific files.

I crossed my fingers, knocked on wood, said a quick prayer, and began the update process. It’s always a bit nerve-wracking to watch the update utility tick through each version, and given that I was updating from ExpressionEngine 2, it had to tick through a lot of versions. I kept bracing myself for some fatal error or conflict, but though it took awhile, everything went smoothly. After a couple minutes, the site had been successfully updated to ExpressionEngine 6.

Or had it? I pulled up the “dev” site’s homepage… and got a “500” server error. Similarly, I was unable to access the site’s admin area. A quick test revealed that the subdomain was still using PHP 5.6, but I got server errors even after updating it to PHP 7. My troubleshooting finally revealed that the update utility had missed one critical step: it hadn’t updated the site’s existing “config.php” and “database.php” files to the format needed for ExpressionEngine 6.

The v2 “database.php” file looked something like this:

$active_group = 'expressionengine';
$active_record = TRUE;
$db['expressionengine']['hostname'] = 'hostname';
$db['expressionengine']['username'] = 'username';
$db['expressionengine']['password'] = 'password';
$db['expressionengine']['database'] = 'database';

However, those settings needed to be moved into the “config.php” file and reformatted like so:

$config['database'] = array (
	'expressionengine' => 
	array (
		'hostname' => 'hostname',
		'username' => 'username',
		'password' => 'password',
		'database' => 'database',
	),
);

Once I manually fixed that, the site loaded and I could log in to the admin area. I then began verifying that the site’s content was still displaying in the admin area, nothing had been mysteriously deleted, etc.

I uploaded all of the new add-on files and began updating them via the admin area’s “Add-ons” section. Again, to my relief, this went more smoothly than I expected. I was particularly concerned with two add-ons: Bloqs and Freeform. The Bloqs add-on — originally called Blocks when I built the site — updated without any problem, with all of the content blocks converted and nothing lost. Freeform, however, was a bit more involved.

Because of the long gap between updates, Freeform’s built-in migration utility didn’t work. Instead, I had to manually rebuild all of the site’s forms, fields, and notifications. This wasn’t hard, but it was very time-consuming because the site had several forms and lots of fields. As for the form templates, that was basically a matter of replacing the old Freeform template tags with the new ones. Again, more time-consuming than difficult.

On a related note, this meant that I lost all of the form submissions that had accumulated over the years. I had a feeling this might happen, though, and had exported the form submissions before making any updates. Which just goes to show, when in doubt, make a back up.

I encountered a couple of other miscellaneous issues, post-update:

  • Some of the publish layouts that I’d created for the site’s channels got messed up. I tried fixing them piecemeal, but eventually just deleted and rebuilt them all. I suspect this was due in part to having deleted a bunch of custom fields earlier.
  • I encountered some minor glitches with Bloqs and Structure (another third-party add-on used by the site). These were independent of the update process, though, and were quickly resolved with help from their respective developers.

Also, while this wasn’t necessary for the update, I spent some time updating the site’s templates to take advantage of ExpressionEngine 6’s templating capabilities (e.g., template partials). This allowed me to further reduce the site’s reliance on third-party add-ons and make the templates easier to manage.


Overall, the process of updating from ExpressionEngine 2 to ExpressionEngine 6 went much better than I’d feared and anticipated. I’m a “worst case scenario” kind of guy, so I envisioned spending days wading through deprecated code, config files, database settings, and support forums.

In all, the actual work of updating the site and resolving the various post-update issues took about 8 – 10 hours total, with much of that time spent fixing the Freeform-related issues. That doesn’t, however, include all of the work I did beforehand (e.g., backing up the site, creating the subdomain, auditing the add-ons). But that initial work undoubtedly helped to make the actual update process as smooth as it was.

I would be greatly remiss if I didn’t give some props to the folks in the ExpressionEngine Slack. Minutes after I’d posted my query, multiple individuals chimed in and offered lots of helpful advice, especially with how to deal with Freeform and other third-party add-ons. I suspect that without their help, the update process would’ve taken much longer and been much more nerve-wracking, with my wife finding me curled up under my desk at some point.


This is actually my first ExpressionEngine 6 site. (I have several ExpressionEngine 5 sites, but for various reasons, I have yet to update them.) Overall, I’ve been really impressed with the work that the ExpressionEngine team has done with this latest version.

I like the new admin UI (though the sidebar occasionally gives me some trouble on larger Bloqs-powered pages) and it definitely feels like a more modern CMS. But at the same time, it still feels like ExpressionEngine, even with all of the reorganizing and design updates that are obviously intended to modernize it. Which made it easy to present the new, ExpressionEngine 6-powered site to the client, who was able to dive right in and start working with little-to-no hand-holding on my part.

I’m not as active in the ExpressionEngine community as I once was, but after my experience so far, I’d say that ExpressionEngine 6 represents the start of a very bright future for the platform. In the past, ExpressionEngine has sometimes felt direction-less and lagging behind other CMS platforms like Craft and Statamic. But with ExpressionEngine 6, it’s pretty clear that Packet Tide is committed to the CMS and moving things in the right direction.

Enjoy reading Opus? Want to support my writing? Become a subscriber for just $5/month or $50/year.
Subscribe Today
Return to the Opus homepage