Why you should host Next.js on AWS
By now, every Next.js user knows how to host their app on Vercel. But what should you do when your costs start to spiral or you want to integrate your site with products in the AWS ecosystem like S3, DynamoDB, or RDS?
Hosting Next.js on AWS has a number of benefits. Here are some reasons you might want to do it:
- Reduce latency between your Next.js API and your infrastructure (RDS, Elasticache, etc.).
- Improve security by keeping requests to your infrastructure within your private network.
- Reduce egress fees for Next.js itself. With Vercel you receive 100GB free, after which each 1TB on Vercel costs $150. On AWS, you get 1TB for free in CloudFront's free tier and each additional 1TB costs about $85. That's a massive difference!
- Reduce egress fees for infrastructure. When you transfer data out to Vercel from your RDS instance, S3 bucket, or DynamoDB table it costs about $90/TB.
- Eliminate cold starts! Deploying to a serverless platform doesn't mean cold starts are necessary. With AWS Fargate, you get the benefit of not having to manage servers while avoiding cold starts.
- Hyperscale. AWS supports massive scale. You'll never run out of compute.
- You can deploy to AWS for less than $10/mo.
Challenges
Like most things, it's easier said than done! Where do you even begin? As many online commentators note, 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. Doing so avoids issues with deploying to AWS Lambda in a VPC, is easier to iterate on than using EC2, and is 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
Strap yourself in because you're about to hear a universal truth: FlexStack is the easiest way to deploy web services to AWS. Here's how you can host Next.js 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 Next.js app needs to your new environment.
- Connect a GitHub repository containing your Next.js app.
- Hit the deploy button.
That's all! We generated a Dockerfile and all of the CloudFormation templates you need for you. When it finishes deploying you'll be set up with a serverless Next.js 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
The first thing you'll need is a Dockerfile. Worry not, we've got you covered! You can find a Next.js Dockerfile on our GitHub or use FlexStack's Dockerfile generator.
Terraform
To demonstrate deploying Next.js to AWS with Terraform, we made a Next.js 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 Next.js 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.
- Push your Next.js 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 Next.js to AWS with Terraform, we made a Next.js 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.
- 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.
Drawbacks to deploying outside of Vercel's walled garden
Next.js is purpose-built to be hosted on Vercel. When you choose not to host Next.js on Vercel, you lose infrastructure features like edge functions. You should ask yourself if some of that infrastructure isn't a solution to a contrived problem. That is, the features exist to buttress the fact that deploying web applications to Vercel and Lambda comes with serious downsides and they've created many of these features to work around them.
Reasons to avoid OpenNext
OpenNext takes the output of your Next.js build and packages it so you can deploy all of Next.js's features on your own infrastructure in a variety of environments including Lambdas, Cloudflare Workers, and more. That sounds delightful! But wait, here's the recommended architecture:
If you, as an individual or mid-sized team, are putting in this level of effort just to avoid a difficult conversation with the Vercel sales team - sorry but you have chosen the wrong web framework. The surface area for failures is insanely big. Unless you're working for a company with a dedicated team willing to support this level of complexity for a front-end web framework, we would highly recommend that you pass on OpenNext. If you're using a "warmer" function to keep your Lambdas live, are we wrong for thinking what you actually want is something more like AWS Fargate that is designed to keep your web server online?