← Back to list

Migrating from Fly.io to AWS: Key Challenges and Migration Considerations

Overview

Dmitry Yakovlev in TrackIt · 2026-03-27 06:07 · 5 claps · 10.3 min read
#aws #cloud-migration #flyio #cloud-computing #cloud-services
Open on Medium ↗
Wiki topics: ☁️ · DevOps & Cloud

Migrating from Fly.io to AWS: Key Challenges and Migration Considerations

Overview

This guide outlines the key challenges and considerations when migrating an application from Fly.io’s platform-as-a-service (PaaS) to AWS infrastructure using a lift-and-shift approach with minimal application changes. Beyond hosting, Fly.io also provides integrated observability via Prometheus metrics and supports CI/CD workflows through third-party integrations like GitHub Actions. It is intended as a practical guide for engineering teams evaluating or planning such a migration.

The analysis is based on the TaskVault sample application, a task tracker with file attachments built using Node.js, Express, PostgreSQL (via Prisma ORM), and S3-compatible storage. The application uses hexagonal architecture, which significantly simplifies the migration by isolating infrastructure concerns from business logic.

Each section below covers a specific area of the migration, identifies the challenges involved, and proposes solutions or workarounds.

While this guide focuses on Fly.io, the same migration patterns and considerations generally apply to similar developer-focused PaaS platforms such as Render and Railway.

Why Migrate from Fly.io to AWS?

Beyond the early-stage platform

Fly.io is an excellent platform for moving fast: deploy in a few commands, managed Postgres, built-in object storage. It is the go-to choice for teams that want to ship without worrying about infrastructure.

But as applications scale and business requirements evolve, its limitations start to show. Less control over networking, a narrower service catalog, and compliance requirements (SOC2, HIPAA, ISO 27001) that enterprise customers increasingly demand from their vendors.

AWS is a massive ecosystem of over 200 services, and that breadth is precisely what makes it compelling at scale. Beyond the service catalog, AWS gives teams granular control over their networking layer through VPC, security groups, private subnets, VPC peering, and PrivateLink, which provides the kind of infrastructure control that PaaS platforms deliberately abstract away.

For enterprise deals, that abstraction becomes a blocker: many large organizations and third-party integrations explicitly require an AWS environment as a baseline. For engineering teams hitting these walls on Fly.io, migrating to AWS is less of a platform switch and more of a natural next step.

Scaling: where the gap becomes real

Machines on Fly.io are not provisioned on demand based on request traffic. They must be provisioned upfront, after which the platform starts or stops them as needed. Autoscaling exists, but it requires external Prometheus monitoring and triggering scale operations via CLI or API.

AWS, by contrast, handles this natively: Amazon ECS (Elastic Container Service) on AWS Fargate scales tasks automatically based on CPU, memory, or custom CloudWatch metrics. AWS Lambda scales to thousands of concurrent executions transparently. For teams expecting unpredictable traffic spikes or rapid growth, this difference is significant.

Cost efficiency at scale

PaaS platforms come with a simplicity premium that compounds as traffic grows. At high volumes, moving to AWS and using Spot Instances or Graviton processors can meaningfully reduce infrastructure costs. AWS also offers Savings Plans and Reserved Instances, where committing to a usage level unlocks substantial discounts. These options simply do not exist on Fly.io.

Database maturity

Fly.io’s managed Postgres is convenient but remains largely self-managed. Users are responsible for scaling and tuning. Amazon RDS (Relational Database Service) is a fully mature service with automated failover, point-in-time recovery, and multi-AZ replication out of the box. For production workloads where uptime is non-negotiable, this gap matters.

Architecture Transformation

The migration involves a fundamental shift from a container-based deployment model to an AWS container-based architecture using ECS Fargate. The table below maps Fly.io components to their AWS equivalents:

Challenge #1: Container Registry Migration

Fly.io builds and deploys Docker images directly from a Dockerfile using its built-in remote builders. On AWS, the Docker image must be pushed to Amazon Elastic Container Registry (ECR) before ECS can pull and run it. The Dockerfile itself requires no modifications. The same image runs on both platforms.

Challenge #2: Service Discovery and Networking

Fly.io provides automatic service discovery via .flycast internal DNS and WireGuard-based private networking. On AWS, services communicate through VPC networking, security groups, and ALB target groups. The ECS service must be configured with proper health checks, target group registration, and security group rules to allow traffic from the ALB.

Solution: Since this is a lift-and-shift migration, the Express application runs unchanged inside the same Docker container on ECS Fargate. No code modifications are required. The application listens on the same port (3000), uses the same middleware, and serves the same routes. Only the infrastructure configuration changes (from fly.toml to Terraform).

Database Migration

Challenge #1: Network Topology

Fly Postgres is accessed via Fly’s internal private network using .flycast DNS names (e.g., taskvault-db.flycast:5432). On AWS, RDS instances are accessed via public endpoints or through VPC private networking. The DATABASE_URL connection string must be updated to point to the RDS endpoint.

Challenge #2: Prisma Binary Targets

Prisma generates platform-specific query engine binaries. Fly.io’s Docker image uses Debian (debian-openssl-3.0.x), while the ECS container uses the same Debian base image (debian-openssl-3.0.x), so no binary target changes are needed if the same Dockerfile is used.

Challenge #3: Connection Pooling

Fly.io runs a persistent process that maintains a database connection pool. ECS Fargate also runs persistent containers, so connection pooling behavior is identical. Prisma’s default connection pool works out of the box without any configuration changes.

Solution: No changes required. ECS Fargate runs long-lived containers just like fly.io, so Prisma’s connection pool operates identically. For high-traffic production workloads, Amazon RDS Proxy ($15–20/mo*) can be added as an additional optimization layer.

* Pricing may vary based on region, usage patterns, data transfer, and AWS pricing changes.

Data Migration with AWS DMS

AWS Database Migration Service (DMS) is the recommended approach for migrating data from Fly Postgres to Amazon RDS. DMS provides a fully managed migration experience, supporting both full-load and change data capture (CDC) for minimal downtime migrations.

Step 1: Expose Fly Postgres Externally (Temporary)

Fly Postgres is only accessible via Fly’s internal private network (.flycast). To allow DMS to connect, temporarily expose the database externally using Fly.io’s native external connection feature: allocate a public IPv4 address (fly ips allocate-v4 — app taskvault-db), configure the fly.toml to expose port 5432 with the pg_tls handler, and redeploy. The database becomes accessible at taskvault-db.fly.dev:5432 with mandatory TLS encryption.

Step 2: Configure DMS

Create a DMS replication instance in the same VPC as the target RDS. Configure the source endpoint pointing to taskvault-db.fly.dev:5432 with SSL mode set to “none”, and the target endpoint pointing to the RDS instance in the private subnet. Create a full-load replication task to migrate all tables and data.

In the AWS console, the following appears under DMS → Dashboard → Replication Instance:

And in DMS -> Dashboard -> Endpoints:

Step 3: Run Migration and Clean Up

Start the DMS replication task. DMS connects to Fly Postgres over the internet (TLS-encrypted), reads the data, and writes it to RDS in the private subnet. Once the task has started and completed, the following will appear in the AWS dashboard (DMS → Dashboard → Database migration tasks)

After verifying row counts match, immediately revoke external access to Fly Postgres by releasing the public IP (fly ips release — app taskvault-db) and delete the DMS replication instance to stop incurring costs.

Security Considerations

While the database is temporarily exposed, it remains protected by TLS encryption (enforced by the pg_tls handler) and PostgreSQL authentication (username and password). The exposure window should be minimized to the duration of the migration only. It is recommended to change the database password after revoking external access as an additional precaution.

Storage Migration

Challenge #1: Different Endpoints and Credentials

Tigris is S3-compatible but uses a different endpoint (fly.storage.tigris.dev) and its own access credentials. The AWS SDK configuration must be updated to use Amazon S3’s default endpoint and IAM role-based authentication instead of explicit access keys.

Challenge #2: Presigned URLs

Existing presigned download URLs generated by Tigris will stop working after migration. Any URLs stored in the database or shared externally will need to be regenerated from the new S3 bucket. Since TaskVault generates presigned URLs on-demand (not stored), this is handled automatically.

Solution: The application already uses the AWS SDK v3 for S3 operations. On ECS Fargate, the SDK automatically uses IAM task role credentials (no explicit keys needed).

Data Migration with AWS DataSync

AWS DataSync Enhanced mode is the recommended approach for migrating object storage from Tigris to Amazon S3. Since May 2025, DataSync supports cross-cloud transfers without requiring an agent, making it ideal for S3-compatible sources like Tigris.

Step 1: Create Source Location (Tigris)

Configure a DataSync location of type “Other cloud object storage” with the Tigris endpoint (https://fly.storage.tigris.dev), the bucket name, and the Tigris access key and secret key credentials. Select Enhanced mode to enable agent-free cross-cloud transfer.

Step 2: Create Destination Location (Amazon S3)

Configure a DataSync location pointing to the Amazon S3 attachments bucket provisioned by Terraform. Assign an IAM role that grants DataSync read/write permissions on the bucket.

After this step, the following should be visible on the AWS dashboard (DataSync → Locations):

Step 3: Create and Run the DataSync Task

Create a DataSync task linking the Tigris source to the S3 destination with the “Transfer all data” option. DataSync automatically parallelizes the transfer and verifies data integrity after completion. Once the task is created, it will appear on the dashboard (DataSync → Tasks) as shown below:

Once the task has been executed, it will appear in DataSync → Task history as shown below:

Once verified, delete the DataSync task and locations to avoid any recurring costs.

Cost

DataSync Enhanced mode charges $0.0125* per GB transferred. For a demo application with minimal data, the cost is effectively zero. For enterprise migrations with terabytes of data, DataSync provides significant advantages over manual tools: automatic parallelization, data integrity verification, and transfer monitoring through the AWS console.

* Pricing may vary based on region, usage patterns, data transfer, and AWS pricing changes.

Networking & Security

Challenge #1: SSL/TLS Certificates

Fly.io automatically provisions and renews SSL certificates for all applications. On AWS, SSL is handled by AWS Certificate Manager (ACM) integrated with Amazon CloudFront. Certificates must be provisioned in us-east-1 for CloudFront, regardless of the application region.

Challenge #2: Network Architecture Complexity

Fly.io provides automatic private networking between services via WireGuard. AWS offers more granular control through VPCs, subnets, security groups, and NAT gateways, but this adds significant configuration complexity compared to Fly.io’s zero-config approach.

Challenge #3: RDS Access Control

ECS Fargate tasks and RDS are placed in private subnets within the VPC. The Application Load Balancer sits in public subnets and forwards traffic to ECS targets in private subnets. ECS accesses S3 via a free S3 Gateway Endpoint. This architecture provides defense in depth without requiring a NAT Gateway for S3 access.

Solution: Deploy ECS Fargate and RDS in private subnets with an ALB in public subnets. Use a free S3 Gateway Endpoint for storage access. All database connections are encrypted via PostgreSQL SSL within the VPC. For outbound internet access (e.g., external APIs), add a NAT Gateway (~$32/mo*).

* Pricing may vary based on region, usage patterns, data transfer, and AWS pricing changes.

Infrastructure as Code

Challenge: Configuration Complexity

Fly.io’s entire deployment configuration fits in a single fly.toml file (~20 lines). The equivalent AWS infrastructure requires Terraform configuration covering ECS, ECR, ALB, CloudFront, RDS, S3, IAM roles, and security groups.

Benefit: While Terraform requires more initial setup, it provides fine-grained control over every resource, supports team collaboration through remote state, and integrates with CI/CD pipelines. AWS CDK is an alternative for teams preferring imperative TypeScript over declarative HCL.

Conclusion

With a well-structured application, migrating from Fly.io to AWS is largely an infrastructure problem, not a code problem. The application logic, database schema, and business logic stay the same.

What changes is the layer underneath. And that layer, once on AWS, provides growing engineering teams with the control, the compliance, and the scalability they eventually need.

This approach introduces additional complexity. Terraform configurations, IAM roles, and VPC networking are not as simple as a Fly.io deploy. But this is a one-time investment that pays dividends as both the product and the team scale.

While this guide focuses on Fly.io, the same principles apply to similar developer-focused PaaS platforms such as Render and Railway.

Migration Checklist

Follow these steps in order to complete the migration from Fly.io to AWS:

  1. Write Terraform configuration: define all AWS resources (VPC, ECR, ALB, ECS, RDS, S3, CloudFront, IAM)
  2. Deploy AWS infrastructure: run terraform init and terraform apply to provision all resources
  3. Build and push Docker image to ECR: build the existing Docker image and push it to Amazon Elastic Container Registry
  4. Deploy ECS service: Terraform creates the ECS task definition and service, pulling the image from ECR
  5. Run database migrations: dedicated ECS task executes npx prisma migrate deploy
  6. Expose Fly Postgres externally: allocate public IP and configure pg_tls handler for temporary external access
  7. Migrate database with AWS DMS: configure source endpoint (Fly Postgres), target endpoint (RDS), and run full-load replication task
  8. Revoke Fly Postgres external access: release the public IP and change the database password
  9. Migrate storage data with AWS DataSync: configure Tigris as source location (Enhanced mode, no agent), S3 as destination, and run the transfer task
  10. Deploy frontend: sync static files to the S3 frontend bucket, invalidate CloudFront cache
  11. Verify the migration: test health endpoint, CRUD operations, file uploads/downloads
  12. Update DNS: point the domain to the CloudFront distribution
  13. Decommission Fly.io: destroy Fly.io resources after confirming AWS deployment is stable

Note: Why ECS Fargate over AWS App Runner?

AWS App Runner is a simpler alternative for running containers on AWS, offering a PaaS experience similar to Fly.io. However, for a lift-and-shift scenario, Amazon ECS on AWS Fargate is preferred for several reasons:

  • Complete control over VPC, security groups, and network architecture
  • Support for one-off tasks (e.g., running database migrations independently)
  • Native integration with ALB for advanced routing and health checks
  • Better suited for production workloads requiring fine-grained infrastructure control

App Runner remains a valid option for teams prioritizing simplicity over infrastructure control, but it would be closer to replacing one PaaS with another rather than a true lift & shift into AWS.


메타데이터
post_id
1890b4697f7e
slug
migrating-from-fly-io-to-aws-key-challenges-and-migration-considerations-1890b4697f7e
url
https://medium.com/trackit/migrating-from-fly-io-to-aws-key-challenges-and-migration-considerations-1890b4697f7e
canonical_url
https://medium.com/trackit/migrating-from-fly-io-to-aws-key-challenges-and-migration-considerations-1890b4697f7e
author_url
https://medium.com/@dmitry_yak
status
ok
fetched_at
2026-06-23 17:05:31