Services
Custom software developmentUmbraco developmentWeb developmentCRM developmentERP developmentAI & LLM integration.NET development & modernizationE-commerce developmentIntegrations & automationCloud & DevOpsMaintenance, support & project rescueDedicated teams & white-label
Our Work
Case studiesProducts
About Blog Contact
Cloud

Azure to AWS Migration Without Downtime: Moving a .NET and Umbraco Site to ECS Fargate

A practical plan for moving a high-traffic .NET or Umbraco site from Azure App Service to AWS (ECS Fargate, RDS for SQL Server, S3, ECR, ALB) while it stays online, with a worked example from Ben-Gurion University's ~80,000-page site.

Key takeaways

  • Containerize first: a Linux container image built from the current code is the unit you test, ship to ECR and run on ECS Fargate.
  • The database decides your cutover plan; for Azure SQL Database, plan a full load plus a short write freeze rather than counting on continuous replication.
  • Route 53 weighted records (weights 0 to 255) let you move traffic in steps and send it back by changing one number, as long as DNS TTLs are short.
  • Umbraco on several containers needs shared media storage, shared Data Protection keys, a distributed cache for session and a clear scheduling server role.
  • Keep the Azure stack running and unchanged until the AWS stack has carried full traffic through a normal busy period.
  • Compare costs only after rightsizing both sides; licensing, data transfer and engineering time often matter more than compute prices.

An Azure to AWS migration without downtime works by building the AWS environment next to the running Azure one, keeping data in sync, and moving traffic over in steps you can reverse. For a .NET or Umbraco site that means: package the app as a container, run it on Amazon ECS with Fargate behind an Application Load Balancer, move the SQL Server database to Amazon RDS, copy media to S3, then use Route 53 weighted DNS records to shift traffic gradually. The Azure stack stays live until the AWS stack has proven itself under real load.

The hard parts are not the containers. They are the database cutover, shared state between instances (media, session, cache, encryption keys) and having a rollback you have actually tested. This guide walks through each step, marks where we are giving general guidance, and uses Ben-Gurion University’s main website as a worked example. Our cloud and DevOps team moved that site from Azure to AWS while it stayed online.

The worked example: Ben-Gurion University

The Ben-Gurion University case study describes the university’s main Umbraco site, which holds about 80,000 pages, is edited by around 1,000 people and sees traffic spikes of around 300 requests per second. It also hosts an Organizational Units subsystem where each unit runs its own content, plus daily imports of 15,000+ staff records from university systems.

The requirement was that the live public site move from Azure to AWS without any interruption. What the case records about the implementation:

  • The application was containerized and moved to Amazon ECS on Fargate, with Amazon RDS, Amazon S3, Amazon ECR and a load balancer.
  • A Windows EC2 bastion runs the import services. The case lists a daily staff API import and a CRIS research publications import.
  • Traffic shifted to the new setup gradually.
  • The migration finished with zero interruption to the public site.

Everything else below is general guidance for this kind of move, not a description of the university’s specific configuration.

Target architecture on AWS

Concern On Azure (typical) On AWS
Compute App Service plan ECS service on Fargate tasks
Images Built during deploy, or Azure Container Registry Amazon ECR
Traffic entry App Service front end Application Load Balancer
Database Azure SQL Database or SQL Server Amazon RDS for SQL Server
Media and files App Service file system or Azure Blob Storage Amazon S3
DNS Any provider Route 53 (weighted records)
Background jobs and imports WebJobs or a separate app Separate ECS service or EC2 (BGU used a Windows EC2 bastion)

Fargate means you run containers without managing the servers under them. Plan at least two tasks across two Availability Zones for a public site, and put a separate, small service or instance in charge of scheduled work.

Step 1: Containerize the application

App Service can run code without a container, so many .NET sites have never been packaged as one. Do this first, while still on Azure, because the image is what you will test, scan and promote.

  • On modern .NET, the SDK can build an image without a Dockerfile: dotnet publish --os linux --arch x64 /t:PublishContainer (requires the .NET 8 SDK or later). A Dockerfile is still reasonable if you need extra OS packages.
  • Remove anything that assumes App Service: local file writes for media, WEBSITE_* environment variables, App Service authentication, in-process session.
  • Move configuration to environment variables and a secrets store (AWS Secrets Manager or SSM Parameter Store).
  • Add a health endpoint that checks the app is ready, not just alive. The ALB target group and ECS use it to decide when a task can take traffic.
  • Push images to ECR from CI with immutable tags (a commit SHA, not latest).

If the site still runs on .NET Framework, the container route needs Windows containers or a runtime migration first. See our guide on moving .NET Framework apps to .NET 10.

Step 2: Make the app safe to run on several containers

This is where most Umbraco and ASP.NET Core sites need code or configuration changes. Umbraco’s load balancing documentation lists the requirements:

  • Server roles. One server acts as SchedulingPublisher (scheduled publishing and background tasks), the others as Subscriber. Umbraco recommends setting the roles explicitly rather than relying on automatic election. A common pattern is one backoffice service and a scaled public front-end service.
  • Data Protection keys. All instances must share the same keys, or you get validation and encryption errors and users get signed out. Microsoft documents key storage providers for Redis and EF Core (a database table), among others; on AWS, the EF Core provider pointing at RDS is a simple choice.
  • Session and TempData. Umbraco’s docs require a distributed cache such as DistributedSqlServerCache when load balancing. Do not rely on ALB sticky sessions as the only fix; containers get replaced during deploys and scaling.
  • Examine and Lucene indexes. Umbraco’s file system replication guidance explains that Lucene has problems on remote file shares and offers TempFileSystemDirectoryFactory and SyncedTempFileSystemDirectoryFactory for keeping indexes on local storage. On Fargate, each task builds and keeps its own index on local ephemeral storage. Measure how long a cold index rebuild takes for your content volume, because it affects how fast new tasks become ready.
  • Media. Containers are disposable, so media cannot live on the container file system (see Step 4).

Search and caching deserve extra attention on large sites. On the BGU site, Lucene powers global search plus more than 10 dedicated search pages, and a custom page-level cache records which pages depend on which content so only affected pages are invalidated. Behavior like that has to be checked on the new infrastructure under load, not assumed.

Step 3: Choose a database migration path

The database is what decides your cutover plan. Pick the path based on what your source is.

Option A: Native backup and restore (source is SQL Server you control)

Amazon RDS supports restoring full .bak files from S3, and AWS calls this “usually the fastest way” to move a SQL Server database. Limits worth knowing: the S3 bucket must be in the same Region, files are limited to 5 TB each (use multifile backups beyond that), you can’t restore a database with a FILESTREAM filegroup, and on Multi-AZ instances only databases in the full recovery model can be restored natively. AWS’s own recommendation is to use native restore if the database can be offline during backup, copy and restore, and AWS DMS if it can’t.

Option B: BACPAC export (source is Azure SQL Database)

Azure SQL Database does not give you .bak files. Microsoft’s supported export format is a BACPAC, which can be imported into a SQL Server instance. For a consistent export, there must be no writes during the export, or you export from a database copy. Blob storage exports are capped at 200 GB, and Microsoft recommends SqlPackage for larger databases. In practice this means a write freeze for the duration of export plus import, so test the timing.

Option C: AWS DMS

AWS DMS lists Azure SQL Database as a supported source and can do a full load followed by ongoing replication (CDC) for many SQL Server sources. Two documented details matter for Azure:

  1. The DMS SQL Server limitations say it “doesn’t support replication from databases with accelerated database recovery (ADR) turned on.”
  2. Microsoft states that ADR is always enabled in Azure SQL Database and Azure SQL Managed Instance.

Read together, we would not plan a cutover that depends on continuous CDC from Azure SQL without proving it first in a proof of concept. Also note that DMS does not migrate the identity property of columns, does not capture TRUNCATE, and does not support temporal tables. The usual pattern is to create the schema on RDS first (from a schema script or BACPAC) and use DMS for data only.

What we recommend in general

For a CMS-driven site, content changes come from editors, not from the public. That makes a short editorial freeze the simplest safe cutover: public traffic keeps flowing, editors pause for the final sync window, and the backoffice comes back up on AWS. Rehearse the full sequence on a copy at least once, and time it.

Step 4: Move media to S3

Copy existing media to S3 early, then run a final sync during the freeze. Umbraco supports replacing the media file system with a custom IFileSystem implementation; Umbraco’s own maintained provider in the Umbraco.StorageProviders project targets Azure Blob Storage. For S3 you either use a community provider that supports your Umbraco version or write a small implementation. Umbraco’s docs note that a custom media file system must also provide an IFileProviderFactory so media is served and image processing still works.

Two details to test: image cropping and resizing on media from S3, and the cache headers on media responses. Put a CDN in front of S3 or the site if you serve heavy images.

Step 5: Shift traffic in stages with Route 53

Route 53 weighted routing lets you create several records with the same name and type and assign each a weight from 0 to 255. Each record gets traffic in proportion to its weight divided by the total. Setting a weight to 0 stops traffic to that resource. The example AWS gives is weights of 1 and 255, which sends 1/256 of traffic to the first resource.

Illustrative plan (adapt it to your traffic and risk):

Stage Azure weight AWS weight Approx. share to AWS Hold for
Smoke 255 1 ~0.4% Until errors and latency are clean
Canary 230 25 ~10% One busy period
Half 128 128 50% One to two days
Most 25 230 ~90% One busy period
Full 0 255 100% Until you decommission Azure

Practical notes from the Route 53 docs and general experience:

  • Lower TTLs days in advance. AWS suggests a short TTL such as 300 seconds when changing records in use, and 60 seconds or less when health checks are attached. All records in a weighted group share one TTL, and if alias records point to a load balancer, AWS says to use 60 seconds for the non-alias records too.
  • DNS is per resolver, not per user. Resolvers cache answers, so actual traffic shares will not match the weights exactly. Watch the real request counts on both sides.
  • Health checks. If you attach health checks to all records in the group, Route 53 stops returning an unhealthy endpoint, which gives you automatic fallback.
  • Single writer. While both stacks serve public traffic, both must read from the same source of truth. Either point both at one database temporarily, or keep the AWS stack read-only until the database cutover. Do not let both sides write to different databases.

Step 6: Load test before the first real user

Load test the AWS stack with production-like data before any weight goes above zero:

  • Replay a realistic mix of URLs from your logs, not just the home page.
  • Test at and above your known peak. For the BGU site the case records spikes of around 300 requests per second.
  • Include cold starts: scale out during the test and watch how long new tasks take to become healthy, including index builds and cache warm-up.
  • Watch RDS CPU, IOPS and connection counts, not just response times.
  • Configure ECS service auto scaling and confirm it triggers before latency degrades.

Step 7: Write the rollback plan before you need it

A rollback you have not rehearsed is a hope. The basics:

  1. Keep Azure running and unchanged until AWS has carried full traffic through a normal busy period.
  2. Rollback for public traffic is a weight change: set AWS to 0 and Azure back to 255. With short TTLs, most traffic returns within minutes.
  3. Rollback after the database cutover is harder, because edits made in AWS exist only in RDS. Decide in advance: either export those changes back, or accept a short freeze while you move the data back. Keep the cutover-to-decision window short.
  4. Define triggers in advance: error rate, p95 latency, failed health checks, failed editor workflows. Agree who can call a rollback.
  5. After go-live, use ECS blue/green deployments for application releases. AWS documents a bake time during which the old revision keeps running and can take traffic back.

Cost comparison caveats

Cost is a common reason to move and a common source of disappointment. General points:

  • SQL Server licensing is often the largest line. RDS for SQL Server offers License Included pricing for Enterprise, Standard, Web and Express editions, and AWS describes a Bring Your Own Media option that relies on License Mobility through Software Assurance. Compare editions and sizes, not just instance prices.
  • Data transfer between Availability Zones, out to the internet and between clouds during the migration all cost money.
  • Fixed costs such as load balancers, NAT gateways and a bastion host add up on small sites.
  • Rightsize both sides before comparing. An oversized App Service plan compared to a tuned Fargate service tells you nothing.
  • Count the engineering time for the migration and for running the new platform.

When not to move

A migration is a large change with real risk. It may not be worth it when:

  • The main driver is a discount that a renegotiated Azure agreement could match.
  • The site depends on Azure-specific services (Azure AD B2C flows, Service Bus, Functions) that would each need rework.
  • Nobody on the team will own AWS operations after the move.
  • The application still needs deeper work (an unsupported runtime or CMS version). Fix that first; moving clouds and runtimes at once multiplies risk.

Migration checklist

  • Container image builds in CI and runs locally with production-like config
  • Health endpoint, structured logs and error tracking in place
  • Server roles, Data Protection keys, distributed session and index settings configured
  • Media copied to S3, image processing verified
  • Database path chosen and rehearsed with timings
  • Load test passed above known peak, auto scaling verified
  • DNS TTLs lowered days in advance, weighted records created at weight 0
  • Rollback triggers, owners and steps written down
  • Editor freeze window agreed and communicated
  • Azure kept warm until sign-off

The same AWS foundation can support more than one site. The university’s login-only student and staff portal, described in the BGU portal case study, also runs on AWS Fargate with Amazon RDS, S3 and a load balancer. If you are planning a similar move for an Umbraco site, our Umbraco development and .NET development teams can help with the application-side changes.

OSKI Engineering TeamSenior .NET, Umbraco and AI engineers

Written by the engineers who build and support OSKI client projects, reviewed by our CTO before publishing.

FAQ

Questions readers ask

Anything missing? Send us your question and an engineer will answer, usually within one business day.

Contact us
Can you migrate from Azure to AWS with zero downtime?+

Yes, for most web applications. Run the new AWS stack in parallel, sync the data, and shift traffic gradually with weighted DNS while the Azure stack keeps serving. The database cutover is the only moment that needs careful coordination, usually a short freeze on content edits rather than a public outage.

How do I migrate an Azure SQL database to Amazon RDS for SQL Server?+

Azure SQL Database does not produce native .bak files, so the usual paths are a BACPAC export imported with SqlPackage, or AWS DMS, which lists Azure SQL Database as a supported source. RDS native backup and restore from S3 works when your source is a SQL Server you control and can produce .bak files.

Does AWS DMS support ongoing replication from Azure SQL Database?+

Be careful here. AWS DMS lists Azure SQL Database as a source, but its SQL Server limitations say it does not support replication from databases with accelerated database recovery turned on, and Microsoft states ADR is always enabled in Azure SQL Database. Validate CDC in a proof of concept, and plan for a full load with a write freeze if it does not work.

How does Route 53 weighted routing work for a migration?+

You create records with the same name and type, one per environment, each with a weight from 0 to 255. Route 53 answers queries in proportion to each record's weight divided by the total, so you can move traffic in steps and set a record's weight to 0 to stop sending traffic to it.

Is AWS cheaper than Azure for a .NET website?+

It depends on the workload, licensing and how well each side is sized. SQL Server licensing, data transfer, load balancers and support plans can outweigh compute differences. Compare rightsized, like-for-like architectures, and include the engineering cost of the move.

Can Umbraco run on AWS ECS Fargate?+

Yes. Ben-Gurion University's Umbraco site runs on ECS Fargate with Amazon RDS, S3 and ECR after OSKI moved it from Azure. Plan for load-balanced Umbraco configuration: server roles, shared Data Protection keys, distributed session storage and media on shared storage.

Start a project

Tell us what you need to ship. We come back with a plan and a price.

A spec, a Figma file or three paragraphs is enough to start.

  1. First reply · from an engineer, not a sales rep
  2. 30-min call · with Roman, our CTO, to clarify scope
  3. After the call · a written proposal with plan, team and price
Roman Oleksuk
Roman Oleksuk, CTOPrefer to talk? Pick a slot in Roman's calendar
We reply from a human mailbox. No newsletters. Your brief stays under NDA on request. We use your details only to reply; see our privacy policy.