From af2bd2c14f7d3d72f7ce9d685ca5b060c4724962 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Fri, 25 Sep 2026 10:33:50 +0000 Subject: [PATCH 1/2] fix(migration): reload site nginx after its containers are recreated During the image migration, the temporary update-ee- project's php container answers to the same php alias on the site network. nginx resolves that upstream only when it starts, so the recreated site nginx keeps two php IPs, and once the support containers are removed one of them is dead: requests routed to it fail with "No route to host" after ~3 s before nginx retries the live peer, intermittently, until the site nginx is reloaded. Reload the site nginx after its support containers are removed. A failed reload doesn't abort or roll back the upgrade; it logs a warning with the `ee site reload --nginx` workaround. The support nginx has the same problem while it serves during the switch (it resolved the site's old php too), so it's reloaded, best effort, once the site's own containers are gone. HTML sites have no php upstream and are skipped. --- php/EE/Migration/Containers.php | 23 +++++++++++++ php/EE/Migration/SiteContainers.php | 53 ++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 4 deletions(-) diff --git a/php/EE/Migration/Containers.php b/php/EE/Migration/Containers.php index b00b94918..35259ad18 100644 --- a/php/EE/Migration/Containers.php +++ b/php/EE/Migration/Containers.php @@ -312,6 +312,9 @@ public static function migrate_site_containers( $updated_images ) { $ee_site_object = SiteContainers::get_site_object( $site['site_type'] ); + // The support project's php shares the site's php alias, and nginx resolves it only at (re)load. + $reload_nginx = in_array( $site['site_type'], [ 'wp', 'php' ], true ); + if ( $site['site_enabled'] ) { /** @@ -332,6 +335,16 @@ public static function migrate_site_containers( $updated_images ) { [ $site ], [ $site, $ee_site_object ] ); + + if ( $reload_nginx ) { + self::$rsp->add_step( + sprintf( 'reload-support-nginx-%s', $site['site_url'] ), + 'EE\Migration\SiteContainers::reload_support_nginx', + null, + [ $site['site_url'], $site['site_fs_path'] ], + null + ); + } } self::$rsp->add_step( @@ -369,6 +382,16 @@ public static function migrate_site_containers( $updated_images ) { [ $site['site_url'], $site['site_fs_path'] ], [ $site['site_url'], $site['site_fs_path'] ] ); + + if ( $reload_nginx ) { + self::$rsp->add_step( + sprintf( 'reload-site-nginx-%s', $site['site_url'] ), + 'EE\Migration\SiteContainers::reload_site_nginx', + null, + [ $site['site_url'], $site['site_fs_path'] ], + null + ); + } } } } diff --git a/php/EE/Migration/SiteContainers.php b/php/EE/Migration/SiteContainers.php index f2b7e0e31..032451374 100644 --- a/php/EE/Migration/SiteContainers.php +++ b/php/EE/Migration/SiteContainers.php @@ -223,6 +223,42 @@ public static function reload_nginx( $site_fs_path ) { } } + /** + * Reload site's nginx after its support containers are removed, so it drops the support php's IP. + * A failure only warns: the upgrade is done, nginx just keeps retrying the dead peer. + * + * @param string $site_url Site URL. + * @param string $site_fs_path Directory containing site's docker-compose.yml. + */ + public static function reload_site_nginx( $site_url, $site_fs_path ) { + EE::debug( sprintf( 'Start reloading nginx of %s', $site_url ) ); + + try { + self::reload_nginx( $site_fs_path ); + } catch ( \Exception $e ) { + EE::warning( sprintf( 'Could not reload nginx of %1$s after upgrading its containers. Some requests may take ~3 s until you run `ee site reload %1$s --nginx`.', $site_url ) ); + + return; + } + + EE::debug( sprintf( 'Complete reloading nginx of %s', $site_url ) ); + } + + /** + * Reload the support nginx after the site's containers are removed, so it drops the old php's IP. + * Best effort: the support nginx only serves until the site's containers are back. + * + * @param string $site_url Site URL. + * @param string $site_fs_path Directory containing site's docker-compose.yml. + */ + public static function reload_support_nginx( $site_url, $site_fs_path ) { + $command = sprintf( "docker-compose --project-name=%s exec nginx sh -c 'nginx -t && nginx -s reload'", self::get_support_project_name( $site_url ) ); + + if ( ! chdir( $site_fs_path ) || ! EE::exec( $command ) ) { + EE::debug( sprintf( 'Could not reload support nginx of %s', $site_url ) ); + } + } + /** * Function to reload site's php. * @@ -262,8 +298,7 @@ public static function docker_compose_pull( $site_fs_path ) { public static function enable_support_containers( $site_url, $site_fs_path ) { EE::debug( sprintf( 'Start enabling containers for %s', $site_url ) ); - $site_name = str_replace( '.', '', $site_url ); - $project_name = sprintf( 'update-ee-%s', $site_name ); + $project_name = self::get_support_project_name( $site_url ); if ( ! chdir( $site_fs_path ) ) { throw new \Exception( sprintf( '%s does not exist.', $site_fs_path ) ); @@ -288,8 +323,7 @@ public static function enable_support_containers( $site_url, $site_fs_path ) { public static function disable_support_containers( $site_url, $site_fs_path ) { EE::debug( sprintf( 'Start disabling support containers for %s', $site_url ) ); - $site_name = str_replace( '.', '', $site_url ); - $project_name = sprintf( 'update-ee-%s', $site_name ); + $project_name = self::get_support_project_name( $site_url ); if ( ! chdir( $site_fs_path ) ) { throw new \Exception( sprintf( '%s does not exist.', $site_fs_path ) ); @@ -302,4 +336,15 @@ public static function disable_support_containers( $site_url, $site_fs_path ) { EE::debug( sprintf( 'Complete disabling support containers for %s', $site_url ) ); } + + /** + * Compose project name of a site's support containers. + * + * @param string $site_url Site URL. + * + * @return string + */ + private static function get_support_project_name( $site_url ) { + return sprintf( 'update-ee-%s', str_replace( '.', '', $site_url ) ); + } } From 0aa9c185fb9fe9527ad4655ad62501d72a4dbc64 Mon Sep 17 00:00:00 2001 From: Riddhesh Sanghvi Date: Fri, 25 Sep 2026 16:19:40 +0000 Subject: [PATCH 2/2] fix(migration): reload site nginx after a rolled-back site migration When a later step of the image migration fails, the rollback recreates each WP/PHP site's old containers while the support project's php still answers to the site's php alias, then removes the support project. The site nginx kept the dead support php as a peer, so some requests took ~3 s until `ee site reload --nginx`. Add a first step per enabled WP/PHP site with a no-op up and a site nginx reload as its down. Undo runs in reverse, so the reload runs last for that site, after the support project is gone. A failed reload only warns. --- php/EE/Migration/Containers.php | 11 +++++++++++ php/EE/Migration/SiteContainers.php | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/php/EE/Migration/Containers.php b/php/EE/Migration/Containers.php index 35259ad18..8c078e60d 100644 --- a/php/EE/Migration/Containers.php +++ b/php/EE/Migration/Containers.php @@ -317,6 +317,17 @@ public static function migrate_site_containers( $updated_images ) { if ( $site['site_enabled'] ) { + if ( $reload_nginx ) { + // Undone last for this site: after the old containers are back and the support project is gone. + self::$rsp->add_step( + sprintf( 'reload-site-nginx-on-rollback-%s', $site['site_url'] ), + function () {}, + 'EE\Migration\SiteContainers::reload_site_nginx', + null, + [ $site['site_url'], $site['site_fs_path'] ] + ); + } + /** * Enable support containers. */ diff --git a/php/EE/Migration/SiteContainers.php b/php/EE/Migration/SiteContainers.php index 032451374..e3a9d3bea 100644 --- a/php/EE/Migration/SiteContainers.php +++ b/php/EE/Migration/SiteContainers.php @@ -225,7 +225,7 @@ public static function reload_nginx( $site_fs_path ) { /** * Reload site's nginx after its support containers are removed, so it drops the support php's IP. - * A failure only warns: the upgrade is done, nginx just keeps retrying the dead peer. + * A failure only warns: the site works, nginx just keeps retrying the dead peer. * * @param string $site_url Site URL. * @param string $site_fs_path Directory containing site's docker-compose.yml. @@ -236,7 +236,7 @@ public static function reload_site_nginx( $site_url, $site_fs_path ) { try { self::reload_nginx( $site_fs_path ); } catch ( \Exception $e ) { - EE::warning( sprintf( 'Could not reload nginx of %1$s after upgrading its containers. Some requests may take ~3 s until you run `ee site reload %1$s --nginx`.', $site_url ) ); + EE::warning( sprintf( 'Could not reload nginx of %1$s after recreating its containers. Some requests may take ~3 s until you run `ee site reload %1$s --nginx`.', $site_url ) ); return; }