Run NextJS in an AWS Lambda

Looking to host a new web app that was built in NextJS, we set out to find a way to host it on a Lambda rather than via a docker image (which can get rather expensive). These are the steps we took to do so.

Create a static bucket

First thing you will need to do is create an S3 bucket to host your static files that NextJS creates. No need hosting these in the lambda, and S3 is dirt cheap.

For the sake of demo we will call the bucket nextjs-web-static-bucket. Make sure that you have this bucket set up to host websites.

Compile your NextJS app

You’re going to want to add a couple of values your next.config.js file.

  • output: 'standalone'
  • assetPrefix: 'http://nextjs-web-static-bucket.s3-website.REGION.amazonaws.com/'

The output makes the NextJS compiler output everything you need to run in a production instance.

The assetPrefix option makes sure that the paths to the staic files are correct, linking to your new bucket rather than the lambda function.

You are now ready to run a next build command in your terminal.

Upload your static assets

NextJS will have created a .next/static folder. Upload the contents of that folder to s3://nextjs-web-static-bucket/_next/static.

Create your Lambda

Add a file to the .next/standalone folder called run.sh, and put the following in it.

#!/bin/bash
# Execute server.js, which is output using "next build"
exec node server.js

Now, zip up the contents of the .next/standalone folder.

In the AWS console, create your lambda. Upload the zip you just created as the contents of the lambda. You should also under advanced options select the Enable function URL option, to save you needing to set up an API gateway.

Now comes the magic part. Under code, you want to add a layer. You will be adding the aws-lambda-web-adapter. Click Add Layer, specify ARN, and enter the following into the box.

arn:aws:lambda:${AWS::Region}:753240598075:layer:LambdaAdapterLayerX86:17

Then, go to configuration, and add a couple of environment variables.

  • AWS_LAMBDA_EXEC_WRAPPER: /opt/bootstrap
  • PORT: 3000

Save your lambda, and then visit the Function URL.

You can see some example Terraform code on the Gist below

resource "aws_lambda_function" "nextjs_lambda" {  
  filename         = var.standalone_zip
  function_name    = "nextjs-lambda"
  role             = aws_iam_role.nextjs_lambda_role.arn
  handler          = "run.sh"
  runtime          = "nodejs16.x"
  architectures    = ["x86_64"]
  source_code_hash = filebase64sha256(var.standalone_zip)
  layers = [
    "arn:aws:lambda:${var.aws_region}:753240598075:layer:LambdaAdapterLayerX86:16"
  ]
  environment {
    variables = { 
      AWS_LAMBDA_EXEC_WRAPPER = "/opt/bootstrap"
      NODE_ENV                = "production"
      PORT                    = "3000"
      RUST_LOG                = "info"
    }
  }
}

resource "aws_lambda_function_url" "nextjs_lambda" {
  function_name      = aws_lambda_function.nextjs_lambda.function_name
  authorization_type = "NONE"
  cors {
    allow_credentials = true
    allow_origins     = ["*"]
    allow_methods     = ["*"]
    allow_headers     = ["date", "keep-alive"]
    expose_headers    = ["keep-alive", "date"]
    max_age           = 86400
  }
}

resource "aws_s3_bucket" "nextjs_bucket" {
  bucket = "nextjs-web-static-bucket"
}

resource "aws_s3_bucket_website_configuration" "nextjs_bucket" {
  bucket = aws_s3_bucket.nextjs_bucket.id

  index_document {
    suffix = "index.html"
  }

  error_document {
    key = "error.html"
  }
}

Posted

in

,

by

Comments

Leave a Reply