Search code examples
webpackaws-lambdanext.jsserver-side-renderingserverless-framework

Serverless-framework & next.js lambda function exceeds unpacked size limit


I'm trying to figure out how to reduce the package size of a ssr application (next.js & serverless-framework) to get it under 250MB AWS limit: An error occurred: ServerLambdaFunction - Unzipped size must be smaller than 262144000 bytes (Service: AWSLambdaInternal; Status Code: 400; The app is deployed by running next build && sls deploy

The unpacked archive is 600MB+, more than double of 250MB that AWS allows for lambda. Below is the list of largest dependencies in node_modules. I can see that i can reduce it marginally by moving several to dev dependencies, but that won't solve the problem.

I'd appreciate if someone could point me to the right direction.

node modules with largest size

Here's my package.json:

{
  "name": "ssr-ui",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "next build",
    "deploy": "next build && sls deploy",
    "start": "next start",
    "dev": "next dev"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@aws-amplify/api": "^3.2.4",
    "@aws-amplify/auth": "^3.4.4",
    "@aws-amplify/core": "^3.5.4",
    "@aws-amplify/storage": "^3.3.4",
    "@aws-amplify/ui-react": "^0.2.21",
    "antd": "^4.6.4",
    "antd-img-crop": "^3.10.0",
    "axios": "^0.20.0",
    "country-state-picker": "^1.1.1",
    "echarts": "^4.9.0",
    "echarts-for-react": "^2.0.16",
    "express": "^4.17.1",
    "next": "^9.5.3",
    "next-react-svg": "^1.1.2",
    "path-match": "^1.2.4",
    "query-string": "^6.13.2",
    "react": "^16.13.1",
    "react-dom": "^16.13.1",
    "react-router-dom": "^5.2.0",
    "react-share": "^4.2.1",
    "sass": "^1.26.11",
    "serverless-apigw-binary": "^0.4.4",
    "serverless-domain-manager": "^4.2.0",
    "serverless-http": "^2.5.0",
    "url": "^0.11.0"
  },
  "devDependencies": {
    "@types/jest": "^26.0.14",
    "@types/node": "^14.10.3",
    "@types/react": "^16.9.49",
    "@types/react-dom": "^16.9.8",
    "@types/react-router-dom": "^5.1.5",
    "typescript": "^4.0.2"
  }
}

my serverless.yml is:

service: ssr-react-next

provider:
  name: aws
  runtime: nodejs12.x
  region: ap-southeast-2
  stage: ${self:custom.secrets.NODE_ENV}
  environment:
    NODE_ENV: ${self:custom.secrets.NODE_ENV}

functions:
  server:
    handler: index.server
    events:
      - http: ANY /
      - http: ANY /{proxy+}

plugins:
  - serverless-apigw-binary
  - serverless-domain-manager

custom:
  secrets: ${file(secrets.json)}
  apigwBinary:
    types:
      - '*/*'
  customDomain:
    domainName: ${self:custom.secrets.DOMAIN}
    basePath: ''
    stage: ${self:custom.secrets.NODE_ENV}
    createRoute53Record: true

Thank you!


Solution

  • There are couple of ways to solve your problem:

    1. Move dependencies that are not used during runtime to devDependencies.

        Serverless ignores devDependencies when it's creating the zip file to upload.

    1. Add aws-sdk to devDependencies.

        Until Node.js v16, aws-sdk v2 is already available in Lambda. So don't bundle it.

        Also, if you are using Node.js v18, better to update to latest version of AWS SDK (@aws-ask/*)

    1. Since you are using webpack bundler, you can package your functions individually.

        That means, serverless shall create an individual zip file for each lambda function.

        You can enable that by having the below in your serverless.yml file:

    package:
      individually: true
    
    1. Ignore files that do not have to be in the zip file

        You can do that by having the below in your serverless.yml file:

    package:
      individually: true
      patterns:
        - '!.github'
        - '!.husky'
        - '!node_modules/aws-sdk/**'
        - '!node_modules/@aws-sdk/**'
        - '!package-lock.json'
        - '!README.md'
    

        You can add more patterns depending on your use-cases.