The VPS is usually the oldest thing a small company owns. Someone set it up years ago and has since moved on, it runs the booking app or the invoicing portal, and nobody reboots it because nobody is certain it will come back. Then the host sends a notice about a hardware migration, or the disk fills at 2am, and the question lands on a Tuesday: can we move this to Google Cloud without closing for the weekend?
Usually yes, and the weekend is not the interesting part. The order is. Every broken migration we have been called in to clean up failed in the same two ways: traffic was pointed at the new machine while the data was still catching up, or DNS was changed before anyone checked how long the old record would stay cached.
What we read before anyone touches the server
The first day is inventory, not engineering. We write down which processes listen on which ports, what the app talks to off this box, every cron job and what it writes, where the TLS certificates come from, whether the server sends mail directly, and whether the OS still gets security updates. Say a 12-person logistics company has one VPS: more often than not there is a forgotten cron job emailing a report to the accountant, and it is the thing that breaks three days after the move.
One question then sets the plan: how many minutes of downtime is the business allowed? Google's migration guidance ties strategy to that tolerance and is blunt about the trade-off, since workloads that cannot accept downtime need near-zero-downtime approaches, which cost more and are more complex to run.
Compute Engine, Cloud Run, or both later
Google's guidance on choosing a compute option is clear: Cloud Run suits stateless container applications and takes the OS, kernel, network setup and capacity management off your hands, while Compute Engine is the choice when you need control over the operating system, disk images, CPU, RAM and disk, which is the typical path for migrating an application that already exists.
The filesystem decides it for most VPS apps. Cloud Run instances are stateless, and each container gets an in-memory writable filesystem overlay that does not persist once the container shuts down. If your app writes customer uploads to a folder, keeps sessions on disk or runs on SQLite, it is not a Cloud Run app yet. It can become one, but that is application work with its own testing, and doing it in the same week as the move is how a one-day cutover becomes a three-week outage.
So our default for a working business app is Compute Engine first, with the database broken out to Cloud SQL in the same move. Land it, prove it, then pick pieces off toward Cloud Run later. Two changes at once means that when something breaks you cannot tell which change broke it.
What the migration tooling will and will not take
Search for how to migrate a server and you will be pointed at Migrate to Virtual Machines, which replicates a source VM continuously and lets you build a test clone before cutting over. It is good, and it probably does not apply to you: its documented sources are a vSphere data center, AWS, Azure and Google Cloud VMware Engine. A VPS at DigitalOcean, Linode, Hostinger or a regional host is none of those, so that path is off the table.
Two honest options remain. Rebuild the server on a fresh Compute Engine instance from your configuration, installing the packages and restoring the application and data with every step written down. Or import the disk, which accepts .vmdk and .tar.gz files, only monolithic VMDK, one boot disk at a time, and the old gcloud compute images import command is being deprecated in favour of gcloud migration vms image-imports.
We rebuild whenever we can. An imported disk brings the old kernel, the old packages and whatever the last administrator left behind, so you pay for a migration and keep every problem that made the server scary.
The database decides your cutover window
Dump the database, copy the file, restore it, and your downtime is the length of that dump and restore, growing with the data. Fine for a small database and a quiet Sunday. For anything busier, Database Migration Service changes the shape of the problem: a continuous migration job runs an initial full dump and load, then keeps replicating changes from the source. When the destination is an existing Cloud SQL instance, the service demotes it to a read replica while the job runs.
Cutover is then one step called promotion, which disconnects the destination from the source and promotes it from replica to primary so the migrated databases accept reads and writes. The recommended sequence is worth memorising: stop writes at the source, wait for the replication delay to reach zero, then promote. Google notes you can promote before the delay hits zero to cut downtime, with the honest warning that it may affect the accuracy of the data at the destination, and that dependent applications can expect downtime of at least that replication delay.
Read that as a planning rule: your cutover window is the replication lag, not the size of the database. If the lag is four seconds at 9am on a Wednesday, you do not need a weekend.
DNS: lower the TTL days before you need it
This step gets skipped and cannot be fixed on the day. Cloud DNS guidance is explicit: if you are planning a change that needs a narrow window, lower the TTL first, because the shorter value only applies after the previous TTL expires in resolver caches. If your A record has been at 24 hours, lowering it this morning does nothing for the next 24 hours.
Three more things about DNS day. Some resolvers ignore your TTL and use their own, so a slow tail of traffic to the old IP is normal rather than a fault. Many resolvers cache negative answers for up to 15 minutes, so a typo punishes you for longer than the fix takes. And Cloud DNS pushes a change to its authoritative name servers within about 120 seconds, so while you wait, query those name servers directly rather than a public resolver.
The consequence: keep the old server running and reachable until traffic to its IP actually stops. Raise the TTL back up a week or two later.
The cutover, in order
Here is the plan. The right-hand column is the gate: if it is not true, you do not move to the next row. That rule is what keeps a cutover from becoming an incident.
| When | What we do | What must be true before the next step |
|---|---|---|
| 7 days out | Lower the TTL on the records that will change, and list every third party pointing at the old IP | The previous TTL has expired, so the short value is in force |
| 3 days out | Build the Compute Engine and Cloud SQL instances, restore a copy of the data, run the app end to end on a test hostname | A timed dry run exists and the app serves real journeys |
| 1 day out | Start the continuous migration job to Cloud SQL and let it settle | Replication is running and the lag is small and steady |
| Hour zero | Put the app in maintenance or read-only mode so writes stop at the source | No new writes reach the old database and the lag is heading to zero |
| Zero plus minutes | Promote the destination, point the app at it, change the DNS records | Smoke tests pass and the authoritative name servers answer with the new address |
| Zero plus one hour | Watch logs, error rates and the mail the app sends, old server still running | Errors are at the normal baseline and nothing fails silently |
| 1 to 2 weeks later | Decommission the old VPS and raise the TTL again | A backup of the new database has been restored once and no traffic reaches the old IP |
Rollback, and the hour you give it
Google's best practices for validating a migration plan ask for two things small projects skip: a rollback strategy for each step, tested rather than assumed, and a maximum execution time per step, after which you start rolling back instead of pushing on. We turn that into a number written down before the day. If the new path is not serving clean traffic by 09:40, we go back, and nobody decides that at 09:38 with the owner watching.
Be honest about the cost of going back. Google's guidance notes that rolling a migration back can take more effort than fixing forward, and that undoing data movement means copying data again, which is why you keep the source data for a while after cutover. The real line is the first write: once orders have landed in the new database, going back means losing them or merging them by hand. That is the entire reason for the read-only window.
Cost and risk notes
A VPS is a flat monthly fee and Google Cloud is metered, so the first surprise is usually a bill with a shape instead of a number. We set a budget with alert thresholds on day one, before the first instance exists, and check current rates on Google's pricing pages rather than trusting any blog, including this one.
Two risks worth naming. If the app sends mail straight from the server, the new sending path needs its authentication set up, or week one after the move is a deliverability problem instead of a hosting one. And on 19 October 2026 Google's release notes recorded graceful shutdown for Compute Engine as generally available: it is off by default, and once enabled it gives the guest operating system a grace period to finish work when the instance is stopped or deleted, using a 10-minute default when you do not set one, with shutdown scripts running after that process ends. On a machine that writes to a database, it is cheap protection against a stop at the wrong moment.
How Guanacos Tech helps
We run this as a fixed piece of work: read the VPS and write the inventory, choose between Compute Engine and Cloud Run with the filesystem question answered honestly, build the target, do a timed dry run, then run the cutover on a scheduled hour with the rollback time agreed in advance. You keep the runbook, with the gates in the table above filled in for your stack. That work sits inside our Google Cloud consulting for small teams engagements. Book a 30-minute call if you want a second opinion on a move you are already planning, and we will tell you where we think it will hurt.
Sources
- Google Cloud: choose compute options
- Google Cloud: what is Cloud Run
- Google Cloud: Migrate to Virtual Machines architecture and sources
- Google Cloud: import virtual disks to Compute Engine
- Google Cloud: promote a Database Migration Service migration job
- Google Cloud: Cloud DNS overview (TTL and caching)
- Google Cloud: best practices for validating a migration plan
- Google Cloud: Compute Engine graceful shutdown overview