---
title: How to Migrate to AWS Without Cost Surprises
description: AWS migration cost estimates are consistently wrong — not because the tools are bad, but because they miss the parallel run period, data transfer during migration, and the operational tax of learning a new environment. Here is what to actually model.
url: https://www.factualminds.com/blog/aws-migration-without-cost-surprises/
datePublished: 2026-03-29T00:00:00.000Z
dateModified: 2026-04-16T00:00:00.000Z
author: Palaniappan P
category: Cloud Architecture
tags: how-to-guide, aws, migration, cost-optimization, lift-and-shift, re-architecture, data-transfer, finops, migration-planning
---

# How to Migrate to AWS Without Cost Surprises

> AWS migration cost estimates are consistently wrong — not because the tools are bad, but because they miss the parallel run period, data transfer during migration, and the operational tax of learning a new environment. Here is what to actually model.

AWS migration cost estimates are wrong with remarkable consistency. Not in random ways — they are wrong in the same ways across almost every project. They model the steady-state AWS environment accurately. They account for reserved instance discounts, instance right-sizing, and managed service replacements. What they miss is everything that happens in the gap between "current infrastructure" and "AWS infrastructure running in production."

That gap has a cost. It includes weeks of parallel operation, one-time data transfer charges, DMS replication costs, license portability complications, and the operational overhead of a team learning a new environment. These costs are budgetable if you know to look for them. This guide covers what migration estimates miss, how to model the true total cost of a migration, and how to structure the migration itself to minimize cost overruns.

## Why Migration Estimates Are Consistently Wrong

The typical AWS migration estimate uses one of two tools: AWS Migration Evaluator (formerly TSO Logic) or a spreadsheet-based TCO comparison. Both have the same blind spot: they model steady-state.

**What the estimates include:**

- On-demand EC2 instance costs for migrated workloads
- RDS or Aurora equivalent costs for migrated databases
- Storage costs for EBS, S3, and EFS equivalents
- Networking costs (approximate, often underestimated)
- Reserved Instance or Savings Plan discounts (1-year or 3-year)
- Support plan costs

**What the estimates exclude:**

- The parallel run period (both old and new environments running simultaneously)
- Data transfer costs during migration (initial bulk transfer + ongoing replication)
- AWS Database Migration Service (DMS) costs
- CloudWatch, CloudTrail, AWS Config, and Security Hub costs (enabled by default, often not modeled)
- License portability complexity (Windows Server, SQL Server, Oracle)
- The operational overhead of the first 3–6 months (more incidents, longer resolution times, more support cases)

The parallel run period alone adds 10–30% to the first-year migration cost for most projects. Teams that do not model it find their first-year savings estimate was too optimistic by that margin.

## Lift-and-Shift vs Re-Architecture: The Three-Year Cost Calculus

The migration approach decision has a larger cost impact than any individual AWS service selection.

### Lift-and-Shift (Rehost)

Lift-and-shift moves workloads to equivalent EC2 instances with minimal change. The application runs on AWS but retains on-premises architecture patterns: dedicated instances, static provisioning, manual scaling.

**Year 1 cost profile:**

- Migration cost: low (AWS Application Migration Service is free for replication, you pay EC2 and EBS during cutover)
- Operational cost: typically 10–20% lower than on-premises (no hardware refresh, reduced colocation costs)
- Engineering cost: low (minimal application changes)

**Year 3 cost profile:**

- Infrastructure running the same oversized instances, same static capacity
- No benefit from auto-scaling, managed services, or serverless patterns
- Technical debt accumulates (the application is "on AWS" but not "cloud-native")

Lift-and-shift is the correct choice when: the migration timeline is fixed (datacenter contract expiring), the application is being decommissioned after migration, or the application is stable and low-traffic with limited optimization opportunity.

### Re-Architecture (Re-Platform or Refactor)

Re-architecture converts on-premises workloads to use managed AWS services: EC2 to ECS/Fargate, self-managed databases to Aurora, on-premises queues to SQS.

**Year 1 cost profile:**

- Migration cost: high (application changes, testing, parallel operation period is longer)
- Operational cost: comparable to or slightly higher than on-premises during transition
- Engineering cost: significant (refactoring takes time)

**Year 3 cost profile:**

- Infrastructure scales with demand (auto-scaling, Fargate, serverless)
- Managed services eliminate operational overhead (patching, backups, high availability)
- Cost typically 30–50% below lift-and-shift equivalent at the same traffic level

The three-year TCO comparison for a representative web application (8 EC2 instances, 2 database servers, 5 TB storage):

| Approach                 | Year 1                       | Year 2   | Year 3   | 3-Year Total |
| ------------------------ | ---------------------------- | -------- | -------- | ------------ |
| On-premises (status quo) | $180,000                     | $180,000 | $180,000 | $540,000     |
| Lift-and-shift           | $165,000 + $25,000 migration | $145,000 | $140,000 | $475,000     |
| Re-architecture          | $170,000 + $60,000 migration | $120,000 | $90,000  | $440,000     |

Re-architecture has higher Year 1 cost but wins over 3 years. If your organization evaluates migrations on Year 1 ROI, lift-and-shift looks better. If you evaluate on 3-year TCO, re-architecture wins. Know which lens your organization uses before recommending an approach.

## AWS Migration Evaluator: What It Tells You and What It Misses

Migration Evaluator collects performance data from your on-premises environment (via the Migration Evaluator Collector or on-premises agents) and generates a report estimating equivalent AWS costs with right-sizing recommendations.

**What Migration Evaluator does well:**

- Identifies CPU and memory utilization to recommend appropriate EC2 instance families
- Calculates 1-year and 3-year cost projections with Reserved Instance discounts
- Flags instances with consistently low utilization (rightsizing candidates)
- Provides a directionally accurate estimate for leadership presentations

**What Migration Evaluator misses:**

- Database migration complexity (DMS costs, replication lag, validation effort)
- Application-level dependencies that require simultaneous cutover
- License portability costs (SQL Server on EC2 requires license verification; some licenses require Dedicated Hosts at `$3.12/hour` vs `$0.19/hour` for standard EC2)
- The parallel run period
- Networking costs in the migrated environment (NAT Gateway, data transfer, VPN)

Use Migration Evaluator for the steady-state estimate, then add a migration cost line item of 15–25% of Year 1 AWS cost to account for what it misses. Our [AWS cloud migration services](/services/aws-migration/) build this line item into the SOW so parallel-run and DMS costs are named up front, not buried in contingency.

## Data Transfer Economics During Migration

Initial data transfer to AWS is free — AWS does not charge for ingress. The costs are elsewhere.

### Internet Transfer

For databases under 1 TB: internet transfer is fast and free (from AWS's perspective). Your colocation or hosting provider may charge egress fees. Check your contract — egress fees at colocation facilities range from $0 (included in monthly commit) to `$0.05–0.10/GB`. A 500 GB database transfer at `$0.08/GB` egress: `$40`. Negligible.

For applications with ongoing replication during the parallel run period: DMS replication continuously transfers change data from source to target. The source egress costs accumulate over weeks.

### AWS Snowball vs Direct Connect vs Internet

The decision framework for bulk data transfer:

**Internet transfer:**

- Best for: under 10 TB, available bandwidth, no tight migration window
- AWS ingress: free
- Your egress: varies by provider

**AWS Snowball Edge (80 TB usable):**

- Device cost: `$300/device` + `$15/day` (order to return, typically 10–14 days = `$450–510` total)
- Data transfer after loading: free
- Best for: 10+ TB where internet would take days, constrained bandwidth, offline environments
- Breakeven vs internet: when your data center egress cost × data volume > `~$500`, or when speed is a constraint

**AWS Direct Connect:**

- Setup: `$0` for virtual interfaces, but requires cross-connect at a colocation ($50–200/month port fee)
- Data transfer: `$0.02/GB` (vs `$0.09/GB` internet egress from AWS after migration)
- Best for: ongoing hybrid connectivity post-migration, not one-time migration transfer
- Migration use case: if you are establishing Direct Connect for hybrid connectivity anyway, route migration traffic through it

### Database Migration Service Costs

DMS is not free. The cost model:

- Replication instance: `$0.065–0.26/hour` depending on instance class (`dms.r5.large = $0.26/hour = $187/month`)
- Storage: `$0.023/GB/month` for DMS replication storage
- Data transfer: standard AWS data transfer rates if the target is in a different region

For a typical database migration with 4 weeks of ongoing replication (full load + change data capture until cutover): `$187 × 1 month = $187/month` for the replication instance. At `dms.t3.medium = $0.065/hour`: `$47/month`. DMS instance cost is modest; the cost that matters is the engineering time to configure, test, and validate the replication.

**DMS Serverless: No Instance Sizing Required**

DMS Serverless removes the need to select and size a replication instance. Instead of paying per hour for a provisioned instance, DMS Serverless charges per DCU (Data Capacity Unit) per hour — you pay only for compute consumed during replication, not idle capacity between bursts.

Cost model: `$0.18 per DCU-hour` (us-east-1). DCU consumption scales with replication volume. For short-burst migrations (full load over a few days, then cutover), DMS Serverless is typically cheaper than provisioning a fixed instance for the full migration window. For long-running CDC workloads, model expected DCU consumption against instance-based pricing before choosing.

### DMS Replication Task Terraform

```hcl
resource "aws_dms_replication_instance" "main" {
  replication_instance_id    = "${var.project_name}-dms"
  replication_instance_class = "dms.r5.large"
  allocated_storage          = 50
  publicly_accessible        = false
  multi_az                   = false  # Single-AZ acceptable for migration (not HA)

  vpc_security_group_ids = [aws_security_group.dms.id]
  replication_subnet_group_id = aws_dms_replication_subnet_group.main.id

  engine_version               = "3.5.2"
  auto_minor_version_upgrade   = false
  allow_major_version_upgrade  = false

  tags = var.tags
}

resource "aws_dms_replication_subnet_group" "main" {
  replication_subnet_group_description = "DMS subnet group for ${var.project_name}"
  replication_subnet_group_id          = "${var.project_name}-dms-subnet-group"
  subnet_ids                           = var.private_subnet_ids

  tags = var.tags
}

resource "aws_dms_endpoint" "source" {
  endpoint_id   = "${var.project_name}-source"
  endpoint_type = "source"
  engine_name   = "mysql"  # or postgres, sqlserver, oracle

  server_name = var.source_db_host
  port        = 3306
  username    = var.source_db_username
  password    = var.source_db_password
  database_name = var.source_db_name

  tags = var.tags
}

resource "aws_dms_endpoint" "target" {
  endpoint_id   = "${var.project_name}-target"
  endpoint_type = "target"
  engine_name   = "aurora"

  server_name   = aws_rds_cluster.main.endpoint
  port          = 3306
  username      = var.target_db_username
  password      = var.target_db_password
  database_name = var.target_db_name

  tags = var.tags
}

resource "aws_dms_replication_task" "full_load_and_cdc" {
  migration_type            = "full-load-and-cdc"
  replication_instance_arn  = aws_dms_replication_instance.main.replication_instance_arn
  replication_task_id       = "${var.project_name}-migration"
  source_endpoint_arn       = aws_dms_endpoint.source.endpoint_arn
  target_endpoint_arn       = aws_dms_endpoint.target.endpoint_arn

  table_mappings = jsonencode({
    rules = [
      {
        rule-type   = "selection"
        rule-id     = "1"
        rule-name   = "include-all-tables"
        object-locator = {
          schema-name = var.source_db_name
          table-name  = "%"
        }
        rule-action = "include"
      }
    ]
  })

  replication_task_settings = jsonencode({
    TargetMetadata = {
      TargetSchema            = ""
      SupportLobs             = true
      FullLobMode             = false
      LobChunkSize            = 64
      LimitedSizeLobMode      = true
      LobMaxSize              = 32
    }
    FullLoadSettings = {
      TargetTablePrepMode = "DO_NOTHING"
      CreatePkAfterFullLoad = false
      StopTaskCachedChangesApplied = false
      StopTaskCachedChangesNotApplied = false
      MaxFullLoadSubTasks = 8
      TransactionConsistencyTimeout = 600
      CommitRate = 50000
    }
    Logging = {
      EnableLogging = true
    }
  })

  tags = var.tags
}
```

The `full-load-and-cdc` migration type performs an initial full load of all tables, then switches to Change Data Capture to keep the target in sync until cutover. This enables zero-downtime migration: the source and target stay synchronized until you are ready to cut over.

## The Parallel Run Period: Budgeting What Everyone Skips

The parallel run period is the time between "AWS environment is ready" and "on-premises environment is decommissioned." Both environments run simultaneously, and you pay for both.

The cost model is straightforward: `(AWS monthly cost × parallel_weeks/4.3) + (on-premises weekly cost × parallel_weeks)`.

For a `$5,000/month` AWS environment with a 4-week parallel run:

- AWS cost during parallel run: `$5,000 × 4/4.3 = $4,651`
- On-premises cost continues: assume `$8,000/month` current spend = `$8,000 × 4/4.3 = $7,442`
- **Total parallel run period cost: ~$12,093**
- Net savings if parallel run was not in the estimate: you spent `$12,093` you did not plan for

Typical parallel run durations by migration type:

- Simple web application, no compliance requirements: 1–2 weeks
- Production transactional system, business-hours validation: 2–4 weeks
- Regulated environment (healthcare, financial services): 6–12 weeks

The parallel run is not waste. It is the cost of validating that the migration is safe. Budget it explicitly in your migration estimate as a line item, not hidden in contingency. This is one reason we structure [AWS migration engagements](/services/aws-migration/) around a fixed-scope SOW with discovery up front — surprises get scoped before they land on your invoice.

## Gradual Traffic Cutover with Route 53 Weighted Routing

Cutting over 100% of traffic immediately from old to new infrastructure is high-risk. Weighted routing allows a gradual shift: 10% to new, validate, then 50%, then 100%. Route 53 weighted routing Terraform:

```hcl
# Phase 1: 10% to new AWS environment
resource "aws_route53_record" "legacy" {
  zone_id = var.hosted_zone_id
  name    = var.domain_name
  type    = "A"

  weighted_routing_policy {
    weight = 90  # Reduce as validation progresses
  }

  set_identifier = "legacy-datacenter"

  alias {
    name                   = var.legacy_load_balancer_dns
    zone_id                = var.legacy_load_balancer_zone_id
    evaluate_target_health = true
  }
}

resource "aws_route53_record" "aws_new" {
  zone_id = var.hosted_zone_id
  name    = var.domain_name
  type    = "A"

  weighted_routing_policy {
    weight = 10  # Increase as validation progresses
  }

  set_identifier = "aws-new"

  alias {
    name                   = aws_lb.main.dns_name
    zone_id                = aws_lb.main.zone_id
    evaluate_target_health = true
  }
}
```

Update the weights by changing Terraform variables and applying:

- Day 1: `legacy = 90`, `aws = 10`
- Day 3 (after monitoring): `legacy = 50`, `aws = 50`
- Day 7 (after business cycle validation): `legacy = 0`, `aws = 100`

Route 53 weights are not exact percentages — they are proportional ratios. A weight of 90 and 10 means approximately 90% and 10% of traffic (Route 53 does not guarantee exact split, especially at low traffic volumes).

## Collecting Baseline Metrics with CloudWatch Agent

Right-sizing requires performance data from the source environment. The CloudWatch Agent can collect from on-premises servers before migration:

```json
{
  "agent": {
    "metrics_collection_interval": 60,
    "run_as_user": "cwagent"
  },
  "metrics": {
    "namespace": "MigrationBaseline",
    "append_dimensions": {
      "ServerName": "${aws:ec2-instance-id}",
      "Environment": "on-premises",
      "MigrationProject": "2026-q1-migration"
    },
    "metrics_collected": {
      "cpu": {
        "resources": ["*"],
        "measurement": ["cpu_usage_active", "cpu_usage_iowait", "cpu_usage_steal"],
        "totalcpu": true
      },
      "mem": {
        "measurement": ["mem_used_percent", "mem_available_percent"]
      },
      "disk": {
        "resources": ["/", "/data"],
        "measurement": ["disk_used_percent", "disk_read_bytes", "disk_write_bytes", "disk_read_time", "disk_write_time"]
      },
      "diskio": {
        "resources": ["*"],
        "measurement": [
          "diskio_reads",
          "diskio_writes",
          "diskio_read_bytes",
          "diskio_write_bytes",
          "diskio_iops_in_progress"
        ]
      },
      "net": {
        "resources": ["eth0"],
        "measurement": ["net_bytes_sent", "net_bytes_recv", "net_packets_sent", "net_packets_recv"]
      }
    }
  }
}
```

Install the CloudWatch Agent on source servers 30 days before the planned migration. This gives you a full month of data including any business cycle peaks (month-end processing, weekly batch jobs, peak traffic periods). The CloudWatch Agent on on-premises servers sends metrics to CloudWatch via the internet (free ingress) and you pay standard CloudWatch custom metrics charges (`$0.30/metric/month` — `$30–50/month` for a 10-server environment, worth every dollar for right-sizing accuracy).

## Right-Sizing: The Before/After Comparison

The right-sizing methodology:

1. Collect p50 and p95 CPU and memory from baseline metrics over 30 days
2. Map to AWS instance families:
   - CPU-bound (p95 CPU > 60%, memory < 60%): `c7g` (ARM, 20% cheaper) or `c6i` (x86)
   - Memory-bound (memory p95 > 70%): `r7g` or `r6i`
   - Balanced: `m7g` or `m6i`
3. Size to p95 utilization + 30% headroom (accounts for traffic growth during validation period)
4. Plan a right-size review at day 30 post-migration

Initial migration instances are typically 20–30% oversized. This is intentional — you do not want to discover an instance is too small during the parallel run period. The day-30 right-size review converts the migration instances to the appropriate production size. This is also when you purchase Savings Plans (after you know the production instance family and size).

## Rollback Cost Complexity

Every migration plan assumes rollback is free. It is not.

Rollback scenarios and costs:

- **DNS rollback:** Change Route 53 weights back to legacy. Cost: `$0`. Time: DNS propagation (TTL, typically 60–300 seconds).
- **Database rollback:** If DMS is running continuous replication, the source database is current and rollback is a DNS change. If DMS was stopped at cutover, rollback requires reverse replication (manual or DMS reverse task). Cost: 1–2 days of DMS replication instance (`$10–50`).
- **Full environment rollback after 30+ days on AWS:** Old environment has been decommissioned. Rollback requires re-provisioning on-premises hardware or negotiating emergency capacity with your former provider. This is not a real rollback option — it is a crisis.

The practical conclusion: keep the parallel run period active (both environments running) until you have at least one full business cycle of production data confirming the AWS environment performs correctly. Decommissioning the legacy environment on day 8 to "save money" removes your real rollback option.

## The Invisible AWS Costs: Services Enabled By Default

When you create a new AWS account or a new account in AWS Organizations, several services start generating charges immediately:

- **CloudTrail:** By default, management events are free (first delivery of management events per region). Data events cost `$0.10/100,000 events`. If your application generates high S3 or Lambda traffic and you enable data events in CloudTrail for security, budget `$50–500/month`.
- **AWS Config:** `$0.003/configuration item recorded`. A production account with 500 resources recording configuration changes daily: `~$450/month`. Config is often enabled by compliance requirements; it is not free.
- **Amazon GuardDuty:** `$0.50–$4.00/million events analyzed`, varies by data source. A typical production account: `$50–150/month` for GuardDuty findings.
- **VPC Flow Logs:** `$0.50/GB` for logs stored in CloudWatch Logs. If enabled for all traffic (accept and reject), a high-traffic application generates gigabytes of flow logs daily. Store in S3 (`$0.023/GB`) instead of CloudWatch Logs for 95% cost reduction.
- **Systems Manager Session Manager:** Free for basic sessions; `$0.00007/minute` for advanced instances.

Model these costs explicitly in your migration estimate. They are real, recurring, and often omitted.

## Edge Cases in Migration Cost Modeling

### Partial Migrations

Not everything migrates at the same time. A partial migration where 30% of the application runs on AWS and 70% on-premises means full costs for both environments, plus cross-environment networking costs: VPN (`$0.05/hour + $0.10/GB`) or Direct Connect (circuit cost). Model the hybrid state explicitly, including the networking overhead.

### Legacy License Portability

SQL Server on EC2 using BYOL requires Software Assurance. If your SQL Server licenses do not include SA, you cannot use BYOL on EC2. Options: license-included EC2 (more expensive), RDS SQL Server (managed, license included, but cannot bring your own), or migrate to Aurora PostgreSQL or MySQL (requires application changes). The license portability analysis should happen before the migration estimate, not during.

Oracle licenses have additional complexity: Oracle has an audit clause that counts EC2 vCPUs differently than physical cores. A rule of thumb used by Oracle for license counting on AWS: all vCPUs in the account potentially count, not just the ones running Oracle. Engage an Oracle license specialist before migrating Oracle workloads to EC2.

### DMS Unsupported Data Types

DMS does not support all data types in all databases. PostgreSQL array types, spatial data, and some custom types require manual migration or custom transformation rules. Discovering unsupported data types after starting the DMS task adds days of rework to the migration timeline. Run DMS Schema Conversion Tool before the migration estimate to identify unsupported types.

For the strategic framework on choosing your migration approach, see our guide on [AWS migration strategy](/blog/aws-migration-strategy-choose-right-approach/). For the data transfer cost implications that extend beyond the migration itself, see [AWS data transfer costs for startups](/blog/aws-data-transfer-costs-startups/). For a comprehensive cost governance approach to manage costs after migration is complete, see [FinOps on AWS: Complete Guide to Cloud Cost Governance](/blog/finops-on-aws-complete-guide-cloud-cost-governance/). For a full engagement covering assessment, execution, and post-migration optimization, see [AWS Cloud Migration Services](/services/aws-migration/).

## FAQ

### What are the most commonly missed costs in AWS migration projects?
Four costs are consistently absent from migration estimates: (1) Parallel run period — running old and new environments simultaneously for validation, typically 2–8 weeks at full production cost for both. A $5,000/month new AWS environment × 4-week parallel run = $5,000 extra before you see any savings. (2) Data transfer during migration — moving 1 TB from a data center to AWS via internet costs nothing in AWS ingress but potentially $200–500 in data center egress fees depending on your provider contract. Ongoing egress from AWS post-migration: $0.09/GB for the first 10 TB/month. (3) License changes — Windows Server and SQL Server bring-your-own-license (BYOL) has different rules on EC2 vs on-premises; some licenses require dedicated hosts. (4) CloudWatch, CloudTrail, Config costs — enabled by default at account creation, generating ongoing charges that are invisible until the first bill.

### How long should you run parallel environments during migration and what does it cost?
The parallel run period is determined by your validation requirements, not a fixed timeline. Minimum: 1 week to validate basic functionality. Realistic for production migrations: 2–4 weeks to observe behavior across business cycles (weekly reports, monthly invoicing, peak traffic events). Worst case (regulated industries): 8–12 weeks for full audit and compliance validation. Cost model: $C_new/week × parallel_weeks + $C_old_daily/week × parallel_weeks, where C_new is the new AWS environment cost and C_old is the old environment variable cost during that period. Budget the full parallel cost as a migration line item, not as waste — it is the cost of validating that your migration is safe before cutting over.

### When does AWS Snowball save money over internet data transfer for migrations?
AWS Snowball Edge (80 TB usable) costs $300/device + $15/day usage. Compared to internet transfer at 1 Gbps (10.8 TB/day): 1 TB via internet takes ~2.5 hours; 80 TB takes 8+ days. Snowball transfers 80 TB in ~1 day of loading + 2–3 days shipping. Cost comparison for 80 TB: Snowball = $300 + $30 (5 days) = $330 + shipping ~$100 = ~$430. Internet transfer: 8 days × cost of transit (your DC bandwidth, typically free within monthly commit but delays other workloads). AWS does not charge for ingress. The decision point: Snowball is cost-effective when the migration window matters (Snowball is faster for large datasets), when internet bandwidth is constrained, or when your colocation/hosting provider charges for egress at a rate that exceeds $430/80TB.

### How do you right-size instances during migration without underprovisioning for peak load?
Right-sizing during migration requires performance baseline data from the current environment before migration. Collect: CPU at p50 and p95 over 30 days, memory at p95 (requires agent in most on-premises environments), disk I/O at peak, and network throughput at peak. Use AWS Migration Evaluator or CloudWatch Agent on source servers to collect standardized metrics. Map current utilization to AWS instance families: CPU-bound → c7g/c6i, memory-bound → r7g/r6i, balanced → m7g/m6i. Add 30% headroom above p95 for initial sizing. Plan to right-size again after 30 days of running on AWS — initial migration instances are typically 20–30% oversized, representing the first cost optimization opportunity post-migration.

---

*Source: https://www.factualminds.com/blog/aws-migration-without-cost-surprises/*
