🚚 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:
- Choose a strategy for this workload.
- Inventory it, including the things nobody wrote down.
- Build the target in the cloud, empty, from code where you can.
- Copy the data in rounds while the old server keeps working.
- Cut over in a short, rehearsed window.
- 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:
- Retire: switch it off. Worth asking first: AWS's guidance suggests candidates are applications with no inbound connection for 90 days, or CPU and memory use below 5 percent ("zombie") or 5 to 20 percent over 90 days ("idle").
- Retain: leave it where it is, for now. Reasons include data-residency rules, special hardware, a recent upgrade, or another system that must move first.
- Rehost ("lift and shift"): move it unchanged to a cloud VM. The least thinking and the quickest, and this page's example.
- Relocate: move a whole platform (for example a VMware environment) to the cloud version of the same platform, or move a resource between accounts, networks or regions.
- Repurchase ("drop and shop"): replace it with a vendor's SaaS product.
- Replatform ("lift, tinker and shift"): move it with a few changes, for example a database to a managed database service.
- Refactor or re-architect: redesign it for the cloud. The most complex and costly. AWS recommends against doing it during a large migration: rehost or replatform first, modernise afterwards.
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:
- What runs: services, scheduled jobs (cron and systemd timers), listening ports, the OS and its version, and which users and groups own the files.
- What data it holds and how much, and how fast it changes.
- Who connects to it, and what it connects to.
- How it is backed up now, and how long a restore takes.
- Who owns it and how much downtime they can accept. This number drives the whole plan.
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:
- 10.0.4.17 (database) and 10.0.4.22 (cache): are they moving too, or must the cloud VM reach them across a VPN? Private ranges in the cloud are your own ranges, and they must not overlap with the ones you still use on site (see the networking page).
- mail.corp.example is a name that only resolves inside the company. Does it resolve from the cloud?
- 192.168.50.9 (a licence server) may check the client's address or allow only the old server's address.
- /mnt/nas/shop is a mount that will not exist on the new VM.
- 203.0.113.40 is on someone else's allow list (the partner office is allowed in, but the partner's firewall may only accept the old server's public address). Your new public address will be different and they will need time to change theirs.
- The cron job and its log path must exist on the new machine too.
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:
- Azure Migrate is a free service for deciding, planning and executing a migration. Its discovery uses a lightweight appliance in your datacentre (there is also a collector for air-gapped or restricted networks). A business case compares on-site and Azure costs. Assessments estimate Azure readiness, right-sized VMs and cost, with dependency analysis. It migrates VMware VMs (agentless or agent-based), Hyper-V VMs, physical servers and VMs from other clouds (treated as physical servers), plus databases, web apps and, with Data Box, large offline data.
- AWS lists AWS Transform MGN (application migration service), the Cloud Migration Factory Solution and VM Import/Export for rehosting. AWS says the rehosted application keeps serving users while the machines are replicated, and the downtime depends on your cutover strategy.
- Google Cloud Migrate to Virtual Machines migrates VM instances and disks from on-site vSphere, Google Cloud VMware Engine, AWS and Azure to Compute Engine VMs or Persistent Disk volumes. The overview page I could read did not describe replication or cutover details, so check the current guides.
- Oracle: I did not read Oracle's migration documentation for this page. Check its current migration guides.
⚠️ 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:
- Account and budget alerts first (cost control).
- Network: the VPC or VNet, subnets, and firewall rules that allow only what the inventory justified (networking).
- The VM, sized from measured use on the old server (not from the old server's specification, which is often generous), built with cloud-init or a script (cloud-init) or Terraform (Terraform basics), so you can rebuild it if the rehearsal goes wrong.
- Identity: a role for the VM instead of keys in files (IAM).
- Backups from day one (backups and DR).
- Install the same software versions as the old server first. Upgrade later.
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:
- First copy, server still running: 9 files, all of them transferred. Take as long as it needs: nobody is waiting.
- 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.
- 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".
- 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
- Lower the DNS TTL on the names clients use to a few minutes, at least one old TTL before the cutover, so caches expire quickly when you switch (DNS).
- Warn the users and the partners (the allow-list problem from the inventory), and make sure the new public address is reserved, not changing.
- Run the delta sync now, and a test of the application against the new VM with a hosts-file entry on your own machine, so you see what users will see before they do.
The window
- Stop the application on the old server (not the machine): no more writes.
- Final sync and verification (step 4).
- 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.
- Switch DNS (or the load balancer or the floating address) to the new address.
- 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
- Not inventorying: the hardcoded address, the NAS mount, or the partner's allow list that nobody remembered.
- Overlapping private address ranges between the cloud network and the one at the office, found after the VPN is built.
- Changing too much at once: new OS, new database and new host in a single move.
- Copying a live database's files.
- A DNS TTL left at 24 hours, so half your users reach the old server for a day.
- rsync --delete aimed at the wrong folder, or a missing trailing slash.
- No rehearsal, so the cutover takes four hours instead of twenty minutes.
- No rollback plan, or one that loses the data written since the cutover.
- Sizing the VM from the old server's spec instead of its real use.
- Forgetting backups, monitoring and time sync on the new VM.
- Switching the old server off for good on the day of the cutover.
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
- Plan: choose a strategy, inventory, build the target, copy in rounds, cut over, keep the old one for a rollback.
- Strategies (the 7 Rs): retire, retain, rehost, relocate, repurchase, replatform, refactor. For a first move, rehost, and modernise later.
- Inventory: services, jobs, ports, data size, who connects, what it connects to, owner, allowed downtime. Search the configs: grep -rnE '([0-9]{1,3}\.){3}[0-9]{1,3}|/mnt/' /etc/app.
- Copy: rsync -a old/ new/ repeatedly, then rsync -a --delete with the app stopped. --dry-run first. Mind the trailing slash.
- Verify: rsync -rc --dry-run --itemize-changes old/ new/ prints nothing when the copies match. Add -H -A -X --numeric-ids when they matter. Never copy a live database's files.
- Cutover: lower the TTL beforehand, rehearse, stop the old app, final sync, start the new one, run the checks, switch DNS, watch both logs.
- Rollback: point DNS back and restart the old app. Decide what happens to the data written meanwhile.
- Afterwards: keep the old server for a defined time, take a last backup, then remove its records, jobs and billing.
- Providers: Azure Migrate (free, discovery, assessment, migration), AWS Transform MGN and VM Import/Export, Google Cloud Migrate to Virtual Machines. Oracle's tools not checked here.
← Serverless Functions on Every Cloud: Handlers, Events and Limits · ↑ All tutorials