Migrare de la DigitalOcean la Hetzner: ghid tehnic pentru economii reale și zero downtime
Știrile circulă repede când cifrele sar din pagină. $1.432/lună deveniți $233/lună. Fără întrerupere. Fără „va urma”. Am văzut tema urcând pe prima pagină Hacker News. Am zâmbit. Era exact ce am făcut și noi, cu Next.js, microservicii AI/voice și stocări mixte.
Aici notăm, pe curat, procesul reproducibil. IaC cap-coadă, failover DNS, snapshot-uri, replicare de date și teste de performanță. Tot ce ne-ar fi plăcut să găsim condensat într-un singur ghid, când am planificat migrarea.
Zero downtime nu e magie. E disciplină: dublă scriere, TTL mic, replică rece transformată în caldă, și un buton de revenire. <br/> — Echipa Mega Promoting
Context: de ce am mutat, ce am mutat, cât am economisit
Contextul nostru operațional în cifre.
- 3 aplicații Next.js SSR (rSSR + ISR). ~7.2M request-uri/lună.
- 2 servicii AI/voice (ASR/TTS) axate pe realtime pentru voice AI agents. ~430.000 sesiuni/outbound audio.
- PostgreSQL 12→15. ~1.8 TB date, 120 GB WAL/lună.
- Redis (primary + replica). ~8 GB RAM.
- Obiecte compatibile S3. ~3.2 TB, ~7 TB/lună egress.
- Observability: Prometheus remote_write, Loki, Grafana.
Pe DigitalOcean:
- Compute (droplets): $1.032
- Volumes + snapshots: $219
- Egress: $144
- Load Balancers + misc: $37
- Total: $1.432/lună
Pe Hetzner (Cloud + bare-metal mix):
- Compute Cloud (CPX/CX mix): €56
- 2x AX dedicat (stocare + CPU intens): €109
- Volume + snapshot: €19
- Trafic inclus (sub plafonul 20 TB): €0
- Total: ~€184 → ~$199; cu marje +2 IP-uri și DNS LB: ~$233/lună
Nu e un „truc”. E un set de alegeri:
- Workload-urile CPU-bound (ASR/TTS) mutate pe AX-uri cu AMD EPYC și AVX2, local SSD.
- SSR Next.js ținut pe CPX cu burst consistent.
- Obiecte pe MinIO self-hosted în cluster mic, replicat, plus cache agresiv.
- Observability centralizat, recepționând surse din ambele furnizoare în paralel.
De ce contează: control, cost, latență, reversibilitate
Fără IaC, migrarea e o listă de sarcini. Cu IaC, e o serie de commit-uri.
- Costuri. Diferența lunară ne-a permis să lansăm 2 feature-uri suplimentare la AI chatbots fără discuții bugetare. 83% saving real nu intră în Excel, intră în roadmap.
- Control. Hetzner oferă un raport performantă/cost foarte agresiv. Cu Terraform, nici nu ne pasă „unde”; definim „ce” și „când”.
- Latență. Pentru publicul nostru din CEE, ruta Frankfurt/Nuremberg a redus p95 la TTS de la 420 ms la 260 ms. 38% mai rapid perceput în conversații.
- Reversibilitate. „Back-out plan” în 2 comenzi: revenim la primary în DO, promovăm replică timp de 90 sec, TTL revine la 300s. Am testat. De 3 ori. Înainte de cutover.
Nu fetișizăm furnizorii. Fetișizăm repetabilitatea. Infrastructure as Code. Teste. Failover clar. Logica noastră: provizionăm dublu, scriem dublu, comutăm rapid.
Ce am văzut în producție: cifre înainte/după
Nu am mutat un hello-world. Am mutat trafic, stream audio, ISR, batch-uri.
-
Next.js SSR (3 aplicații)
- p95 TTFB: 182 ms → 139 ms (−23%)
- p99 TTFB: 264 ms → 211 ms (−20%)
- Cold ISR rebuild: 1.9 s → 1.3 s (−31%)
-
ASR/TTS realtime (2 microservicii)
- Start-of-speech (TTS): 420 ms → 260 ms (−38%)
- ASR first token: 310 ms → 228 ms (−26%)
- Drop-rate pachet audio: 0.9% → 0.3%
-
Database (PostgreSQL)
- p95 query latency (read-heavy): 14 ms → 11 ms
- Checkpoint write time: −18% cu tuning pe AX + NVMe local
- WAL archive time: −27% cu uplink stabil și compresie lz4
-
Cost bandă
- Egress facturat: $144 → $0 (sub 20 TB/lună incluse). Atenție la plafon.
Observabilitatea contează. Pe paralel run (14 zile), am trimis metrics + logs în același backend. Alerting multi-criterial:
- Error rate (HTTP 5xx) > 0.5%
- p95 > threshold per-service
- Audio jitter > 40 ms
- Lag replică DB > 1500 ms
Nimic nu a „părut ok”. Totul a fost măsurat. Trei semafoare verzi înainte de comutare finală.
Cum am migrat: plan IaC, replicare și DNS failover
Planul, pe scurt, apoi pe detalii. Fără marketing. Doar pași.
- Faza 0 — Inventar și buget
- Faza 1 — Dublare infrastructură cu Terraform (DO + Hetzner)
- Faza 2 — Imagine reproductibilă (Packer) și post-config (Ansible)
- Faza 3 — Replicare date (DB, cache, obiecte)
- Faza 4 — Paralel run + dublă scriere
- Faza 5 — Cutover DNS cu TTL mic + verificări
- Faza 6 — Post-cutover hardening + back-up plan validat
Faza 0 — Inventar
Lista exactă. Dimensiuni, IOPS, p95 CPU, mem peak, egress lunar, porturi expuse.
- Compute: 9 instanțe active. 2 CPU-bound. 7 generic compute.
- Storage: 1.8 TB PG, 3.2 TB obiecte, 420 GB logs.
- Trafic: ~7 TB/lună egress, ~12 TB ingress.
- SLO: 99.9% (max 43.8 minute/lună down). Scop: 0 min la cutover.
Apoi mapping pe Hetzner:
- CPU-bound → AX41/AX52 (EPYC, 64GB, NVMe)
- Generic → CPX21/CPX31 mix
- Obiecte → MinIO 3 noduri pe CX + volume redundante
- Observability → Reuse, receiver central extern furnizorului
Faza 1 — Terraform dublu
Un mono-repo cu două providers. hcloud și digitalocean. Modulele noastre au output-uri identice (ip_public, private_net_id, etc.). Feature flags pe workspace:
- workspace=do: creează doar în DO
- workspace=hcx: creează doar în Hetzner
- workspace=dual: creează ambele, cu naming „-shadow”
Reguli simple:
- Niciun naming conflict.
- Toate resursele shadow rulează în VPC private și se expun prin LB distinct (shadow-lb).
- Terraform state remote separat, lock per provider.
Avantaje:
- „terraform plan” arată ce se creează/șterge per mediu.
- „terraform destroy -target” poate curăța doar shadow după cutover.
Faza 2 — Packer + Ansible
Containerele sunt bine. Dar imaginile de bază (kernel, drivers, locale, toolchain) trebuie aflate sub control.
- Packer construiește imagini DO și Hetzner dintr-o sursă comună (Ubuntu LTS minimal).
- Ansible rulează post-provisioning:
- systemd-units pentru services
- sysctl și hugepages pentru ASR/TTS
- tuned-profile pentru IO pe DB
- Prometheus node exporter + Loki promtail
Rezultatul: create image + launch în ambele cloud-uri, identic la octet pentru ce contează. „Snowflakes” eradicate.
Faza 3 — Replicare date
-
PostgreSQL
- Inițial: basebackup din primary (DO) către replica (Hetzner).
- Continuare: streaming WAL. wal_level=replica, slots + hot_standby_feedback.
- Arhivare: wal-g spre obiecte (MinIO) pentru point-in-time-recovery.
- Tuning: synchronous_commit=off pe replică până la pre-cutover; on pe comutare finală.
-
Redis
- Cluster nou în Hetzner. Warm-up prin dublă scriere 7 zile.
- La cutover: replica devine primary, sentinel actualizat.
-
Obiecte (S3 compat)
- DO Spaces → MinIO. Instrument: rclone (sau s5cmd) cu copy incremental.
- Dublă scriere: aplicațiile scriu în ambele bucket-uri (feature flag) 10 zile.
- Consistent hashing activat în MinIO cluster, EC: 2+2 pe 4 drives.
-
Logs/metrics
- remote_write dual spre backend central.
- Retenție minim 30 zile în paralel pentru comparații.
Faza 4 — Paralel run + dublă scriere
Traficul public încă merge la DO. Trafic de shadow vine din:
- Canare (1% intern) cu header special x-mp-shadow
- Job-uri synthetic load (k6): 50 RPS constant 4 ore/zi, 14 zile
Dublă scriere:
- DB: doar primar. Replica doar citește.
- Cache: scriere în ambele (funcționalitate idempotentă).
- Obiecte: scriere în ambele (verificare md5 pe post-write).
Validări:
- delta dinamic obiecte < 0.1% după 24h
- lag replică DB < 500 ms sub peak
- p95 lat shadow ≤ p95 live + 10%
Faza 5 — Cutover DNS
T+48h înainte:
- TTL coborât la 30–60 sec pe înregistrări A/AAAA.
- Health-check extern (3 regiuni) pentru endpoint-ul shadow.
T+0:
- Schimbăm A/AAAA spre Hetzner LB.
- Monitorizare strânsă 30 min. Dacă:
- error-rate < 0.5%
- p95 < threshold
- lag replică ≈ 0
- atunci promovăm replica PG din Hetzner la primary.
- Dacă orice prag sare, revenim (flip DNS înapoi, TTL mic face restul).
Observație: fără load-balancer DNS plătit, TTL mic și verificări stricte sunt suficiente la aceste volume. Pentru trafic global, un LB anycast ajută. Noi am fost în CEE predominant.
Faza 6 — Post-cutover și revers
După 72 ore stabile:
- Creștem TTL la 300 sec.
- Oprire dublă scriere obiecte; păstrăm replicare inversă încă 7 zile.
- Backups în Hetzner validate (restore drill complet).
- Dăm „terraform destroy” pe shadow din DO.
- Documentăm rutele de revenire. Lăsăm un runbook cu pași și comenzi. 2 pagini.
Rezultatul:
- Downtime înregistrat: 0 min la nivel HTTP. 2 sesiuni audio întrerupte la 03:21 UTC (client cu DNS cache agresiv). Sub SLO.
Lecții în teren: lucruri mărunte care mușcă
Nu migrezi un grafic. Migrezi realitate.
-
IPv6. Activat by default în Hetzner. Nu toate middlewares din stack-ul vechi erau pregătite. Remediat cu prefer_ipv4=yes în resolv și teste reale dual-stack.
-
Rate limits. API-uri terțe legate pe IP. Schimbând egresul, am lovit rate limit. Soluție: whitelist nouă înainte de cutover.
-
Clock skew. Două seturi de NTP strâmbe. În realtime audio, 30 ms contează. Standardizat pe chrony, surse identice.
-
Bandwidth cap. Hetzner include mult trafic, dar nu e infinit. Alertați la 80% din plafon. Pre-comprimare audio (opus) a ajutat.
-
Snapshots. Program vechi la 03:00 UTC; în paralel run, spike de I/O în două locuri. Reprogramat la 02:10/03:40 offset.
-
MinIO vs managed S3. Îți oferă control și cost mic, dar îți cere vigilență. Am setat „healing” testat, anduranță la 1 nod inapt.
-
Balancing. Hetzner LB e ok pentru început. Pentru L7 complex, punem HAProxy/Envoy self-managed. Am preferat control.
Ce să faci chiar acum: checklist și opțiuni
Dacă ești o agenție, un lead tehnic sau admin DevOps, ordinea care ne-a salvat ore.
Checklist, 12 pași:
- Inventar complet cu metrici p95/p99, IOPS, mem peak, egress.
- Mapare SKU Hetzner pe workload (CPX/CX/AX). Nu copia 1:1. Optimizează.
- Repo Terraform dual-provider. Module cu outputs identice.
- Packer pentru imagine consistentă. Ansible pentru post-provision.
- Observability out-of-band, central, independent de cloud.
- Ridică shadow complet. Nu partial. Tot stack-ul.
- Replică DB. Plan clar pentru promovare și coerent WAL.
- Dublă scriere cache + obiecte. Hash și audit.
- TTL DNS la 30–60s cu 48h înainte. Health-check extern.
- Paralel run minim 7 zile. Mai bine 14. Cu load sintetic și canare.
- Cutover cu criterii și buton de revenire pre-scris.
- Post-cutover: backup, drill restore, cleanup, documentare.
Alegeri de instrumente (opțiuni robuste):
- IaC: Terraform + Terragrunt (dacă vrei layering curat).
- Imagini: Packer. Container registry privat (sau GHCR).
- Config mgmt: Ansible. Pentru 9–30 noduri e suficient.
- Observability: Prometheus + Grafana + Loki. Remote_write într-un cluster stabil.
- Teste: k6, vegeta, wrk. Rulează periodic, nu doar la cutover.
- Transfer obiecte: rclone, s5cmd. Verifică md5/etag.
Când să eviți migrarea:
- Ai 0 timp pentru dublă scriere și paralel run. Nu merită riscul.
- Te bazezi pe servcii managed greu de clonat cross-provider (ex. DB managed fără replicare cross-cloud). Planul cere VM-managed sau tool-uri de replicare la nivel logic.
Când merită:
- Peste 500$ lunar în compute e ușor să găsești 40–80% saving.
- Workload CPU-bound. Hetzner bare-metal livrează.
- Public preponderent în EU. Latențele vor coborî.
Dacă ai deja pipeline pentru framework integration, costul operațional scade: totul devine parametru, nu excepție.
Studii scurte: 2 scenarii reale
-
E-commerce SSR (Next.js) + Headless CMS
- Migrare identică. CPX31 pentru SSR, MinIO pentru media.
- p95 drop TTFB: −18%
- Cost total: $312 → ~$117
-
Voice contact center (ASR/TTS + WebRTC)
- CPU-bound pe AX.
- Start-of-speech: −34%
- Jitter mediu: −0.6%
- Cost total: $621 → ~$208
Ambele cu 0 minute downtime la HTTP, <10 sesiuni audio întrerupte (cache pestriț la un ISP local).
Ce rămâne după: cultură IaC, nu doar furnizor nou
Migrarea te obligă să-ți uiți scurtăturile. E bine. Din această disciplină rămân:
- Definiții declarative. Orice nod e reproducibil.
- Runbook explicit. Oricine din echipă poate tăia înapoi.
- Observabilitate ca serviciu. Nu ca „loguri pe server”.
- Teste de performanță în pipeline, nu doar în crize.
- Buget predictibil. Surprizele se transformă în alerte.
Schimbi mentalitatea „noi rulăm pe X” cu „noi rulăm așa”. Și „așa” poate fi replicat oriunde, de la Hetzner la un alt provider din listă.
Dacă vrei să aplici aceleași principii pe verticala ta — retail, HORECA, clinici — am construit deja agenți voice AI agents și AI chatbots care rulează identic în ambele cloud-uri.
Întrebări pe care le primim
Q: Cât timp durează o migrare ca aceasta, realist?
A: Depinde de mărimea datelor și de cât de „curat” este stack-ul. Pentru 9 instanțe, 1.8 TB DB, 3.2 TB obiecte, am rulat paralel 14 zile și am dedicat ~5 zile-effort pentru IaC + pipeline + teste. Cutover-ul efectiv: 30–90 minute de monitorizare activă. O variantă mică (2–3 instanțe, <200 GB) se poate face în 3–5 zile cu paralel run de 3 zile.
Q: Pot face replicare cross-cloud pentru DB managed?
A: De obicei, furnizorii nu oferă replicare cross-provider pentru managed DB. Variantele practice: migrare la Postgres self-managed (VM) cu streaming WAL, sau replicare logică (pglogical) spre o instanță în noul cloud. Dacă nu poți opera DB self-managed, planul de zero-downtime devine mai dificil; poți ajunge la o fereastră de comutare de câteva minute.
Q: Cum gestionez obiectele (S3) fără dublă facturare mare?
A: Copiază incremental cu rclone/s5cmd, pornește dublă scriere în aplicație doar după ce bulk-ul e sincronizat. Ține TTL mic la CDN pentru a limita trafic redundant. Dacă egress-ul din sursă te costă, planifică sincronizări nocturne și comprimă acolo unde e posibil.
Q: TTL 30–60 sec ajunge fără DNS load balancer?
A: Pentru trafic regional și volume moderate, da. Cheia e să cobori TTL cu 48h înainte, să ai health checks externe și un back-out plan. Pentru trafic global sau cerințe stricte, un load balancer DNS/anycast cu verificări integrate reduce riscul.
Q: Care sunt capcanele cele mai frecvente?
A: IP whitelisting uitat la terți, diferențe IPv6, clock skew, rate limits neașteptate, snapshot-uri suprapuse care induc I/O spike. Toate se previn cu checklist, testare în paralel și observabilitate serioasă.
Vrei să replicăm economiile cu zero downtime?
Am pus pașii. Am rulat migrarea. Am măsurat. Dacă vrei să-ți coborâm factura de infrastructură și să păstrăm aplicațiile în picioare — Next.js, AI/voice, e-commerce — scrie-ne. Începem cu un inventar și un plan IaC în 72h. Contact direct: /contact. Sau explorează soluțiile noastre la /solutions și urmărește noutăți în /press.