Skip to content

DigitalOcean → Hetzner

Hetzner Cloud consistently delivers 3–4× better price-to-performance than DigitalOcean. A 4 vCPU, 8 GB server that costs $48/month on DigitalOcean costs approximately €14/month on Hetzner — for equivalent or better hardware.

Migrating away from DigitalOcean is one of the most common things teams use RasnuCloud for. This guide walks through a complete production migration.


The Scenario

Source: DigitalOcean Droplet — Ubuntu 22.04, 4 vCPU, 8 GB RAM
Running: Nginx, a Node.js API on PM2, PostgreSQL 15, Redis 7

Destination: Hetzner CX32 — freshly provisioned Ubuntu 24.04

Goal: Complete the migration with under 5 minutes of user-visible downtime


Step 1 — Connect Both Servers

Connect your DigitalOcean server →
Connect your Hetzner server →

Once both servers are connected, RasnuCloud maps them completely. It knows your exact Node.js version, PM2 process count, PostgreSQL version, and Redis configuration — without you having to list any of it.


Step 2 — Generate the Migration Plan

Go to Dashboard → New Migration. Select the DigitalOcean server as source and the Hetzner server as destination. Click Generate Plan.

RasnuCloud produces a plan specific to your stack. For this example:

Phase 1  Destination Preparation          (~10 minutes)
  → Install Node.js 20 LTS, PM2
  → Install PostgreSQL 15 (matching source exactly)
  → Install Redis 7 with persistence config from source
  → Install and configure Nginx with your site configs

Phase 2  Data Migration                   (~20–40 minutes)
  → Snapshot PostgreSQL databases (estimated 2.1 GB)
  → Transfer to Hetzner and restore
  → Verify: row counts and checksums match on both servers
  → Sync Redis keyspace

Phase 3  Application Setup                (~5 minutes)
  → Sync application files from /var/www/api
  → Install production dependencies
  → Copy environment configuration, update DB connection to localhost
  → Start application with PM2
  → Confirm application returns HTTP 200

Phase 4  Live Cutover                     (~3–5 minutes downtime)
  → Start PostgreSQL streaming replication: DigitalOcean → Hetzner
  → Wait for replication lag = 0 seconds
  → Enable maintenance mode on source (HTTP 503)
  → Final data sync
  → Promote Hetzner as standalone primary
  → Update DNS: your domain → Hetzner IP
  → Disable maintenance mode
  → Run external health checks

Step 3 — Approve and Execute

Review the plan. If anything needs adjusting, tell RasnuCloud before approving:

The maintenance mode step — I want to handle that manually. Skip it from the plan.

Once you're satisfied, click Approve and Start. Watch each step execute in real time.


Step 4 — Cutover

When RasnuCloud reaches Phase 4, it waits for your explicit confirmation before switching traffic. This is your last checkpoint:

Replication is running with 0 seconds lag. Destination passed all health checks.
Ready to initiate cutover. This will cause approximately 2–3 minutes of downtime.

Type CONFIRM to proceed.

After cutover, RasnuCloud runs external verification checks from outside your servers to confirm the domain is resolving to Hetzner and the application is responding correctly.


Step 5 — Decommission

After 24–48 hours of stable operation on Hetzner, delete the DigitalOcean droplet — RasnuCloud will remind you and walk you through it:

Everything has been running on Hetzner for 48 hours with no issues.
I'm ready to remove the DigitalOcean droplet. Confirm and I'll handle it.

Lower Your DNS TTL First

If you're switching DNS during cutover, lower your domain's TTL to 60 seconds 24 hours before the migration. This ensures your DNS change takes effect in under 2 minutes instead of hours.

Released under the MIT License.