Containerizing a legacy application can make its deployment more repeatable, but a container does not repair unsupported software, remove dependencies, or make local state durable. Start with the application’s real behavior and constraints, then choose the smallest migration that meets the reason you are moving it.
This guide covers seven practical strategies: assess target fit and choose a pilot; keep the existing deployment unit for the first move; adapt the application and split components only where there is a clear reason; plan data and persistent storage separately; make image builds and releases repeatable; secure the image, workload identity, and network; and measure the migrated service before tuning it. You can apply them in stages; not every application needs a rewrite or a microservices design.
Assess the application before choosing a container target
Build a short, evidence-based inventory before writing a Dockerfile. Record the application and operating-system versions, build and startup steps, scheduled jobs, databases, shared files, external services, identity needs, network paths, licenses, and support dates. Ask application owners about infrequent jobs, month-end work, maintenance windows, and recovery procedures that may be missing from diagrams.
Compare those facts with business requirements: availability, response time, recovery objectives, data handling, compliance controls, support ownership, and the reason for migrating. A workload that depends on an unsupported operating system feature, a host-level driver, or a shared disk may need a different target or a targeted change before it can run safely in a container.
Map the current runtime and dependencies
List the processes that start together and the resources they use. Trace calls to databases, file shares, queues, identity providers, license servers, and third-party APIs. Confirm important dependencies with owners and observed logs or network data; a quiet test period does not prove that an infrequent job has no dependency.
For each candidate, note the source version, the evidence you used, what remains unknown, and how the team will test the important workflows after the move. If you are still analyzing an unfamiliar codebase, our guide to analyzing legacy code for an AWS migration covers source review, runtime observations, dependency mapping, and target-fit evidence.
For a larger estate, use portfolio analysis to decide which applications deserve detailed assessment and which systems need to move together. The AWS application portfolio analysis guide explains how to combine application ownership, dependencies, business context, and migration sequencing.
1. Assess target fit and choose a pilot
Choose a bounded pilot
Pick a workload that is important enough to exercise the real build, security, data, and operating process, yet bounded enough that the team can understand its dependencies and recover if a test fails. “Simple” or “low traffic” alone does not mean low business risk. Record the workflows, data checks, service goals, and rollback conditions that define an acceptable pilot.
Choose orchestration and compute separately
Amazon ECS is AWS's container orchestration service; Amazon EKS runs Kubernetes on AWS. Fargate is a serverless compute option for ECS or EKS, while ECS can use EC2-backed capacity that your team manages or Amazon ECS Managed Instances, where AWS manages the instances. Choose ECS or EKS based on the orchestration API and operating model the application requires; choose among Fargate, EC2-backed capacity, and ECS Managed Instances based on host access, compatibility, resources, and operating needs. Fargate abstracts the hosts, so it is a poor fit when an application requires unsupported host access or a platform feature Fargate does not provide. Compare the current ECS capacity options and EKS on Fargate against the actual application requirements.
With EKS on Fargate, a Pod must match a Fargate profile; DaemonSets, privileged Pods, HostNetwork, and HostPort are not supported. If the workload depends on one of these features or a host-level agent, verify whether it can be adapted or needs EC2-backed nodes. See the EKS Fargate limitations.
For Windows workloads, verify the supported Windows Server release and image-to-host compatibility. On ECS for EC2, a Windows container image must match the host's Windows Server version; Windows images cannot run on Linux hosts. ECS Fargate supports Windows Server 2019 and 2022 but has feature limits, including no EFS, EBS, FSx, or gMSA support for Windows tasks. A requirement for one of those features may change the compute choice. Check AWS's Windows container compatibility guidance and Fargate Windows limitations.
Check tool and product status
AWS says App2Container stopped accepting new customers on November 7, 2025; existing customers can continue using it to finish in-flight projects. AWS recommends AWS Transform for new .NET modernization projects. That recommendation does not make Transform a drop-in containerization pipeline for every application: verify that its current feature supports your language, framework, and deployment path before choosing it. See the AWS App2Container availability notice, its getting-started guide, and the AWS Transform overview.
Automated assessment or containerization output is a starting point for review. Confirm the generated build files, dependencies, permissions, image contents, deployment configuration, and runtime behavior before treating them as production-ready.
2. Keep the existing deployment unit for the first move
What a minimal-change move can achieve
When an application is compatible with a supported container base image, packaging its existing deployable unit can help the team test a new runtime without first redesigning the application. Keep components that already deploy and scale together in the same initial boundary. This is a migration step, not proof that the app has become cloud-native or that it will cost less or run faster.
This approach is useful when the migration goal is to move a supported runtime, establish a repeatable deployment, or learn about operational behavior before making broader changes. It is a poor fit when the app relies on unsupported kernel features, a physical device, an incompatible operating system, or unexamined local state.
Build and validate one deployable image
- Reproduce the existing build and capture the runtime and dependency versions.
- Create an image from a supported base image and include only required application files and dependencies.
- Build the same image in a repeatable environment and publish it to a registry such as Amazon ECR.
- Run it in a non-production environment with the real configuration, secrets, network paths, and representative data dependencies.
- Compare key workflows and service measures with the agreed baseline before routing production traffic.
Keep image identity traceable through a version or digest. Amazon ECR supports immutable tags, which can prevent an existing tag from being overwritten; deployment by digest also identifies the exact image. Do not bake credentials or environment-specific configuration into the image. Pin dependencies and base-image versions deliberately, then rebuild and test when patches are needed. See the ECR guidance for immutable tags.
Set expectations for a minimal-change move
A small initial change can reduce the amount of application behavior changed at once, but the old assumptions remain. A process that writes important files only to its container filesystem, expects an administrator to edit a running host, or never handles a restart still needs a migration plan. Identify those assumptions during the pilot instead of treating a successful image build as completion.
3. Adapt the application and its boundaries in small steps
Make processes replaceable and configuration explicit
Containers are commonly stopped, replaced, and restarted by the scheduler. Make startup deterministic, move environment-specific settings outside the image, and ensure the main process handles termination cleanly. On ECS, define meaningful health checks for the service; make sure a health check tests whether the application can serve its intended work rather than merely whether a process exists.
Use environment variables for non-secret settings where appropriate. Store credentials in a managed secret store. When an ECS task definition references a secret, scope the task execution role to retrieve it; when the application calls AWS APIs, scope its task role to those calls. Avoid copying production secrets into build arguments, image layers, source control, or logs. See how to reference Secrets Manager data in an ECS task definition.
Keep logs, files, and operating assumptions visible
- Keep the existing logging library if it is useful, and configure its appender to write structured logs to standard output or standard error. Replacing a logger with
print calls is not required.
- Make writable paths explicit. Separate temporary scratch files from uploads, reports, or other data that must survive replacement.
- Use a supported runtime and base image, remove build tools and packages that are not needed at runtime, and run without elevated privileges where the application allows it.
- Test startup, shutdown, health checks, retries, and error handling under the same resource limits planned for the target.
Change one operational assumption at a time
Use characterization tests or representative workflows to protect behavior before changing configuration, logging, file handling, or process startup. After each small change, repeat the affected checks and record the result. If the old system has no reliable tests, start with the business-critical paths and data reconciliation that owners can verify.
Keep the modernization scope proportional
Targeted changes can make a workload easier to deploy and operate while preserving much of its code. They can also reveal hidden dependencies and require more testing than expected. Keep a list of unresolved risks; do not expand a container pilot into a full rewrite unless the business goal and evidence justify that work.
Split components only at useful boundaries
Containerization does not require microservices. A monolith can run as one containerized service if it can be built, started, scaled, secured, and monitored as a unit. Consider extracting a component when it has a clear boundary and a separate need, such as independent scaling, release cadence, ownership, fault handling, or technology lifecycle.
Before splitting, map the component’s data ownership, synchronous calls, queues, and failure behavior. A new service boundary adds network failure modes, deployment coordination, observability work, and security policy. A diagram that divides a codebase into boxes is not enough evidence that each box should become an independently operated service.
Containerize existing deployment boundaries first
For an initial migration, keep processes together when they must be released or scaled together and when that arrangement fits the target. If a component is extracted, give it an explicit build, deployment, health check, data contract, and owner. Change one boundary at a time so the team can identify which change caused a behavior difference.
Design service communication deliberately
Use the network path and protocol that match the application: for example, a private service endpoint, a load balancer, or a queue for asynchronous work. Define authentication, authorization, timeouts, retries, and what happens when the destination is unavailable. In ECS, task networking and security groups can control which resources may connect to a task; in EKS, apply the Kubernetes and VPC networking controls required by the chosen cluster design.
Example: a legacy order system
If an order-processing application contains a web front end, order workflow, and database calls, first containerize its current deployable unit and verify orders end to end. Extract the web tier or a background worker only if independent scaling, releases, or ownership make the extra network and operational boundary worthwhile. Keep transaction and data consistency behavior explicit throughout the change.
What independent deployment changes
Separate services can be deployed or scaled independently when their interfaces and data ownership support that model. They do not automatically isolate failures or make releases faster: shared databases, synchronous chains, and coupled schemas can preserve the same dependencies across new service boundaries.
Preserve a path to operate each service
- Assign a clear owner and an operational runbook.
- Version and test the interface between components.
- Set timeouts and bounded retries, and avoid retrying non-idempotent operations blindly.
- Trace requests across service boundaries and alert on user-visible failures.
- Keep the old and new paths available for the agreed validation and rollback period.
4. Plan databases and persistent files separately
Choose where durable state belongs
Do not treat “move the database into a container” as the default strategy. Database engines have separate needs for durable storage, backup and restore, replication, upgrades, licensing, and recovery. Compare a managed database service, a database on infrastructure you operate, and a containerized database against those workload requirements and the team’s operational capability.
Likewise, inventory local files, uploads, reports, batch output, and user sessions. The container image and task-local writable storage should not be the only copy of data the application must retain. ECS storage choices and operating-system support differ: for example, AWS documents persistent EFS volumes for Linux ECS tasks, while FSx for Windows File Server task volumes require ECS on EC2. Windows containers on ECS Fargate have additional storage limitations. Check the current ECS storage options and Windows-on-Fargate considerations for the exact combination you need.
Compare database targets by requirements
Managed relational services such as Amazon RDS can reduce the database infrastructure the application team operates, but they still require compatibility, capacity, security, backup, and recovery decisions. A containerized database may make sense when a required engine or extension is unavailable as a managed service, or when the team deliberately accepts responsibility for operating it. A document or key-value database is a data-model change, not a drop-in replacement for a relational database.
Test the data move and the application together
- Record source and target engines, versions, data volume, write rate, consistency needs, and the allowed cutover window.
- Choose a migration method that supports the source and target, and decide how writes will be handled during the copy.
- Run a test migration and reconcile records, files, counts, and application-level queries.
- Exercise backups and restores in the target, then test application behavior with the moved data.
- Schedule cutover only after the data owner approves the reconciliation and the team has a rollback plan for both application and data changes.
AWS Database Migration Service supports migration patterns, including ongoing replication for some source and target combinations. It does not remove the need to check engine support, schema behavior, replication lag, validation coverage, or the application's consistency requirements. Check the current AWS DMS documentation and keep a tested source-to-target plan with the migration record.
5. Make image builds and releases repeatable
Build once, test, then promote the same image
Automate the steps that should not depend on a person remembering them: retrieve source, compile, run unit and integration checks, build the image, scan it, publish it to a registry, and deploy the identified image version. Promote the same tested image through environments instead of rebuilding a different artifact for production. Keep image tags immutable or deploy by digest so a rollback points to the image that was actually tested.
Store environment-specific configuration outside the image, and review changes to both application code and infrastructure. Scope the pipeline's permissions to the actions it needs, and retain build and deployment records that connect source revision, image digest, tests, and release.
Amazon ECS and Amazon EKS expose different deployment models; select one that fits the service and your team's operating experience. ECS services support deployment strategies such as rolling and blue/green, subject to their documented prerequisites. With a load balancer, blue/green can validate a new revision before sending production traffic and can support rollback when configured with health checks and alarms. See AWS's ECS deployment strategies and deployment failure detection.
Do not assume a rollback also reverses a database schema or data change. Prefer compatible, staged schema changes and test the old and new application versions against the transition state when both may run during rollout.
Automate checks and rehearse the return path
- Test the health endpoint and business-critical workflows before and after deployment.
- Monitor errors, latency, throughput, resource pressure, and relevant dependency health during the rollout.
- Define who or what stops the release, the signal that triggers rollback, and the known-good application image.
- Practice rollback and data recovery in a representative environment; record steps and recovery time.
Use a gradual or parallel traffic shift when the service and target support it and the extra capacity is acceptable. A rollout strategy reduces the blast radius only when the health signals detect the failure modes that matter to this application.
6. Secure the image, workload identity, and network
Keep the image small, known, and current
Build from a trusted, supported base image, include only runtime dependencies, and rebuild when the base image or packages need security fixes. Scan images for vulnerabilities before release and choose a process to review findings, prioritize applicable issues, and replace vulnerable deployed images. Amazon ECR supports basic scanning and enhanced scanning through Amazon Inspector; enhanced scanning can cover operating-system and programming-language package vulnerabilities. See the Amazon ECR image-scanning guide.
Run the application as a non-root user where it is compatible, avoid privileged mode unless there is a reviewed requirement, and do not store credentials in image layers. Image scanning is one control; it does not establish that the application, configuration, or deployment is secure.
Separate AWS roles and restrict traffic
For ECS, distinguish the task execution role from the task role. The execution role lets the ECS agent perform actions such as pulling an ECR image or sending logs; the task role grants the application code access to AWS APIs. Give each only the required permissions. AWS describes these separate roles in its ECS IAM role guidance.
Use private subnets where appropriate, encrypt network traffic that requires protection, and allow only the application paths the workload needs. With ECS, the awsvpc network mode gives tasks their own network interface and allows security groups to control task traffic; it is required for Fargate. Review both inbound and outbound paths between tasks, load balancers, databases, and external services. See AWS's ECS network security recommendations.
Keep evidence and response in the operating plan
Define the required encryption, access, audit, vulnerability, and incident-response controls with the responsible security owner. Use CloudTrail for AWS API activity and CloudWatch for operational logs and metrics; configure coverage, retention, and alert routing to match the workload. Keep a record of accepted findings and the person accountable for each exception.
7. Measure the migrated service before tuning it
Set initial CPU, memory, and storage from evidence
Use observed peak and steady-state behavior to choose starting task or pod resources, then test under representative load. Watch memory pressure, CPU throttling, restarts, queue depth, startup time, and temporary disk use. Leave headroom for expected bursts and deployment overlap, and verify that logs and temporary files will not fill available storage.
Resource limits and units differ between ECS, EKS, Fargate, and EC2. Apply the settings for the selected platform and test the actual task definition or pod specification; a number copied from a virtual machine is not automatically the right container allocation.
Scale only after checking application state
Autoscaling can add or remove service capacity in response to an appropriate signal, but more copies are safe only when the application and its dependencies support them. Move session state out of local memory when multiple replicas must serve the same user, check database connection limits, and verify scheduled jobs or singleton work will not run twice. Set scaling limits and alarms, then test scale-out and scale-in behavior.
Compare service behavior and cost over time
Collect application logs, service metrics, traces where useful, and alarms tied to the workflows users rely on. Compare the migrated workload with its pre-migration baseline using the same representative paths and service goals. Tune one variable at a time and confirm that any change improves the measured outcome without weakening reliability or security. Containers alone do not establish a performance or cost improvement.
A migration sequence the team can verify
The seven strategies at a glance
- Assess target fit, distinguish orchestration from compute, and choose a bounded pilot.
- Keep the existing deployment unit for the first move.
- Adapt the application lifecycle and split components only where the boundary has a clear purpose.
- Plan databases, files, sessions, backups, and data cutover separately.
- Build, test, and release an identified image through a repeatable pipeline.
- Secure the image, workload identity, and network.
- Measure the migrated service before tuning resources or scaling it.
Treat containers as a deployment choice
Containers can package an application and its runtime dependencies into a repeatable artifact. They do not by themselves create portability, resilience, better isolation, faster releases, or lower costs. Those outcomes depend on image maintenance, data design, IAM and network controls, deployment practices, and the team's ability to operate the target.
Use the pilot to decide what changes next
Write down the pilot's acceptance and rollback conditions, run it with representative workflows and data, and review the results with application, operations, security, and data owners. Continue with a minimal-change move when it meets the business goal; schedule targeted remediation or further modernization where evidence shows a specific need.