Serverless Framework v3 reached end of support on 1 January 2025. It gets no security fixes, bug fixes or new features, and it still bundles aws-sdk v2, which AWS stopped supporting in September 2025. Version 4 is the direct way forward.
Changing frameworkVersion from 3 to 4 is one line. The rest of the work is the licence key, the build step, the plugins and the pipeline, and confirming that the first v4 deploy replaces no resource. This post covers what v4 costs, what changes, the one-time setup, the steps to move a service safely, and what changes for developers day to day.
How much time you have
v3 has no shutdown date. It still installs and deploys, and it will even deploy nodejs24.x, because its runtime check only gives a warning. The deadlines that matter come from AWS’s runtime deprecation schedule:
| Runtime | AWS blocks creating functions | AWS blocks updating functions |
|---|---|---|
nodejs16.x, nodejs18.x, nodejs20.x |
29 July 2027 | 31 August 2027 |
nodejs22.x |
1 June 2027 | 1 July 2027 |
python3.8 to python3.11 |
29 July 2027 | 31 August 2027 |
nodejs24.x |
1 June 2028 | 1 July 2028 |
python3.12 |
30 November 2028 | 10 January 2029 |
What the two blocks mean:
- Create block: you can’t create a function on that runtime. Deploying a new stage or region fails, and so does any change that makes CloudFormation replace a function.
- Update block: you can’t change the code or configuration of existing functions. The only change still allowed is moving to a supported runtime.
- Existing functions keep running after both blocks, but AWS no longer patches them.
Node.js 22 is blocked two months before Node.js 18 and 20, so skip it and go straight to nodejs24.x. AWS has moved these dates before, so check the Lambda runtimes page when you plan.
These dates also catch v3 itself. For some features, v3 adds a small helper Lambda to your stack, and in the final v3 release (3.40.0) that helper always runs on nodejs16.x. No setting changes it. v3 uses the helper for:
- API Gateway logs (
provider.logs.restApiorprovider.logs.websocket), unlessprovider.logs.restApi.roleManagedExternally: trueis set; - S3 events with
existing: true; - Cognito events with
existing: true; - EventBridge events with
useCloudFormation: false.
Every v3 deploy updates the helper. So for services that use any of these features, v3 can’t create new stages from 29 July 2027 and can’t deploy at all from 31 August 2027, even if your own functions are on nodejs24.x. v4 (4.43.0) runs the same helper on nodejs24.x.
The order is: fix runtimes and data protection on v3 first, then upgrade to v4 one service at a time, and finish before 29 July 2027.
What v4 costs
- It is free for organisations(projects) with less than $2M in annual revenue. There is no form and no check: if you qualify, you simply don’t subscribe. Non-profit, government and academic organisations are excluded, and so are subsidiaries more than 75% owned by a parent above $2M.
- Above $2M you pay in credits. One credit is one service, in one stage, in one region, deployed for more than 10 days in a month. Pay-as-you-go costs $4 per credit. Subscribe from the Dashboard’s billing page or through AWS Marketplace. The Customer Agreement says 5 days, so if you sign an Order Form, put 10 days in it.
- Free still needs an account. Even at $0, you need a Serverless organisation, and every command needs a licence key or a signed-in session.
- Ignore the “2 free credits” banner. The banner in the Dashboard is about Dashboard features such as traces and metrics. CLI deploys stay free under $2M, so don’t subscribe because of it.
- If you deploy for clients, the licence follows the client’s revenue. Each client should own its own Serverless organisation. Never buy on your own account and bill the client for it, because that makes you the licensee and your revenue sets the price.
What actually changes in v4
- Every command needs authentication. This covers
deploy,packageand local commands such asserverless offline, for free organisations too. Authentication means a licence key or a signed-in session. Set it up once, as described below, and developers never handle it. - The npm package only downloads the framework. The real binary comes from
install.serverless.com. Your lock file does not pin the version;frameworkVersiondoes. - esbuild is built in. v4 can build and bundle your code itself, which conflicts with build plugins such as
serverless-esbuildorserverless-webpack. serverless packagenow needs valid AWS credentials. v3 didn’t.- The default deployment method is now
direct. That means no change sets unless you ask for them. - Compose is built in. The separate
@serverless/composepackage and--max-concurrencyno longer exist. - Several popular plugins are now part of v4 itself (see step 4 below).
Step 0: Prepare on v3
Do each of these as its own change, deployed with v3:
- Move functions to
nodejs24.x, or topython3.12or later. - Add
DeletionPolicy: RetainandUpdateReplacePolicy: Retainto every table, bucket, queue and user pool. Include a smallMetadatachange in the same deploy, or CloudFormation reports “No updates to perform”. - Turn on point-in-time recovery for each table and take an on-demand backup.
- Remove plugins that are installed but not listed under
plugins:. - Stop CI from printing credentials or parameter values in its logs.
- Allow build agents to reach
install.serverless.comandcore.serverless.comover HTTPS.
A note on the retain policies. The common fear is that “v4 will replace our DynamoDB tables.” It won’t: v4 keeps v3’s naming rules, and tables are written by hand under resources:. The real risk is often already in the template: tables with no retain policies at all. That risk exists today, on v3. Also, DeletionProtectionEnabled alone does not stop a replacement. CloudFormation creates a new, empty table, the application starts using it, and the deploy reports success.
One-time setup: the organisation and the licence key
Do this once per company. If you deploy for clients, do it once per client, in the client’s own organisation and AWS accounts.
Create the organisation. Sign up at app.serverless.com and create your organisation. Use a company-owned login, not a personal one, so the organisation doesn’t depend on one person. The organisation is the account that all usage is counted against. Its name, as shown in the Dashboard, is what you pass as --org to the usage commands later.
Create a licence key. An admin of the organisation creates it in the Dashboard under Settings → License Keys. You can create as many keys as you like; one is enough to start.
Store the key in AWS SSM Parameter Store. v4 finds it automatically only if all of these are true:
- the parameter name is exactly
/serverless-framework/license-key; - it is in the same AWS account and region as the deployment. v4 reads it with the deployment’s own credentials, profile and region, so create it in every account and every region you deploy to;
- nothing else supplies a key first. A
licenseKeyinserverless.yml, theSERVERLESS_LICENSE_KEYorSERVERLESS_ACCESS_KEYenvironment variable, or a signed-in session (serverless login) all take priority over SSM.
read -rs LICENSE_KEY # paste the key; it is not shown or saved in shell history
aws ssm put-parameter \
--name "/serverless-framework/license-key" \
--type SecureString \
--value "$LICENSE_KEY" \
--region <region> --overwrite
The deployment role, and every developer who deploys, needs ssm:GetParameter on this parameter, plus kms:Decrypt if you encrypt it with your own KMS key.
To keep the key under a different name, point to it from the config: licenseKey: ${ssm:/your/path}. Never paste the key itself into serverless.yml.
Upgrading a service, step by step
Upgrade one service at a time, on a non-production stage first.
1. Save the v3 baseline. Package with v3 and keep the output. It contains resolved secrets, so don’t commit it.
npx serverless@3 package --stage <stage>
cp -r .serverless v3
Use serverless@3 explicitly. Without the @3, npx can download the latest release, which is v4, and your baseline would be wrong.
2. Update serverless.yml.
service: my-service
-frameworkVersion: "3"
+frameworkVersion: "4.43.0"
provider:
name: aws
+ deploymentMethod: changesets
+build:
+ esbuild: false # only if a plugin or script already builds your code
Pin an exact version, and keep the quotes.
3. Update the pipeline. Add v4 to the project (npm install --save-dev serverless@4), then:
-npm i -g serverless@3
-npm i -g @serverless/compose
-serverless deploy --stage qa --max-concurrency 5
+npm ci
+npx serverless deploy --stage qa
The pipeline’s AWS role reads the key from SSM, just as a developer does. No extra pipeline secret is needed.
4. Update the plugins.
| Plugin | Action |
|---|---|
serverless-plugin-aws-alerts, serverless-python-requirements, serverless-appsync-plugin, serverless-prune-plugin |
Remove: now built into v4 |
serverless-offline |
Upgrade to version 14 |
serverless-s3-sync |
Replace: deprecated and no longer maintained |
serverless-esbuild, serverless-webpack, serverless-plugin-typescript |
Keep only with build.esbuild: false |
serverless-package-external, serverless-plugin-canary-deployments, serverless-plugin-ifelse, serverless-plugin-scripts |
Abandoned: test on a non-production stage that each still does its job |
5. Package with v4 and compare logical IDs. Run serverless logout first, and use the AWS credentials for the stage’s account. This is the first v4 run, so it also tests the licence key: if v4 asks you to sign in, it did not find the key in SSM.
npx serverless package --stage <stage>
cp -r .serverless v4
list() {
jq -r '.Resources | to_entries[]
| select(.value.Type != "AWS::Lambda::Version" and .value.Type != "AWS::ApiGateway::Deployment")
| "\(.key) \(.value.Type)"' "$1" | sort
}
diff <(list v3/cloudformation-template-update-stack.json) \
<(list v4/cloudformation-template-update-stack.json)
This shows the tables are safe instead of assuming it. If the diff prints anything, stop.
Also compare the .zip sizes in v3/ and v4/. A large difference means the code is now being built or bundled differently.
6. Review the change set before you deploy. Create it, read it, then delete it. Don’t execute it. The stack is usually named <service>-<stage>. Find its deployment bucket first:
aws cloudformation describe-stack-resource --stack-name <stack> \
--logical-resource-id ServerlessDeploymentBucket \
--query StackResourceDetail.PhysicalResourceId --output text
If the service sets provider.deploymentBucket, use that bucket instead. Then:
aws s3 cp v4/cloudformation-template-update-stack.json s3://<deployment bucket>/review/template.json
aws cloudformation create-change-set --stack-name <stack> --change-set-name v4-review \
--template-url https://<deployment bucket>.s3.<region>.amazonaws.com/review/template.json \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM
aws cloudformation describe-change-set --stack-name <stack> --change-set-name v4-review \
--query 'Changes[].ResourceChange.[Action,LogicalResourceId,ResourceType,Replacement]' --output table
aws cloudformation delete-change-set --stack-name <stack> --change-set-name v4-review
Stop if a table, bucket, queue, user pool, API or log group shows Remove, or shows Replacement as True or Conditional.
7. Deploy to the non-production stage with npx serverless deploy --stage <stage>. Then call every endpoint and invoke each function once.
8. Promote stage by stage, repeating steps 5 to 7 for each stage.
Day to day for developers
Once the key is in SSM, a developer’s routine barely changes:
git clone <repo> && cd <repo>
npm ci
npx serverless deploy --stage dev
There is no sign-in and no key to copy. v4 reads the licence key from SSM using the developer’s own AWS credentials, the same credentials it already uses to deploy. A few things are different:
- Every command uses the key, not only
deploy.package,info,logs,invokeand local commands such asserverless offlineall check for it. They work as long as the developer’s AWS profile and region point at an account that holds the parameter. - If v4 asks you to sign in, don’t. It means v4 didn’t find the key. Check your AWS profile and region instead.
- Run the project’s install, not a global one.
npx serverlessruns the copy installed in the project, which followsframeworkVersion. An old global v3slsrefuses to deploy a service pinned to v4. The first run downloads the framework, so it needs internet access. - Don’t sign in with a personal account. A signed-in session takes priority over the SSM key, so the deploy is recorded against the developer’s personal organisation instead of yours. Anyone who has ever run
serverless loginshould runserverless logout. - Developers never need to see the key. AWS IAM controls who can read it. When someone leaves, removing their AWS access removes their access to the key too, and there is nothing to collect or rotate.
- Local commands cost nothing. Billing counts deployed services: one service, in one stage, in one region, deployed for more than 10 days in a month. Running
serverless offlineorpackageadds nothing. A personal stage that stays deployed for more than 10 days does count above the free tier, so remove personal stages when you finish with them.
Treat the licence key as a secret. Anyone who has it can run Serverless Framework under your organisation, and their deploys count towards your usage. Keep it in SSM as a SecureString. Never commit it, and never paste it into chat, tickets or CI logs.
Per-developer keys are possible but rarely needed. Credits are counted per deployed service, not per key, so extra keys don’t change the bill. One key per AWS account in SSM is the simplest setup. If someone does need their own key, create a named key for them in the Dashboard, set it as SERVERLESS_LICENSE_KEY on their machine, and revoke it when they leave.
Things that will catch you out
- Unquoted
frameworkVersion: 4is ignored, and the framework updates itself. Always quote it. - A v4
frameworkVersionalso blocks old v3 installs. A global v3 refuses to deploy the service. A v4devDependencyon its own doesn’t stop a global v3. build.esbuild: falseandbuild.esbuild.bundle: falseare different. Only the first turns esbuild off and clears the conflict with a build plugin.- CI jobs that only package now need AWS credentials.
useDotenv: falsenow turns off all.envloading.- Use 4.29.3 or later. Earlier 4.29.x releases have a security flaw (CVE-2025-69256).
Rollback
Revert the upgrade commit, including package.json and the lock file, then deploy with v3: npx serverless@3 deploy --stage <stage>. A failed deploy rolls back on its own.
After the upgrade
- Run
npx serverless@4 usage --org <org>every month, from the first month, even if you pay nothing. It lists the service instances you are billed for.<org>is your organisation’s name in the Dashboard. - After deleting a stack by hand, run
npx serverless@4 reconcile --org <org>in that AWS account so the vendor stops billing for it.
v4 is not the only way out of v3. If its terms or cost don’t suit your project, you can migrate to AWS SAM without losing data. That path needs a post of its own.
Questions and corrections are welcome in the comments.