🚚 Moving Your First Workload to the Cloud: Inventory, Sync, Cutover and Rollback

A calm, step-by-step plan for moving one small server to a cloud VM: the seven migration strategies, finding the hidden dependencies, copying data in rounds, a short cutover and a rollback that really works

💡 How this page was tested. The data-copying and checking steps were run for real on 6 October 2026 against a made-up "shop" server in a scratch folder: the hunt for hardcoded addresses with grep, three rounds of rsync (first copy, delta, final pass with a deletion), and verification by checksum, including what a damaged file looks like. No cloud account was used, so the cloud side (the migration services, creating the VM, DNS changes) is from the providers' documentation, read on 6 October 2026, and not run here. Related: networking, backups and DR, DNS, cost control and the security baseline.

Start with one small workload

Your first move should be something small, boring and easy to put back: an internal tool, a test site, a low-traffic web app. You learn the cloud's networking, identity and billing on a workload where a mistake costs an evening, not a business day.

The plan has six steps:

  1. Choose a strategy for this workload.
  2. Inventory it, including the things nobody wrote down.
  3. Build the target in the cloud, empty, from code where you can.
  4. Copy the data in rounds while the old server keeps working.
  5. Cut over in a short, rehearsed window.
  6. Keep the old server for a while, so you can go back.

Step 1: choose a strategy

AWS's migration guidance names seven strategies, the "7 Rs" (from AWS's documentation, read on 6 October 2026). The names are useful on any cloud:

For a first move, rehost is usually right. Changing the application and the hosting in the same weekend means that when it breaks you do not know which change did it.

Step 2: inventory, and the dependencies nobody wrote down

Before copying anything, write down:

The last two items on the "what it connects to" list are where migrations fail. A configuration file often contains an address written in by hand years ago. A quick search finds many of them. This is from the made-up shop server:

user@oldserver:~$ grep -rnE '([0-9]{1,3}\.){3}[0-9]{1,3}|/mnt/|\.corp\.' /etc/shop /etc/cron.d
/etc/shop/allowlist.txt:1:ALLOW 203.0.113.40 # partner office
/etc/shop/app.conf:1:db_host = 10.0.4.17
/etc/shop/app.conf:3:cache = 10.0.4.22:6379
/etc/shop/app.conf:4:smtp = mail.corp.example:25
/etc/shop/app.conf:5:license_server = 192.168.50.9
/etc/shop/app.conf:6:backup_target = /mnt/nas/shop
user@oldserver:~$ cat /etc/cron.d/shop
0 2 * * * root /usr/local/bin/shop-report >> /var/log/shop-report.log

Every line is a question for the new environment:

Searching for addresses does not catch everything. Also look at who connects to the old server (ss -tn on a busy day, firewall logs) and at the clocks: the new VM needs time sync and the right time zone, or scheduled jobs and logs will quietly be wrong. Azure's own migration service includes a dependency analysis step for the same reason: so you do not "miss any critical dependency".

What the providers offer

For many servers, or for VMware and Hyper-V virtual machines, each cloud has a service that replicates the machine continuously and lets you test a copy before the switch. For one small server you often do not need it: a fresh VM plus copied data (the next steps) is simpler and leaves you with a clean OS. But it is worth knowing what exists. From the documentation, read on 6 October 2026:

⚠️ Tools that replicate a whole machine copy everything, including a compromised file, an old unpatched OS and years of cruft. A clean VM with the data copied across is often the safer move for a single server.

Step 3: build the target first

Create the cloud side empty, before any data moves:

Use this time to prove the new server works without the old one's data: starts, passes a health check, logs where you expect.

Step 4: copy the data in rounds

The trick for a short outage is to copy the bulk while the old server is still live, then copy only what changed while it is stopped. rsync does this well, over SSH. These runs are local folders standing in for the two servers; between servers you would add the destination as user@newserver:/path/, and the options are the same.

user@lab:~/mig$ rsync -a --stats --no-human-readable old/ new/ | grep -E 'Number of (regular )?files|transferred|Total (transferred )?file size'
Number of files: 19 (reg: 9, dir: 10)
Number of regular files transferred: 9
Total file size: 100263 bytes
Total transferred file size: 100263 bytes
user@lab:~/mig$ # (a customer orders, a file is uploaded to the old server)
user@lab:~/mig$ rsync -a --stats --no-human-readable old/ new/ | grep -E 'Number of (regular )?files|transferred'
Number of files: 20 (reg: 10, dir: 10)
Number of regular files transferred: 2
Total transferred file size: 20022 bytes
user@lab:~/mig$ # (the old server is stopped; final pass, mirroring a deletion)
user@lab:~/mig$ rsync -a --delete --itemize-changes old/ new/
*deleting   var/www/shop/uploads/img1.bin
user@lab:~/mig$ rsync -rc --dry-run --itemize-changes old/ new/
user@lab:~/mig$ diff <(cd old && find . -type f -exec sha256sum {} + | sort -k2) <(cd new && find . -type f -exec sha256sum {} + | sort -k2) && echo identical
identical
user@lab:~/mig$ # (now damage one file on the copy and check again)
user@lab:~/mig$ echo x >> new/var/www/shop/uploads/img2.bin
user@lab:~/mig$ rsync -rc --dry-run --itemize-changes old/ new/
>fcsT...... var/www/shop/uploads/img2.bin

What each round did:

  1. First copy, server still running: 9 files, all of them transferred. Take as long as it needs: nobody is waiting.
  2. Delta, after an order and an upload arrived: only 2 files moved, 20,022 bytes. The more often you repeat this, the shorter the final window gets.
  3. Final pass, with the application stopped on the old server (so nothing changes any more): --delete makes the copy mirror deletions (*deleting ... img1.bin). Be careful: --delete removes files on the destination that are not on the source. Point it at the wrong folder and it removes real data, so try it with --dry-run first, and mind the trailing slash: old/ means "the contents of old", old means "the folder itself".
  4. Verify. A checksum comparison (rsync -rc --dry-run --itemize-changes) printed nothing, meaning no differences, and the second check with sha256sum printed identical. When one file was deliberately damaged on the copy, the same command named it (>fcsT......: the c means the checksum differs).

Things rsync -a keeps (permissions, ownership when run as root, timestamps, symlinks) and things it does not: hard links (add -H), ACLs (-A), extended attributes (-X), and numeric IDs (--numeric-ids if the user and group IDs differ between the two machines). Databases are different: never copy the files of a running database. Use the database's own dump or replication (see backups), or stop it for the final copy.

For a large amount of data over a slow link, do the arithmetic first: size divided by real throughput (not the line rate). The providers sell offline transfer devices for this (Azure's documentation mentions Data Box), and you can start the first copy weeks early.

Step 5: cutover

A cutover is a short, rehearsed list, not an improvisation. Write it down, run it once against a copy, and time it.

A day or two before

The window

  1. Stop the application on the old server (not the machine): no more writes.
  2. Final sync and verification (step 4).
  3. Start the application on the new VM, and run your checks: the health check, a real login, a real order, the scheduled job run by hand, an outgoing connection to each dependency from the inventory.
  4. Switch DNS (or the load balancer or the floating address) to the new address.
  5. Watch the logs on both sides: the old server should go quiet, the new one should see real traffic. Clients that ignored the TTL will keep arriving at the old address for a while: stragglers are normal.

Define "good" before you start: the exact checks and the time by which you decide to roll back. When you are tired at 02:00 you will not make a good judgement on the spot.

Step 6: rollback, and keeping the old server

Because the old server was only stopped, not changed, rollback is short: point DNS back, and start the application on the old server again. But there is one trap: any data written to the new server after the cutover is not on the old one. So the rollback plan needs an answer to this: either roll back only within a short window (before real writes happen), or copy the new data back with the same rsync in the other direction.

After a successful move, keep the old server, switched off or idle, for a defined period (a few weeks is common, but decide it for your workload) and take a final backup of it. Then retire it deliberately: wipe the disks if it leaves your hands, remove its DNS records, monitoring checks, backup jobs, firewall rules and licences, and stop paying for it. Check the cloud bill after the first week: a surprise there is better found early (cost control).

Common mistakes

What this page did not cover

Database migration in detail (replication, managed database services), large-scale discovery tools in practice, Windows servers, VMware and Hyper-V image conversion, hybrid networks with a VPN or dedicated link, licensing, compliance and data-residency review, and zero-downtime cutovers with a load balancer and weighted DNS.

The cheat sheet

← Serverless Functions on Every Cloud: Handlers, Events and Limits · ↑ All tutorials