Переезд с Латвии на Hetzner: аудит перед отключением
Миграция сервера пугает не самим переездом, а одной кнопкой — той, которой ты выключаешь старый сервер. Пока обе машины живы, всегда есть запасной аэродром: что-то отвалилось — откатился, и никто не заметил. Но в какой-то момент надо решиться нажать на эту кнопку и остаться только с новым домом. У меня был старый VPS в Латвии, на который за пару лет наросло два десятка production-сервисов, и весь этот зоопарк я перевёз на выделенный сервер Hetzner в Хельсинки. Дальше встал главный вопрос: а точно ли всё переехало? Можно ли гасить Латвию без риска, что в три часа ночи отвалится бот, о котором я успел забыть?
Эта статья — про то, как я организовал переезд, как прошёл через формальный аудит перед отключением (в роли независимого проверяющего, который никому не верит на слово), и как потом отдельно проверил, что новый сервер реально доступен из России со всех крупных провайдеров. Без этого последнего шага вся миграция могла оказаться бессмысленной.
Контекст: что за зоопарк переезжал
Старый сервер в Латвии был классическим примером того, как инфраструктура растёт органически и без плана. Начиналось всё с пары Telegram-ботов, а к моменту переезда там крутилось:
- 19 системных systemd-сервисов — боты, рендереры, API-прослойки, headless-браузеры, туннели;
- 2 пользовательских (user-level) сервиса — gateway и постинг-бот;
- 24 Docker-контейнера — всё, что удобнее держать в изоляции;
- 11 публичных доменов с HTTPS;
- мониторинг (Gatus), WireGuard-туннели, RAID-массивы.
Проблема такого хозяйства в том, что его невозможно держать целиком в голове. Когда сервисов три — ты помнишь каждый. Когда их двадцать с лишним, и половина была настроена год назад, ты физически не помнишь, какой бот на каком порту висит и что сломается, если выключить старую машину. Поэтому переезд нельзя было делать на ощупь — нужен был воспроизводимый чеклист и честная проверка по нему.
Проблема: как доказать, что переезд завершён
Главная ловушка любой миграции — иллюзия завершённости. Новый сервер поднят, домены вроде отвечают, ты ставишь галочку «готово» и идёшь спать. А через неделю выясняется, что один из cron-сервисов всё это время дёргал endpoint на старой машине, или что DNS какого-то поддомена так и остался смотреть в Латвию.
Я сформулировал для себя жёсткое правило: перед отключением старого сервера пройти полный аудит так, будто я — внешний аудитор, которому платят за то, чтобы найти проблему. Не «вроде работает», а пункт за пунктом с доказательствами. Список проверок я свёл в отдельный документ-чеклист (условный AUDIT_BEFORE_DECOMMISSION), и прогонял его не один раз, а дважды: до отключения Латвии и сразу после.
Решение: аудит в роли независимого проверяющего
Ключевая идея — не доверять собственным воспоминаниям о том, что «всё перенесено». Каждый пункт чеклиста должен возвращать машинно-проверяемый результат, а не ощущение. Вот что я проверял.
1. DNS — куда реально смотрят домены
Первым делом — резолв всех 11 доменов. Тут важна не теория («я же поменял записи»), а фактический ответ DNS прямо сейчас. Большинство доменов уже указывали на новый сервер, и был один намеренный нюанс: фронт-домен all.* сознательно оставался на промежуточном узле — так и было задумано по архитектуре, и аудит это зафиксировал как ожидаемое, а не как ошибку. Это важный момент: хороший чеклист отличает «отклонение» от «спроектированного исключения».
2. HTTPS — все домены живы
Дальше — реальные HTTP-запросы ко всем доменам. Критерий простой: нет 5xx, нет таймаутов, нет SSL-ошибок. Коды 200/307/401/404 — это нормально, потому что часть доменов это API-only эндпоинты, которые на корне честно отдают 401 (нужна авторизация) или 404. Тут легко обмануться: наивный мониторинг считает 401 «падением», хотя для API-домена это абсолютно здоровый ответ. Аудит должен понимать семантику каждого сервиса, а не слепо ждать 200.
3. Сервисы — все ли поднялись
Проверка статуса всех systemd-юнитов и Docker-контейнеров:
# Системные сервисы
systemctl list-units --type=service --state=running
# Docker
docker ps --format '{{.Names}}: {{.Status}}'Итог: 19/19 системных active, 2/2 пользовательских active, 24/24 контейнера running. И вот тут аудит принёс реальную пользу — нашёлся дубль сервиса kp-renderer: один и тот же рендерер был описан и как системный юнит, и как пользовательский. Они не конфликтовали в лоб (слушали разные порты), но это была мина замедленного действия: рано или поздно кто-то перезапустил бы не тот. Удалил пользовательский дубль, оставил системную версию, обновил документацию архитектуры. Без построчного аудита этот дубль жил бы ещё долго.
4. Мониторинг — расширил покрытие
Заодно я обнаружил, что Gatus (мой self-hosted health-check) не покрывал часть инфраструктурных эндпоинтов. Добавил группу monitoring с четырьмя проверками (API мониторинга, дашборд метрик, self-check самого Gatus, логи). После рестарта все 12 эндпоинтов проходят, первый же тик — success=true на всех. Telegram-алерты подхватились автоматически. Логика простая: если уж проверяешь инфраструктуру вручную перед переездом, закрепи эти проверки в постоянном мониторинге, чтобы они работали и завтра.
5. Telegram-боты — нет ли конфликта long-poll
Отдельная боль при переезде ботов — гонка за long-polling. Если старый и новый инстансы бота одновременно опрашивают Telegram API, оба получают ошибку 409 Conflict, и бот начинает мигать. Я проверил логи на наличие 409 за 30 минут: 0 конфликтов. Это прямое доказательство, что нигде не остался забытый запущенный дубль бота на старой машине.
Финальная проверка — после нажатия кнопки
Когда я наконец выключил Латвию, я прогнал тот же чеклист повторно. Первым пунктом — ping старого IP:
PING old-server: 100% packet loss
100% потерь — сервер мёртв, это именно то, что нужно. И при этом всё остальное зелёное: 11 доменов отвечают, 19 системных и 2 пользовательских сервиса active, 24/24 Docker running, Gatus 12/12, ноль 409-конфликтов у ботов, RAID-массивы в состоянии [UU], диск занят на 7%, WireGuard поднят. Старый дом мёртв, новый работает на полную — ровно тот результат, ради которого затевался формальный аудит.
Второй фронт: а доступен ли Hetzner из России?
Тут начинается самое интересное и неочевидное. Сервер может быть идеально настроен, все сервисы зелёные — но если до него не доходят пакеты из России, для русскоязычной аудитории он мёртв не меньше, чем выключенная Латвия. Hetzner находится в Хельсинки, и доступность европейского хостинга из РФ — это отдельный вопрос, который нельзя проверить из своей квартиры.
Я поднял на новом сервере отдельный тестовый домен test.pashavin.ru с реальным Next.js-приложением (не заглушкой, а полноценным лендингом со standalone-сборкой в Docker). Локально на сервере он отдавал TTFB 46 мс и FCP 200 мс — но это Hetzner проверял сам себя, что ничего не доказывает про путь из России.
Поиск инструмента за $0
Коммерческие сервисы вроде residential-прокси с фильтром по ISP стоят денег, а проверку хотелось делать регулярно и бесплатно. После разведки по форумам и Reddit нашёлся отличный open-source инструмент — Globalping от jsDelivr. Это сеть проб с CLI и REST API, и в России у неё около 100 узлов в семи городах (Москва, Питер, Новосибирск, Казань, Краснодар, Уфа, Таганрог), причём на реальных провайдерах вроде ER-Telecom, Selectel, Timeweb. Бесплатный тариф — 250 тестов в час без регистрации.
Я собрал небольшой тестовый набор на Python: один пайплайн гнал HTTP-замеры через Globalping API (широта охвата), второй пытался прогонять браузерный Playwright через бесплатные прокси (глубина метрик). Структура была честно спланирована через brainstorming → spec → план, потому что «проверить из всех провайдеров РФ» — это 200+ операторов, и без чёткого scope легко утонуть.
Результат прогона
Главный пайплайн на Globalping отработал чисто:
| Город | Успешно | Средний TTFB |
|---|---|---|
| Москва | 27/27 | 36 мс |
| Санкт-Петербург | 11/11 | 21 мс |
| Новосибирск | 5/5 | 97 мс |
| Казань | 2/2 | 55 мс |
| Краснодар | 2/2 | 48 мс |
| Уфа | 1/1 | 39 мс |
| Таганрог | 1/1 | 56 мс |
Итого 49 из 50 проб успешны, единственный таймаут — одна проба в Пензе на ER-Telecom. TTFB в Москве и Питере — 20-40 мс, это отличный результат для хостинга в Хельсинки. Реальный браузерный Playwright-тест показал FCP 200 мс, ноль console-ошибок, ноль упавших запросов. То есть Hetzner Helsinki честно отдаёт живой Next.js в Россию с хорошими цифрами.
Попутно вскрылся технический урок: бесплатные HTTP-прокси не умеют туннелировать HTTPS — все 16 «живых» прокси давали ERR_EMPTY_RESPONSE на CONNECT-запросах к https://. Так что для браузерной глубины через бесплатные прокси нужен другой подход, а основную картину доступности дал именно Globalping.
Результат и выводы
Итог переезда: 19+ системных сервисов, 24 контейнера и 11 доменов переехали с латвийского VPS на Hetzner, старый сервер отключён, аудит до и после подтвердил полноту переноса с машинными доказательствами по каждому пункту. Параллельно подтверждена доступность из России (49/50 проб, TTFB 20-40 мс в столицах) и устранён скрытый дубль сервиса. Теперь несколько обобщённых уроков, которые я забираю с собой.
Аудит — это роль, а не галочка. Самое ценное в проверке перед отключением — сознательно встать в позицию недоверчивого внешнего аудитора, который не помнит, как ты что настраивал, и которому нужны доказательства, а не уверения. Именно эта позиция нашла дубль kp-renderer и дыры в покрытии мониторинга. Когда проверяешь свою же работу «по-доброму», ты подсознательно проскакиваешь именно те места, где наошибался. Формализуйте критерии так, чтобы каждый пункт возвращал факт: статус сервиса, код ответа, число конфликтов — а не ваше ощущение, что «вроде ок».
Отличайте отклонение от спроектированного исключения. Хороший чеклист не паникует от 401 на API-домене и от того, что фронт-домен сознательно смотрит на другой узел. Слепой мониторинг, который ждёт 200 от всего на свете, генерирует ложные тревоги и приучает их игнорировать — а это куда опаснее, чем отсутствие мониторинга. Семантика каждого сервиса должна быть зашита в критерий проверки.
«Сервер работает» и «сервер доступен пользователю» — две разные проверки. Можно идеально настроить инфраструктуру и при этом быть невидимым для своей аудитории из-за сетевого пути. Локальные замеры на самом сервере не значат ничего — нужна проверка снаружи, из реальных сетей реальных пользователей. Бесплатный Globalping закрыл этот вопрос на отлично и стал частью моего регулярного арсенала: теперь после любого изменения инфраструктуры я могу за минуту увидеть карту доступности по российским городам.
Кнопку «выключить» нажимай только с доказательствами на руках. Страх перед необратимым действием лечится не смелостью, а данными. Когда у тебя есть два прогона аудита (до и после), 100% packet loss на старом IP как подтверждение смерти, и карта доступности нового сервера из России — нажатие кнопки перестаёт быть прыжком веры и становится просто следующим пунктом чеклиста. Именно к этому состоянию стоит стремиться в любой миграции: чтобы финальное отключение было самым скучным и предсказуемым шагом, а не самым страшным.

AI-инженер, предприниматель, маркетолог. Основатель feberra.com и x10seo.ru. 13 лет в перфоманс-маркетинге, 3 года в системной интеграции AI в бизнес.