A WordPress fleet off an end-of-life server.
Dozens of client sites on Ubuntu 20 servers no longer getting security updates. We are moving them to new servers in waves, with one isolated user per site and current PHP.
We planned the migration of about 45 sites, left after removing those no longer in use, from Ubuntu 20 servers to new servers run with Laravel Forge. It runs in waves, from lowest to highest risk, with sites isolated per user and the old server kept on until everything is verified. Alongside, we split email sending credentials: one IAM user per site, restricted to its own domain.
Context
Over time, client sites had piled up on a few Ubuntu 20.04 servers, now past standard support: modern WordPress, dated WordPress, Laravel apps and custom PHP, some still on PHP 7.4. One client alone had 14 sites on a server with an ISPConfig panel.
The problem
Leaving everything in place meant servers without security updates and, on the oldest ones, sites sharing a system user. In parallel, an audit of the AWS account found 39 access keys out of 41 needing rotation, after SMTP credentials had been compromised.
What we did
- 01
Remove first
Inventory and deletion of sites no longer used, so no junk is migrated. DNS TTL lowered to 300 seconds a few days ahead, for near-instant switches and rollbacks.
- 02
Waves by risk
Up-to-date WordPress first, then sites to check on recent PHP, then apps with a repository, and last the oldest custom code, already tested on the old server by raising its PHP version.
- 03
The same routine for every site
Site created on the new server with its own user, files copied with rsync, database dumped, certificate issued, tested through the hosts file, DNS switched. The old server stays on for 48-72 hours.
- 04
Code checked on PHP 8.4
Review of custom themes and plugins: null passed as dependencies, fragile strict comparisons, redirects without exit, unguarded $_POST access. Fixed before the move.
- 05
Email with least privilege
One IAM user per site on Amazon SES, with a policy allowing sending only from the site's domain and only from the servers' IPs. A stolen key can't send mail for anyone else.
How it went
Every migrated site runs under its own system user on a supported PHP version, with sending credentials separated site by site. If a site is compromised, it stays confined to its user and its email key is useless for anything else.
Tools
FAQ
Can sites be migrated without downtime?
Yes. With low TTLs, hosts-file testing before the switch and the old server running in parallel, visitors don't notice and rolling back is immediate.
Why one system user per site?
Because if a site is compromised, the attacker stays confined to its folder and can't read other sites' files and databases.