How to Migrate to AWS Without Cost Surprises
Quick summary: 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.

Table of Contents
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/hourvs$0.19/hourfor 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.
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–510total) - 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:
$0for virtual interfaces, but requires cross-connect at a colocation ($50–200/month port fee) - Data transfer:
$0.02/GB(vs$0.09/GBinternet 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/hourdepending on instance class (dms.r5.large = $0.26/hour = $187/month) - Storage:
$0.023/GB/monthfor 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 Replication Task Terraform
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/monthcurrent 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,093you 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.
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:
# 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:
{
"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:
- Collect p50 and p95 CPU and memory from baseline metrics over 30 days
- Map to AWS instance families:
- CPU-bound (p95 CPU > 60%, memory < 60%):
c7g(ARM, 20% cheaper) orc6i(x86) - Memory-bound (memory p95 > 70%):
r7gorr6i - Balanced:
m7gorm6i
- CPU-bound (p95 CPU > 60%, memory < 60%):
- Size to p95 utilization + 30% headroom (accounts for traffic growth during validation period)
- 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/monthfor GuardDuty findings. - VPC Flow Logs:
$0.50/GBfor 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/minutefor 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. For the data transfer cost implications that extend beyond the migration itself, see AWS data transfer costs for startups. For a comprehensive cost governance approach to manage costs after migration is complete, see FinOps on AWS: Complete Guide to Cloud Cost Governance.
AWS Cloud Architect & AI Expert
AWS-certified cloud architect and AI expert with deep expertise in cloud migrations, cost optimization, and generative AI on AWS.



