In the ever-evolving world of web development, deploying applications has become increasingly complex. However, with the rise of serverless architectures and powerful cloud platforms like Amazon Web Services (AWS), the process of deploying and scaling applications has become more streamlined and efficient than ever before. In this article, we will explore how to leverage AWS Lambda Web Adapter, Terraform, and AWS Lambda in combination with CloudFront to deploy a simple Next.js application.
Setting up NextJS app for standalone deployment
To create a new Next.js project, run the following command in your terminal:
Once the project is set up, you'll need to make a couple of modifications in the next.config.js
file to prepare the application for standalone deployment and utilize static files from a CDN. Open the next.config.js
file and add the following code:
By setting output
to standalone
, Next.js can automatically create a standalone folder that copies only the necessary files for a production deployment, including select files in node_modules.
The assetPrefix
property should be set to the URL of your desired CDN. By doing so, your Next.js app will utilize the CDN to serve static files, resulting in improved performance and faster load times for your users.
Remember to replace <CDN_URL>
with the actual URL of your CDN. Once you've made these changes, your Next.js application will be ready for deployment using AWS Lambda and a CDN.
Provisioning infrastructure with Terraform
To deploy the Next.js app, we need to provision specific infrastructure components like a Lambda function with an API Gateway for the server, as well as a CloudFront distribution to handle static files. In this guide, we will focus on packaging the code as a zip file and utilizing it in a Lambda function. If you prefer using a Docker image, please refer to the official documentation for detailed instructions.
Before we begin, make sure you have Terraform and the AWS CLI installed. If you haven't already, you can follow the links below to install them:
Now, let's set up the necessary Terraform files. Create a new folder named nextjs-lambda/
in the root directory of your application, and place the following files inside:
nextjs-lambda
├── api_gw.tf
├── cloudfront.tf
├── deploy.sh
├── domain.tf
├── lambda.tf
├── main.tf
├── output.tf
├── run.sh
├── s3_bucket.tf
├── terraform.tfvars
├── terraform.tfvars.example
└── variables.tf
These files contain the Terraform configurations needed to provision the required AWS resources. The main.tf
file acts as the entry point for Terraform, and the other files define specific resources like API Gateway, CloudFront, Lambda function, and more.
Main
Sets up the providers and remote state management in main.tf
. Here, the state is managed using S3 and Dynamodb. You can configure it to use other state management methods as well.
Lambda
The lambda.tf
file is responsible for declaring the Lambda function, its policy, and CloudWatch logs. It utilizes the archive_file
module to create a zip file of the Next.js package.
- The Lambda handler in the code utilizes the
run.sh
bash script to initialize the server. - To make this entire setup work, we rely on the Lambda Web Adapter layer. The layer is available in different architectures, and further details can be found in the official documentation.
- Additionally, you need to configure the Lambda environment variable
AWS_LAMBDA_EXEC_WRAPPER
to/opt/bootstrap
. In the above example, this value is passed as a Terraform variable.
API Gateway
API Gateway enables us to invoke Lambda functions using a custom domain. In this example, Terraform is used to set up an HTTP API Gateway and grant it permission to invoke Lambda.
S3 Bucket
This section sets up a private S3 bucket and grants permission to access its contents via CloudFront.
CloudFront
CloudFront distribution is created with the S3 bucket as the source. Managed policies and other essential configurations are defined. Origin Access Control (OAC) is used to access the S3 bucket content.
Domain
This section is responsible for creating and assigning custom domains for API Gateway and CloudFront distribution. It utilizes an existing Hosted Zone and SSL Certificate. For API Gateway, a custom domain is mapped to the HTTP API gateway with the default stage.
Variables
All the variables used in this Terraform module:
terraform.tfvars
The terraform.tfvars
file is used to store variable values for the Terraform configuration. You can create it based on the provided terraform.tfvars.example
file and fill in the necessary values.
Output (optional)
Returns the CDN domain and API Gateway domain
With the infrastructure provisioning files in place, we are ready to move on to the next steps of deploying the Next.js app using AWS Lambda and CloudFront.
Deploy Script
Add a run.sh
bash script to the nextjs-lambda/
folder, which will serve as the entry point to run the app after deployment.
In addition, include another bash script called deploy.sh
that packages files, provisions the infrastructure, and uploads static files to S3. Remember to update the CDN_NAME
variable (which must match the one used for infrastructure). Ensure that these bash scripts have execution permissions.
To deploy the app, execute the deploy.sh
bash script with argument deploy
.
Note that even after the deployment is complete, it may take some time for the CloudFront deployment to finish.
To just sync files to Lambda and S3, execute the deploy.sh
bash script with argument sync
.
To destroy the deployment, navigate to the nextjs-lambda/
folder and run:
Remember to add this to the .gitignore
file before committing any changes to Git.
You can find the code and an example in this GitHub repository