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.

Sector
Cross-sector
Practice
Cloud and infrastructure
Year
2026
How it works
Before
Ubuntu 20end of life
ISPConfig panel14 sites
Shared SMTP keysrisk
Migration in waves
From the simplest site to the most delicate
Dead sites removedTTL at 300 secondsrsync and dumpHosts-file testingIsolated usersOld server kept 48-72h
After
Forge serversUbuntu 24
Current PHP8.3 and 8.4
One SES user per siteIAM
In brief

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.

~45
sites to migrate after the clean-up
14
sites of a single client moved off ISPConfig
23
sites with a dedicated email sending user

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

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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

Laravel ForgeAWS EC2Ubuntu 24.04PHP 8.3PHP 8.4WP-CLIrsyncRoute 53Let's EncryptAWS SESIAM

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.

Contact

Facing something similar?

Tell us what you run today and what isn't working. We'll reply within one business day with a first technical read.

Email
hello@cloudfactory.it
Address
Viale di Trastevere 114, 00153 Rome, Italy