Twenty Years, Off the Rack

Moving systems older than most of the team out of two racks and into someone else's data center. What genuinely gets better, what gets expensive, and why the naming convention turns out to be the most important architectural decision in the whole program - more consequential than the provider.

What Gets Left Behind

The starting point is two racks in a building somebody has to drive to: a virtualization cluster, a SAN a year past the point where anyone wants to discuss its warranty, a redundant pair of firewalls, and an application whose oldest running code predates everyone still maintaining it. It works. Everyone knows where it is.

Availability in that state is the sort of figure quoted as a percentage because the percentage sounds better than the hours. Some of it is hardware, most of it is change, and all of it is slow to recover, because recovery means a person and a maintenance window.

The trigger is rarely a strategy deck. It is a quote. The SAN needs replacing, half the hosts are out of warranty, and hypervisor licensing has just been repriced by an acquisition nobody in the room had a say in. Signing it means another five years of the architecture that is already the constraint.

Two years the other side of that decision, the good version of the outcome is real: shorter outages, deployments in minutes rather than windows, a bill below its own peak on a larger estate. What follows is the rest of it. The bill goes up before it comes down, the mess arrives before the benefits, and what decides whether any of it stays manageable is the naming convention almost nobody stops to write.

The Part That Is Genuinely True

Start with what is true, because the speed argument is the one that holds up.

Procurement leaves the critical path

On-prem, a new environment is a purchase order: quote, approval, lead time, rack, cable, image. Six to ten weeks, and the ten-week version is the one where a switch port is the missing piece. In the cloud it becomes a pipeline run, and the underrated half is the reverse: environments that can be thrown away without a meeting. One that exists for four hours cannot drift, cannot absorb somebody's manual fix, and cannot quietly become a production dependency, which is what every long-lived staging box eventually does.

Recovery gets cheaper than prevention

On-prem, a bad release is fixed forward under pressure, because rolling back means restoring something. With immutable images and two target groups, rollback becomes the cheapest action available. That is where most of the availability improvement comes from. Not fewer failures. Shorter ones.

The speed belongs to the pipeline, not the provider

Lift and shift while still deploying by hand to long-lived machines and the purchase is a monthly bill and a worse cable run. The provider sells the ability to be fast; the pipeline is what makes anyone fast, and it has to be built either way - which means much of the benefit was available at home.

And a mistake now ships globally in four minutes

The same pipeline that removes the six-week wait removes the six-week accident of a review. The brakes go back on deliberately: staged rollouts, health-gated promotion, and infrastructure changes through the same pipeline as code. Speed that cannot be stopped is not speed, it is a fault with better marketing.

Names Are Load-Bearing

Now the part to do first, and the part that is genuinely hard to care about at the start of a program when nothing has gone wrong yet.

On-prem, names are sloppy and it barely matters, because the physical world does the work: an asset tag, a rack position, a labeled cable, and a person who knows what the machine is because they carried it up the stairs. If a server is a mystery you can unplug it and see who calls. None of that survives the migration.

In the cloud the name is the entire interface: the bill line item, the alert at three in the morning, the log query, the access policy. There is no rack position and no cable, so the name is the only handle the resource will ever have. Worse, most names cannot be changed. Renaming a bucket, a storage account or a database identifier means creating a new one and migrating to it, which is a project, so it does not happen. There is one attempt, taken by whoever is moving fastest.

The naming space is also more constrained than anyone expects, inconsistently between resource types and providers:

  • Storage bucket names on AWS have been globally unique since 2006, across every account on earth. The name a convention wants is taken by a stranger, which is the actual origin of every bucket anyone has seen called something like acme-data-prod-2-final. That name is permanent and it is in the invoice.
  • Azure storage accounts allow 3 to 24 characters, lowercase letters and digits, no hyphens. The tidy hyphenated convention does not fit the resource it is most needed on, and that turns up while creating one, not while designing the standard.

So design for the tightest constraint and derive the rest, rather than designing something elegant and patching it per resource. What works is a full form and a compressed form, generated rather than typed, ordered broad to narrow so an alphabetical list groups usefully and a policy wildcard matches something meaningful:

  • Full form: org-workload-env-region-component-index, so acme-billing-prd-use1-api-01.
  • Compressed form where separators are not allowed: acmebillingprdue1st01. Twenty-one characters, which is the point - it has to fit the resource with the tightest limit.
  • Environment is always three characters from a closed set: prd, stg, dev, snd. Never prod in one place and production in another, because a grep across an estate is a real thing people do under pressure.

Tags carry what the name cannot, and the list stays short on purpose, because fifteen required fields is a list nobody fills in accurately: owner, cost-center, env, data-class, and expires on anything that is not production. Owner is a person, not a team alias, because aliases outlive the teams and a person can be asked a question.

The rule that makes it real: nobody types a name. A module composes it and the pipeline refuses anything that does not match - tag policies plus a service control policy on AWS, Azure Policy with a deny effect on the other side. A convention documented but not enforced is not a convention, it is a preference, and it holds until the first 2am incident where somebody creates a resource by hand to stop the bleeding.

The test to hold a proposed convention against: read one line of the bill, with no context and nobody to ask. Is it obvious what the resource is, which environment it belongs to, who owns it, and whether deleting it pages someone? If not, the convention has failed in exactly the situation it existed for.

How It Gets Messy

Skip that work and the estate does not fall over. It degrades quietly, in four directions at once.

Nobody deletes what nobody can identify

Six months in there are orphaned disks from instances that no longer exist, snapshots of a decommissioned database, a whole environment for a cancelled project, and a load balancer with no healthy targets. None of it gets deleted, because deleting something unidentifiable is how an outage happens, so the rational move is always to leave it. Flexera's 2024 survey put self-reported waste at 27 percent of cloud spend, and anyone who has audited an estate recognizes the figure.

The console becomes the real source of truth

Somebody fixes an incident by hand at 2am, correctly. Now the infrastructure code no longer matches reality, so the next plan shows a diff that would revert the fix. Nobody wants to apply that, so nobody applies anything, and within a quarter the infrastructure as code documents an intention rather than a system. It starts with one entirely defensible action.

Cost attribution collapses, and the conversation goes with it

Once the largest line in the cost report is untagged, every showback discussion ends with the team disputing the number, and they are right to. What replaces it is a blanket instruction to cut spend, applied evenly, which cuts the things with attentive owners and leaves the waste where it is.

The sprawl is silent

A rack is self-limiting: it fills up, and a human being has to decide what matters. A subscription has no walls. The only thing that fills up is the invoice, and it does that gradually, one defensible small decision at a time, until somebody in finance asks a question nobody can answer in the meeting.

What Gets Exposed

Security is where the migration changes character rather than difficulty, and the model people bring from the data center is wrong in a specific way: they think the hard part is still the network. The provider secures the cloud; the customer configures it, and can configure it wrongly entirely unaided, which is the side of the line practically every large cloud breach of the last decade sits on.

The perimeter stops being geography

On-prem, exposing a service takes a firewall change, a ticket, and a person whose job includes saying no. In the cloud it is one line in a template, and the line opening a port to 0.0.0.0/0 looks almost identical to the one opening it to an internal network. That review has to be taken as seriously as the firewall change it replaced.

Identity is the whole game now

Uber, 2016: a long-lived access key sat in a private GitHub repository, someone got into the repository, and the key opened storage holding data on 57 million riders and drivers. The word doing the work there is private. The key never expired, so when it leaked did not matter, only that it did. Use roles that issue short-lived credentials, federate the build system rather than storing keys in it, and assume anything that cannot be rotated automatically will not be rotated.

Over-permissive roles turn a small bug into a headline

Capital One, July 2019. A request forgery flaw on a firewall host let an attacker reach the instance metadata service, take the credentials of the role attached to that instance, and read 106 million records out of object storage. Two things made it fatal: metadata reachable without a session token, which the next version of that service fixed four months later, and a role that could read buckets it had no business touching.

Lift and shift moves the vulnerabilities, and the orphans have nobody

A 2012-vintage machine arrives with the same unpatched components, except the network it implicitly trusted is gone and it may now have a public address. A migration is the best chance anyone gets to not bring something, and the cutover date is what makes teams bring all of it. The worst finding in a first year is rarely a clever attack path. It is a forgotten test instance with a public address, an old image, and a name that says nothing about who made it.

The Bill

And the argument the business cares about, which is the one the industry has been least honest about.

Cloud is not cheaper. It is cheaper for some shapes of workload and more expensive for others, and the shape it is most expensive for is the one most companies are migrating: steady, predictable, always-on, running near a hundred percent duty cycle. That is precisely the profile served most economically on owned hardware, because the premium buys elasticity that goes unused.

The first year is more expensive, and anyone claiming otherwise is counting different things. On-demand rates apply to machines moved as-is, both estates run in parallel through the transition, and the migration labor lands on top. The savings arrive in year two, from work that has little to do with the migration itself:

  • Rightsizing, the largest single item. Machines were sized years earlier for a peak that never came back, and lifting them preserves the size. Nobody downsizes a physical server, so nobody has ever asked.
  • Turning non-production off outside working hours. Nights and weekends are two thirds of the week, and this is a scheduler and a tag.
  • Commitment discounts on the base load only. Reserve the floor that is certain, never the peak: a three-year commitment on a workload about to be re-architected is a bet against the roadmap.
  • Storage class and generation changes, and lifecycle rules moving old objects to colder tiers. Mechanical work.
  • Deleting what the tagging work exposes. This is the naming convention paying for itself in cash, which is why that work comes first.
  • Managed services replacing self-run middleware, where the saving is not on the invoice at all. It is the engineer-hours that stop going into patching a message broker.

Then the line items that surprise everyone, because they are priced per gigabyte in places nobody thinks of as network. Address translation gateways process traffic at $0.045 per GB at list price, and much of what goes through them is not internet traffic at all - it is workloads reaching object storage, registries and secret stores inside the same provider, which a private endpoint reaches directly for nothing. Traffic crossing availability zones is billed per GB in each direction, so a chatty service spread across three zones pays for that resilience continuously, in a line item not labeled resilience.

The metric to manage is not total spend. Total spend should rise when the business grows, and a falling cloud bill on a flat estate says nothing at all. Pick the unit the business actually sells - a device, a tenant, a transaction - and manage cost per one of those, monthly, in front of the engineers who move it. It is the only version of the number that survives a finance conversation. A capital expense negotiated once every five years has also become an operating expense a single merged pull request can alter, so cost has to be reviewed like latency or error rate: continuously, by the people who cause it.

And the honest counterweight. 37signals moved off the cloud during 2023 and has publicly reported saving on the order of two million dollars a year. At that profile - high, steady, predictable scale, with the skill already in the building - the arithmetic is correct, and the responses calling it heresy mostly came from people who had never run their own numbers. The mistake is assuming their answer generalizes. The questions that decide it are whether the load is predictable, whether the people are there, and whether the best engineers should be spending their attention on hardware.

AWS and Azure, Briefly

The provider comparison, briefly and on purpose.

Two differences show up in the actual plan, and neither is about which service is better. The first is how boundaries are drawn: accounts under an organization with service control policies on one side, subscriptions under management groups with policy assignments on the other. Both give a blast radius, a billing seam and a permissions edge in one line, and both are painful to redraw once workloads are inside. Draw them before the first wave. It is the decision regretted most often, and it gets made in week one.

The second is identity, and it is usually already decided. Where a company runs Microsoft 365 the directory is deployed, the groups exist, conditional access exists, and Azure inherits all of it on day one. Fighting that to land somewhere else is a two-year project that ends roughly where it started.

Past that: both will host the workload, both have a competent managed database, queue and container runtime, and both have a documentation page out of date in the same one place. The argument about which is better has eaten more afternoons than it has settled. It is the infrastructure version of tabs versus spaces, and it does not end with anybody changing their mind. Neither provider saves anyone from a bad naming convention, which is the only part of this section that generalizes.

What It Costs

The parts that do not make the deck, in the order they hurt.

The most experienced people become beginners

The person who knew every behavior of that storage array, including the two undocumented ones, now holds knowledge with no market value in the new estate. Some retrain and become better cloud engineers than anyone the company could hire, because they understand what the abstraction is abstracting. Some will not, and leave. The migration is a retraining program with an infrastructure side effect, and it should be said early and funded as one.

Control is traded for a support ticket

A disk fails on-prem and somebody walks over with a replacement. A service degraded in a zone means opening a case and sitting in front of the business with no ETA. Aggregate availability improves and the on-call experience gets worse at the same time, because the failures left are the ones nobody can personally act on. Warn the team before it happens to them at 4am.

The lock-in is not the virtual machines

Machines move. What holds anyone is the managed queue, the identity integration, the glue between six services and the database feature a product behavior was built on - which is, awkwardly, the same list as the things that made the team faster. Both providers waived egress charges for customers leaving in early 2024 under regulatory pressure, but the bandwidth was never what kept anyone. The rewrite is.

The last ten percent takes as long as the first ninety

There is always an appliance whose license is tied to a hardware address, an application whose vendor supports one operating system version, and a system with a latency budget a wide-area link cannot meet. Hybrid gets presented as a phase. It is an architecture, and the honest plan says so on the first slide instead of carrying a phase-four cleanup that never happens.

A twenty-year-old system only tells the truth in production

Re-architecting surfaces behavior nobody documented because nobody knew it was behavior: a nightly job whose real function is masking a race condition, a downstream team depending on a file a report happens to leave behind, an error path that has become the normal path. Those do not turn up in code review. They turn up in the cutover, at the worst hour, which is why waves run in parallel longer than the plan wants.

Cloud native is a rewrite, and lift and shift is a bill

Both are honest choices. The dishonest one is a program promising cloud-native outcomes on a lift-and-shift schedule. Lift and shift gets a company out of the data center on time and hands it the invoice without the elasticity; re-architecting gets the benefits and costs quarters of engineering nobody asked for. Most programs do both, in waves, and the failure is not saying so at the start.

Before The First Workload Moves

These go in before the first workload moves. Every one is cheap at the start and expensive to retrofit.

  • Write the naming convention first, generate names from a module, and enforce it with a policy that denies. One shot, no renames, design for the tightest constraint.
  • Five required tags, no more, and the one that matters most is a person's name.
  • Draw the account and subscription boundaries first. They are the blast radius and the bill, and they are the hardest thing to change later.
  • No long-lived credentials anywhere, including the build system.
  • Audit logging on from day one, into somewhere the workload's own credentials cannot delete from.
  • Rightsize before committing. A commitment discount on a machine twice the size it needs to be locks in the mistake at a discount.
  • Report cost per unit of business, monthly, to the engineers who move it.
  • Delete something every month. A team that has never deleted anything has an estate nobody understands.

Is it worth doing? Usually, and not mainly for the money. The availability improvement is real, the deployment speed changes what a team is willing to attempt, and the per-unit cost curve bends the right way as the business grows, which the old architecture would not have done.

But the reason it works is boring, and it is the part worth taking away. Names, boundaries, ownership, decided early and enforced by machines rather than good intentions. The cloud supplies none of those. What it does is remove every physical constraint that used to fake them - the rack that filled up, the port that had to be patched, the person who had to carry the thing up the stairs - and it removes all of them on the first day, before anyone has noticed they were doing a job.