ARCHITECTURE · COST COMPARISON AND BUDGET

Cloud migration cost comparison and budget planning

A migration budget fails in two ways: it compares prices of things that are not alike, or it forgets everything that is not a price. The design question is which method gives you a number you can defend. This page sets out four approaches, shows what a list-price model built from your Terraform can state, using a StackToIT run that it priced with and without assumptions, and gives a rule for choosing.

Four ways to build the budget

Pick one as the spine and use the others to check it. Rows marked practice are general method, not something StackToIT computes.

ApproachWhat it gives youWhat it cannot give you
1. List-price model from the assessed estate (the run)A line per billable resource on both sides, with its state and the assumption behind it; a fast first view of coverageUsage-based cost, support, licences, operations, migration labour; any saving across currencies without an exchange rate
2. Usage-based model from billing exports and monitoring (practice)Real quantities for traffic, requests and storage growthA target design; STACKIT lines until the design is chosen
3. Pilot wave, measured (practice)Measured run cost and migration effort for one workloadA figure for workloads of another shape; the cost of waiting for the pilot
4. Supplier quote for committed capacity (practice)A price for a stated commitmentA comparison until the sizes are fixed by one of the approaches above

Approach 1 is fast and open about its gaps. Approaches 2 and 3 fill the gaps with measurement.

What the run priced and what it left open

Assessed by StackToIT on 4 November 2026: two application servers, a data volume, a Multi-AZ PostgreSQL database and a backup bucket on AWS. Written for this page.

Example inputmain.tf
resource "aws_instance" "app" {
  count         = 2
  ami           = "ami-0abcdef1234567890"
  instance_type = "m5.xlarge"

  root_block_device {
    volume_size = 80
    volume_type = "gp3"
  }
}

resource "aws_ebs_volume" "data" {
  availability_zone = "eu-central-1a"
  size              = 500
  type              = "gp3"
}

resource "aws_db_instance" "main" {
  identifier        = "main"
  engine            = "postgres"
  engine_version    = "16.3"
  instance_class    = "db.m5.xlarge"
  allocated_storage = 200
  multi_az          = true
  username          = "app"
  password          = "change-me-at-migration"
}

resource "aws_s3_bucket" "backups" {
  bucket = "acme-app-backups"
}
  • Why this shape

    Compute, block storage, a database and an object bucket are the lines most budgets are made of, and they show all four reasons why a line stays unpriced.

Two runs: why a line has no price

The table of pricing states in financial/cost-coverage.md, from a run without assumptions and from a second run with the assumption file shown below.

Run 1: no assumptionsfinancial/cost-coverage.md
15 lines in all: 6 of the source, 9 of the target.

| State | Lines | Meaning |
|---|---|---|
| AMBIGUOUS | 1 | More than one price fits and none is picked. |
| PRICED | 5 | A price record and a known quantity give an amount. |
| PRICE_SOURCE_ABSENT | 1 | No price source was available for this line. |
| QUANTITY_UNKNOWN | 1 | A price is known and the quantity is not: neither zero nor one. |
| TARGET_NOT_CHOSEN | 7 | The target design does not choose what the price needs. |
Run 2: with the assumption filefinancial/cost-coverage.md
15 lines in all: 6 of the source, 9 of the target.

| State | Lines | Meaning |
|---|---|---|
| AMBIGUOUS | 1 | More than one price fits and none is picked. |
| PRICED | 9 | A price record and a known quantity give an amount. |
| PRICE_SOURCE_ABSENT | 1 | No price source was available for this line. |
| TARGET_NOT_CHOSEN | 4 | The target design does not choose what the price needs. |
  • Run 1

    Of 15 lines, 5 are priced. 7 wait for a target choice such as a server flavor, 1 is ambiguous between prices, 1 has no source price, 1 has a price and no quantity.

  • Run 2

    With the assumption file, 9 of 15 lines are priced. Of the 6 left, 4 are the server boot volume and the data volume, whose target the file does not name, 1 is the AWS server compute (operating system and tenancy not stated) and 1 is the AWS bucket storage, for which the cache holds no price list.

The assumption file

Two of the four entries of the file passed with --assumptions in run 2. The other two name a server flavor and a PostgreSQL storage class as target choices. Each entry names a subject, a variable, a value and a reason. The values are demo figures, not recommendations.

Overlap entryassumptions.yaml
schema_version: "1.0"
assumptions:
  - id: cutover
    scenario: STANDARD_730H
    subject: scenario
    variable: parallel_run_months
    value: "2"
    unit: month
    explanation: "Planning figure for this demo: two months of side-by-side running"
Bucket volume entryassumptions.yaml
  - id: volume-size-stackit-objectstorage-bucket.backups
    scenario: STANDARD_730H
    subject: stackit_objectstorage_bucket.backups
    variable: volume_size
    value: "2000"
    unit: GB
    explanation: "Demo figure: backups hold about 2 TB on average"
  • Only what you state is used

    The template that the run writes lists every value the model waits for. A line the file does not mention stays unpriced. Note that in run 2 cost-model.json lists only the overlap and the bucket entry as used, although the two target choices priced their lines; check each priced line's reason text, which names the assumption.

  • Planning figure for overlap

    parallel_run_months is how long both estates run at once. The run states the per-month figures of each side, multiplies them by your months, and keeps the result out of every total and comparison. In run 2 that was 736.47 USD per month for the priced source lines and 1,355.93 to 1,531.01 EUR for the priced STACKIT lines on the demo choices, each a lower bound because unpriced lines are missing.

How the model states a line

Read from the two runs. The class says how much of a line is fact.

State or classMeaningNext action in the run
PRICEDA price record and a known quantity give an amountNone; check the assumption if the class is ASSUMPTION
TARGET_NOT_CHOSENThe draft leaves the flavor, plan or storage class to a variableState the choice in the assumption file or in the inputs file
AMBIGUOUSMore than one price fits and none is picked, for example the operating system of an instanceState the missing fact
PRICE_SOURCE_ABSENT, CACHE_MISSINGThe price list of the source offer is not in the cacheFill the price cache once, online, with stacktoit prices refresh
QUANTITY_UNKNOWN, USAGE_NOT_STATEDA price is known and the use of one month is not; the line is neither 0 nor 1State the use, for example gigabytes held in a bucket
KNOWN, ESTIMATE, ASSUMPTION, OUT_OF_MODELFact from the source, a stated multiplier such as 730 hours, a quantity or class you assumed, or not computedTreat OUT_OF_MODEL as unknown, never as zero

In run 2 the estate-level comparison was PARTIAL and stated no saving, because source prices were in USD, STACKIT prices in EUR, and no exchange rate was given.

What belongs on the budget and is not in the model

The model computes no usage, traffic, support, licence, operations or migration labour cost. Put these on the budget yourself.

  • Data transfer and requests

    The model prices usage only where the file states one month of it. Your source cloud decides what it charges to read and transfer objects out; ask for the charge of the volumes you move.

  • Version history

    Amazon S3 charges normal rates for every version of an object, each a whole object. Copying history multiplies the volume to read and to store, so decide whether you move it.

  • Redundancy settings

    Azure redundancy options trade lower cost against higher availability. STACKIT Object Storage has no managed cross-region replication according to the run, so a second copy is a sync job with its own storage and transfer.

  • Parallel running

    Both estates cost money while you cut over. The run states it apart as a one-off item, per month of overlap and for your assumed months, in no total.

  • People and time

    Migration labour, retraining and the pilot are yours to estimate with approach 3.

  • Savings timing (practice)

    A saving counts from the day the source is retired, not from the day the target runs. Plan the retirement as its own milestone.

How to decide

A rule you can apply in a steering meeting.

  • Start with approach 1 on every estate

    It is cheap and shows how much of the estate can be priced at all. A coverage of 5 of 15 lines is a finding, not a failure.

  • Close target choices with the architects

    TARGET_NOT_CHOSEN lines are design questions. Answer them in the inputs file, then reprice.

  • Add measured quantities for the top cost lines

    Use approach 2 for storage and traffic where the line has a price and no quantity.

  • Use a pilot wave when workloads differ

    If the estate has several shapes, measure one of each before you extrapolate.

  • Present ranges and coverage, not a single figure

    Show the share of lines priced, the assumptions behind them and the items kept out. Run 2 already gives a range for the STACKIT side rather than one figure.

Questions finance and architecture ask

Does StackToIT say how much we will save?

Only when both sides are priced for the same units in one currency. Run 2 stated no saving, and said why. A saving percentage needs a full comparison with an exchange rate whose source and date are named.

Are the prices current?

They come from a price cache with a retrieval date shown in the run (26 October 2026 here). Both runs showed the AWS S3 offer missing from the cache and named the refresh command. The demo runs were offline; without --offline an assessment can fetch missing public prices itself.

Is the output an invoice?

No. The run calls its figures an initial infrastructure cost estimate under explicit assumptions, not a bill: list prices, no discounts, no Spot, no savings plans.

What about the cost of the migration itself?

Not computed. The one one-off item the model states is the cost of running both estates in parallel, apart from every total.

Which currency should we use?

Use one for the decision. The model keeps each side in its own currency and compares only with an exchange rate you give.

Sources

Your next move: STACKIT.

Tell us about your estate. We show you what StackToIT delivers for it: target architecture, Terraform artifacts and a structured migration plan.