Короткий ответ
Порядок: новый Ubuntu 22.04/24.04 → nginx/Apache + PHP-FPM 8.2/8.3 → копия файлов в /var/www/site.example и БД → vhost с тем же server_name → TLS (временный --resolve / DNS-01 / уже существующие файлы) → функциональный тест без смены публичного DNS → снижение TTL заранее → cutover A/AAAA на 10.0.20.40 → наблюдение → старый сервер не уничтожать, пока TTL жив. Не начинайте с смены DNS «чтобы сразу смотреть».
Чеклист DNS: A на старый сервер. 404 после: document root. Mixed после https: mixed.
Симптомы и как отличить
- DNS сменили, сайт 502/404, откатывать TTL час.
- Certbot не выпускает, HTTP-01 идёт на старый IP.
- Пользователи пишут в старую БД после «переноса файлов».
rm -rfстарого корня в день X.
Возможные причины
- Нет hosts-теста.
- Другой путь public/ vs корень.
- PHP 8.3 на новом, плагин только 8.2.
- Сокет FPM не тот.
- search-replace http→https забыли.
- cron/письма всё ещё со старого.
- Заражение перенесли с собой — малварь чистить до или на копии, не разносить.
Диагностика
На новом сервере:
sudo nginx -t
systemctl is-active nginx php8.3-fpm
curl -sI --resolve site.example:443:10.0.20.40 https://site.example/ | head
sudo -u www-data wp --path=/var/www/site.example option get siteurl
df -h /var/wwwСравните checksum число файлов с источником. Прогоните login, форму, оплату на staging-копии.
Решение
0. За 48 часов
TTL A/AAAA/www → 300. Инвентарь: DNS, почта MX (не трогать), FTP, cron, SSL, объём du -sh.
1. База нового хоста
Пакеты nginx, php8.3-fpm и расширения по фактическому php -m старого. Пользователь www-data, каталог /var/www/site.example. Firewall: 80/443 с мира, SSH с jump, не ufw disable.
2. Копия данных
С старого (пример):
sudo rsync -aH --numeric-ids --delete -e ssh /var/www/site.example/ admin@10.0.20.40:/var/www/site.example/--delete опасно, если путь неверный. Сначала без --delete, сверка. БД:
sudo -u www-data wp --path=/var/www/site.example db export - | gzip > /root/site.example.sql.gzПеренос дампа scp, импорт на новом в новую БД, новые credentials в wp-config.php. Не светите пароль в bash history осознанно (wp config set).
sudo tar -C /var/www -czf /root/site.example-src-$(date +%F).tar.gz site.example3. Vhost и PHP
Как в статьях nginx/Apache/FPM: root, try_files, unix-сокет /run/php/php8.3-fpm.sock. nginx -t. Права 755/644, не 777.
4. TLS до публичного DNS
Варианты: (a) скопировать /etc/letsencrypt с правами 600 на ключи, поправить пути, reload; (b) DNS-01; (c) выпуск HTTP-01 после cutover, до него — самоподписанный только для --resolve теста (браузер ругнётся, curl -k для функционала). Предпочтительно перенести LE lineage и после cutover renew. Не certbot --force-renewal на каждом шаге. HTTP-01 с новым IP до смены A не сработает у CA.
5. URL в приложении
sudo -u www-data wp --path=/var/www/site.example search-replace 'http://site.example' 'https://site.example' --all-tables --dry-runЗатем без --dry-run, если список ок. Хосты только --resolve.
6. Проверка hosts
На рабочей станции администратора:
# Windows: C:\Windows\System32\drivers\etc\hosts
# 10.0.20.40 site.example www.site.exampleЛибо только curl --resolve. Чеклист: главная, ЧПУ, login, медиа, POST формы, cron wp cron event run. Сравните HTML title с ожидаемым.
7. Freeze и финальный delta rsync
Короткое окно: maintenance на старом, повторный rsync и дамп дельты, импорт, снять maintenance на новом.
8. DNS cutover
A/AAAA/www → 10.0.20.40 (или CDN origin). Следите dig. Старый сервер: можно nginx return 302 только если ещё принимает Host — лучше оставить как rollback. Не выключайте 48 ч.
9. После
certbot renew --dry-run, cron, очередь писем, мониторинг на новый IP. Выкиньте hosts у админов. 404/502 — по статьям раздела, не повторный слепой перенос.
Сценарий: почта, cron и очереди после cutover
Сайт на 10.0.20.40, а wp-cron и systemd timers писем остались на старом IP — заказы двоятся или теряются. Перенесите crontab www-data, очереди Redis/БД, webhook URL платёжки на новый Host. SPF: если веб-сервер шлёт почту сам, добавьте новый ip4:10.0.20.40, старый уберите после отключения.
Сертификат: после смены A сразу certbot renew --dry-run. HTTP-01 теперь попадает сюда. Если копировали /etc/letsencrypt, проверьте timer и права privkey.pem 600.
Rollback: DNS назад на старый A только если старый стенд не уничтожен и БД не писали только в новый (иначе потеря заказов). Freeze обязан быть коротким и двусторонне согласованным.
Не делайте финальный rsync с --delete в /var/www, если в команде опечатка в пути. Сначала --dry-run. Не rm -rf старого webroot в ночь cutover: TTL ещё приводит людей, плюс откат.
Проверка с LTE (не офисный DNS): главная, ЧПУ, корзина, админка. Сверьте date в футере/заказе, что запись ушла в новую БД.
Как проверить, что проблема устранена
dig +short A site.example
curl -sI https://site.example/ | head
sudo certbot renew --dry-run --cert-name site.exampleПубличный A = новый IP. Контент свежий (заказ/пост после freeze на новом). Старый access.log затихает. HTTPS валиден без -k. Сотрудники без hosts видят новое.
Rollback: вернуть A на старый IP, если новый не готов и старый не снесён.
Если не помогло
- Часть мира на старом — TTL, DNS.
- Почта сломалась — вы тронули MX/SPF.
- Сессии пользователей сбросились — соли/Redis на новом пустой, это ожидаемо.
- Плагины лицензий привязаны к IP — обновите у вендора.
Профилактика
- Письменный runbook с TTL и freeze.
- Staging = тот же major PHP.
- Бэкап обоих концов до cutover.
- Не хранить единственную копию только на старом диске.
FAQ
Можно ли CNAME на новый hostname вместо A?
Apex часто нельзя CNAME. www — да, если так принято. Проще A на IP origin или CDN.
rsync во время работы сайта
Для финала нужен freeze. Итеративные rsync до окна — да.
Docker на новом, systemd на старом
Проверьте порты и NPM, не копируйте пути сокетов слепо.
Нужен ли одинаковый путь /var/www/site.example?
Сильно упрощает. Другой путь — правьте root и абсолютные крон-пути.
Когда сносить старый сервер?
Когда access.log пуст несколько TTL, бэкап нового проверен restore-тестом, LE renew идёт сюда. Не в день cutover и не через rm -rf webroot «для места».