Why you should host Rails on AWS
Hosting Rails on AWS instead of platforms like Heroku and Render comes with a number of benefits:
- Reduce latency between your Rails API and your data infrastructure (RDS, S3, etc.).
- Improve security by keeping requests to your infrastructure within your private network.
- Reduce egress fees for outgoing requests. On AWS, you get 1TB for free in CloudFront's free tier and each additional 1TB costs about $85. That's a massive reduction in price compared to Heroku and Render.
- Reduce egress fees for infrastructure. When you transfer data out to Heroku or Render from your RDS instance, S3 bucket, or DynamoDB table it costs about $90/TB.
- Hyperscale. AWS supports massive scale. You'll never run out of compute.
- You can deploy Rails to AWS for less than $10/mo.
Challenges
It can be difficult to decide between the wide range of AWS solutions including Amplify, Beanstalk, EC2, ECS, and Lambda. There are benefits and drawbacks to each one, but in our own testing we've found the preferable solution to be CloudFront > API Gateway > AWS Fargate which, when spot placement is utilized, costs about $8.50/mo. With this approach you can avoid issues with deploying to AWS Lambda in a VPC and solve the slow iteration velocity of EC2. It's also easier to horizontally scale than other solutions.
Solutions
Here's how you can deploy this stack to AWS using some of the most popular deployment tools.
The best way: FlexStack
FlexStack is the fastest, easiest way to deploy web services to AWS. Here's how you can host Rails on AWS with FlexStack:
- Create a new Project and Environment. An environment sets up all the private networking you'll need, as well as an ECS cluster. We recommend starting with a Development grade environment, as that'll be the cheapest.
- Connect your FlexStack project to AWS.
- Add any secrets and environment variables your Rails app needs to your new environment, including your Rails master key using the name
RAILS_MASTER_KEY
. - Connect a GitHub repository containing your Rails app.
- Hit the deploy button.
That's all! We generated a Dockerfile (if you didn't have the Rails default one) and all of the CloudFormation templates you need for you. When it finishes deploying you'll be set up with a serverless Rails app that is ready for hyperscale, complete with all of the features that come with FlexStack web services including:
- Global CDN
- Horizontal autoscaling
- Architecture autoscaling
- Zero-downtime rollouts
- Self-healing tasks
- Automatic rollback
- Observability
- GitOps
- High-performance Docker builds
- Custom domains
- and more...
Deploy with IaC
Terraform
To demonstrate deploying Rails to AWS with Terraform, we made a Rails Terraform Example App. The Terraform code itself is in the infra
directory. The general steps are:
- Create a VPC with networking.
- Create an ECS cluster.
- Create an ECR repository.
- Create a CloudMap namespace.
- Create an HTTP API using API Gateway.
- Create an ECS task definition and container definition for the Rails app.
- Create an ECS Fargate service to manage your ECS tasks.
- Add a VPC Link integration to API Gateway which will pass traffic from API Gateway to your ECS service.
- Create a CloudFront distribution that sends traffic to the API Gateway origin.
- Create an SSM parameter containing your Rails master key at
/rails-cdk/prod/RAILS_MASTER_KEY
. Import the parameter as a data source, then reference the data sourcearn
in the container definition's secrets. - Push your Rails image to ECR when you deploy your Terraform app using a
null_resource
withlocal-exec
, making sure it's triggered by a hash of the source code so it doesn't rebuild unnecessarily.
CDK
To demonstrate deploying Rails to AWS with Terraform, we made a Rails CDK Example App. The CDK code itself is in the infra
directory. The general steps are:
- Create a VPC with networking.
- Create an ECS cluster.
- Create a CloudMap namespace and add it to the ECS cluster.
- Create an HTTP API using API Gateway.
- Create an ECS container definition using your source code as a path in
ContainerImage.fromAsset
- Create an ECS task definition.
- Create an ECS Fargate service to manage your ECS tasks.
- Create an SSM parameter containing your Rails master key at
/rails-cdk/prod/RAILS_MASTER_KEY
. Add the master key variable to your container definition via thesecrets
property. - Add a VPC Link integration to API Gateway which will pass traffic from API Gateway to your ECS service.
- Create a CloudFront distribution that sends traffic to the API Gateway origin.
IaC vs. FlexStack
These examples lack some critical functionality that FlexStack provides out-of-the-box.
- Architecture autoscaling: Both of these examples are request-driven using API Gateway as opposed to Application Load Balancers. FlexStack knows when an ALB is more cost-performant and can automatically change your architecture without downtime. Adding this to the examples would make them more difficult to consume.
- Enhanced network security: We used public subnets for these examples because they are cheaper compared to NAT gateway architectures with private networking when you're running a small number of services. FlexStack handles this cost management and security posture for you.
- High-performance Docker builds: FlexStack builds your Docker images on
c5ad.xlarge
EC2 instances, which our tests have shown to be the best performing in terms of cost and build time for building most Docker images. - Observability: For brevity, we didn't add any third-party logging integrations or CloudWatch log groups.
- Preallocated availability zones: FlexStack preallocates 3 availability zones such that if your target availability changes, we can add your services to multiple AZs without needing to reconfigure your VPC.
- Environment-specific cost savings: FlexStack saves you money by using spot placement and shorter log retentions in development environments.
- Custom domains: Using custom domains requires that ACM certificates be added to your CloudFront distribution. For the sake of brevity, we omitted this as well. With FlexStack, all you have to do is add DNS records to your domain and you are set.
- Secret management: You will have to manually add secrets to your application container definitions. By contrast, FlexStack injects them for you safely during build and run time.
- GitOps: You will have to design your own push-to-deploy workflows using something like GitHub actions if you'd like to use GitOps with IaC. By contrast, FlexStack sets up a pipeline for you and additionally handles things like build parallelization so your applications deploy quicker.
- Service connect: FlexStack registers your services to ECS Service Connect for DNS-free service discovery.
- Tags and resource RBAC: FlexStack manages AWS tags for you, allowing for detailed cost breakdowns and AWS resource RBAC.
Terraform vs. CDK
Generally speaking, Terraform is preferable if you desire faster deploys, more control over resources, and need to support multiple cloud providers. CDK is preferable if you care about correctness, staying in the AWS ecosystem, maintaining fewer lines of infrastructure code, and you don't mind spending a few (sometimes more) extra minutes waiting for CloudFormation to deploy or rollback. There is a long list of pros and cons to either approach.