Render to AWS Migration Walkthrough
Start with a written inventory of your Render infrastructure before touching AWS.

The impulse is to open a terminal and start running commands. Resist it. The first deliverable is a written inventory, not infrastructure.
Go through the Render dashboard systematically. List every web service, background worker, cron job, Postgres database, Redis instance, and environment variable. For each one, write down what it does, what it depends on, and what depends on it. This sounds tedious because it is. But it's the only way to know your migration sequence before you've committed to anything irreversible.
Once the inventory exists, the mapping follows naturally. Render Postgres becomes Amazon RDS for PostgreSQL. Redis becomes ElastiCache. File storage, if you've been relying on Render's ephemeral disk (and you probably have, even though you should have been using S3), moves to S3 properly this time. Cron jobs and scheduled tasks map to EventBridge Scheduler or Lambda. Web services and background workers become ECS Fargate tasks behind a load balancer.
Flag anything Render-specific that won't transfer automatically: the build commands configured in the dashboard, the health check paths Render pings, the auto-deploy hooks wired to your GitHub repository. None of these are blockers, but they all require deliberate translation. An auto-deploy hook doesn't evaporate when you leave Render; you have to reconstruct its behavior explicitly in your new CI/CD pipeline. Teams that skip this step often discover the gap during the cutover itself.
The output of this phase is a written map: current state on the left, target state on the right, dependencies noted between rows. That map also surfaces the migration sequence. Data stores move before compute, because a broken application pointing at a live database is recoverable. A live application pointing at a migrated database that missed rows is not. Compute moves before DNS. DNS is the last thing you touch, full stop.
Choosing the right AWS compute model for a team coming off Render
Most teams migrating from Render don't have a dedicated platform engineering function. They have developers who are capable, but whose primary orientation is product, not infrastructure. The compute decision has to account for that honestly, because the wrong choice here creates ongoing drag that compounds over months.
There are three realistic options, and the differences between them are meaningful.
ECS Fargate is the closest analog to Render's model. You define a container, specify CPU and memory, and AWS handles the underlying compute. No EC2 instances to patch, no node pools to manage, no late-night pages because a node drained itself. Autoscaling works on configurable thresholds; billing is per use. For most teams migrating from a PaaS background, Fargate is the correct default. ADPList, which moved from Render to AWS, chose ECS with Fargate over EC2 and EKS specifically because the operational overhead was substantially lower; teams with a similar profile and similar priorities may find that reasoning applicable to their own situation.
Elastic Beanstalk offers the most Render-like surface area. It manages deployments, scaling, and basic monitoring with minimal configuration. If the team's primary goal is to minimize operational change immediately, Beanstalk is a reasonable starting point. The tradeoff is that it abstracts away configuration you will eventually want to control, and unwinding that abstraction later is more disruptive than starting with Fargate in the first place. Some teams choose Beanstalk to buy time, then spend that time dealing with Beanstalk's limitations instead.
Lambda is appropriate for event-driven workloads and scheduled tasks, not for persistent web services. It complements Fargate; it doesn't replace it.
If the team wants to grow into infrastructure ownership incrementally, start with Fargate. If the shortest path to stability with the fewest new concepts is the priority, Beanstalk buys you that breathing room. Neither option requires you to become a Kubernetes operator, which is the fear that drives teams toward bad decisions in this moment.
Moving the database from Render to RDS with minimal downtime
The database migration is the highest-stakes phase of this entire process. Everything else is recoverable. A botched database migration, one where rows are missing, sequences are off, or the restore never finished cleanly, can set a team back weeks.
Locate the external connection string in Render's dashboard under the database's connection settings. Run pg_dump against it. For databases large enough that a sequential export would take meaningful time, use the directory format with the --jobs flag to parallelize the operation and reduce the export window.
Provision the RDS instance before you export anything. Place it inside a private VPC subnet with no public accessibility. A publicly accessible RDS instance introduces significant security risk, and it's one of those decisions that looks fine until it very suddenly doesn't.
For production databases where downtime must be minimized, AWS Database Migration Service handles continuous replication. You run DMS in the background while your application is still live on Render. When replication has caught up and row counts match, the cutover becomes a minutes-long switch rather than a multi-hour window where you're watching a progress bar and hoping.
Teams that skip rehearsal because they're confident in their tooling risk encountering schema edge cases that require manual intervention at the exact moment when manual intervention is most expensive. Restore the dump to a staging RDS instance first. Run your application against it. Run smoke tests. Verify row counts against the source. Only after that process has completed cleanly should you schedule the production migration. The staging rehearsal is where you find out what you didn't know you didn't know.
The RDS instance should be receiving verified application traffic in your staging environment before any production workload goes near it.
Containerizing the application and setting up ECS Fargate
If the application isn't already containerized, the Dockerfile is the first artifact. Mirror the Render build environment precisely: same runtime version, same base image family, same build command sequence. Render runs your build in a managed environment, and the Dockerfile is simply making that environment explicit and portable. The goal here is no surprises, not optimization.
Push the image to Amazon ECR. ECR becomes the canonical source for every deployment from this point forward. Your CI/CD pipeline builds the image, pushes it to ECR, and ECS pulls from ECR. Nothing should be reaching out to Docker Hub or any other registry in production.
The ECS task definition specifies CPU and memory allocation, the container port, the health check path, and environment variable configuration. Environment variables do not go into the task definition as plaintext, and they must never get baked into the image. They come from AWS Secrets Manager or Parameter Store, injected at container startup. This matters for a very practical reason: when an engineer leaves the team, you rotate their access, not six different API keys scattered across services.
Create an ECS service and place it behind an Application Load Balancer. The ALB handles HTTPS termination, distributes traffic across healthy container instances, and enforces the health check path you defined. Configure autoscaling on the service against CPU utilization or request count, with thresholds your team chose deliberately, not thresholds a platform vendor chose on your behalf.
The networking layout is simple in principle: the load balancer lives in public subnets and is internet-facing; the ECS tasks and RDS instance live in private subnets with no direct internet access. Security groups govern what can talk to what. This is the layer of control that a shared-tenant PaaS is structurally incapable of offering, and once you've operated with it, going back feels claustrophobic.
Infrastructure as Code from the start, not as an afterthought
The AWS console is seductive. You can provision a VPC, a security group, an RDS cluster, and an ECS service in an afternoon by clicking through the interface. The result is infrastructure that exists, but that nobody can reproduce, audit, or confidently modify six months later when the person who built it has moved on. That pattern has a name: ClickOps. It is a reliable way to create serious technical debt in the first week of a migration.
Every resource from the previous phases should exist in code before cutover. The VPC, subnets, route tables, RDS instance, ECS cluster, ALB, target groups, IAM roles, CloudWatch log groups: all of it. No exceptions.
The tool choice matters less than the commitment. Terraform is a common choice for teams that operate across multiple cloud providers. AWS CDK suits teams already working in TypeScript, Python, Java, Go, or C#, where infrastructure defined in a familiar language integrates naturally into the existing codebase. CloudFormation works well for teams that want to remain entirely within the AWS ecosystem and leverage native integrations with services like AWS Service Catalog.
IaC also makes the parallel-environment strategy tractable. Spin up a staging stack that is a verified structural replica of production. Validate the full migration path there. Promote to production once the staging environment has processed real application traffic cleanly. Without IaC, that parallel stack is either inaccurate or prohibitively time-consuming to maintain, and you end up testing against an environment that doesn't actually resemble what you're deploying into.
Security defaults belong in the templates, not in a post-deployment checklist. Private subnet placement, least-privilege IAM roles, encryption at rest on RDS, structured CloudWatch log groups: encoding these in IaC means they cannot be accidentally omitted in any environment the templates produce.
Rebuilding CI/CD so deployments on AWS feel as fast as they did on Render
Render's automatic deploy-on-push is the behavior teams miss most immediately after migration. Rebuilding that experience on AWS is a discrete engineering project, not a side effect of the infrastructure work. Plan for it explicitly; teams that treat it as an afterthought end up with a clunky manual process that erodes confidence in the whole migration.
GitHub Actions is the natural starting point for teams already using GitHub. A functional pipeline, one that builds a Docker image, pushes it to ECR, triggers a rolling ECS deployment, runs smoke tests, and sends a notification, can be operational within a few weeks when worked on in parallel with the broader migration effort.
The pipeline follows a predictable shape. A push to main triggers a build. The built image gets tagged and pushed to ECR. ECS receives an updated task definition referencing the new image tag, and the service performs a rolling deployment, replacing old containers with new ones while maintaining availability. Smoke tests run against the deployed environment. A notification confirms success or surfaces failures.
Staging and production should have separate pipeline paths. Staging deploys on every pull request merge so that every change gets an integration environment before production sees it. Production deploys on an explicit version tag or a manual approval gate, depending on how much risk the team is willing to carry.
Environment variables and secrets that lived in Render's dashboard move to AWS Secrets Manager. The pipeline injects them at deploy time. They never enter the image, and they never appear in pipeline logs. Both of those constraints sound obvious until you're debugging a production issue and realize a key rotated four months ago and nobody updated the image.
Run Render and AWS simultaneously until the AWS pipeline has completed at least one full deploy cycle and smoke tests have passed. Do not shorten that parallel period to save on Render costs. The migration took months to plan; the overlap billing is not the variable worth optimizing.
The cutover: switching DNS and decommissioning Render
By the time you reach the cutover, everything consequential has already happened. The cutover itself should be boring.
Before touching DNS, confirm three things: ALB health checks are green across all ECS tasks, RDS is receiving application traffic in the staging environment without errors, and CloudWatch alarms are configured for error rate and latency with notification paths that actually reach a human being. If any of those three conditions is unmet, the cutover date moves. Not "moves with an exception" or "moves unless the team is comfortable with the risk." Moves.
Lower the TTL on the DNS record 24 to 48 hours before the scheduled cutover. A TTL sitting at 86,400 seconds will cause propagation delays that make rollback slow and post-cutover monitoring ambiguous. Bring it down to 60 or 300 seconds well in advance.
The cutover itself is a single DNS record change: swap the CNAME or A record from the Render service URL to the ALB's DNS name. Watch CloudWatch logs and the ALB access log for the first 30 minutes. The most common issues at this stage are environment variable mismatches, which produce application errors rather than infrastructure errors, and security group rules that prevent ECS tasks from reaching RDS on the correct port. Both are diagnosable and fixable quickly if you're watching the right logs. If you're watching no logs at all, you find out about the problem from a user.
Keep the Render service suspended but intact for at least one billing cycle. It is the rollback path. A week of clean production traffic on AWS with no incidents is a reasonable threshold for decommissioning Render services. Decommission the databases only after you've confirmed that no application code anywhere still references the Render connection strings, which means searching for them, not assuming.
What running on AWS adds that Render never could
The benefits that follow from this migration are structural, not aspirational.
VPC isolation means the database is genuinely unreachable from the public internet. Not protected by a password, not behind a shared firewall, but architecturally unreachable because no route exists. Render's shared-tenant networking model cannot replicate this, not because of any deficiency in their engineering, but because it was never the product's purpose.
IAM roles replace API keys for service-to-service authentication. When an engineer leaves, there are no credentials to manually rotate across services. The role exists; the person's access to assume it does not. This change eliminates a category of security risk that grows more significant as the team scales.
Cost structure can improve at scale. A 2 vCPU, 4 GB RAM PostgreSQL instance is listed at $95 per month on Render's published pricing versus $57.59 per month for an RDS db.t3.medium at current AWS on-demand rates, and that gap can widen further with Reserved Instance or Savings Plan commitments — options that are not available on a PaaS. Teams should verify current pricing directly from Render and AWS before relying on these figures for planning purposes.
Compliance becomes more achievable. SOC 2, HIPAA, and PCI DSS audits require auditors to inspect networking controls, access logs, and encryption configurations directly. CloudTrail, VPC flow logs, and RDS encryption at rest are auditable artifacts that exist in your account, under your control. That audit trail is unavailable on a shared PaaS because the infrastructure isn't yours to inspect.
The operational ceiling expands. Multi-region deployments, custom autoscaling policies, GPU compute for inference workloads, private connectivity to third-party data sources: these are available options on AWS. The infrastructure stops being the constraint on what the product team can build, and that shift in ceiling is what the migration is actually for.


