How To: Optimize the Serverless Optimizer Plugin

I’m sure you’re already well aware of how awesome the ⚡ Serverless Framework is for managing and deploying your serverless applications. And you’re probably aware that there are several great plugins available that make Serverless even better. But did you know that there was a plugin to optimize your functions and reduce the size of your deployment packages? Or are you already using this plugin to optimize your functions, but hate how it takes too long to optimize locally run functions? In this post I’ll share some quick tips to help you optimize your Serverless Optimizer experience.

Introducing the Serverless Optimizer Plugin

The plugin we want to use is actually named “serverless-plugin-optimize”, not to be confused with “serverless-optimizer-plugin”. The latter was the original project that was abandoned a few years ago. Lucky for us, a few guys at Fidel Limited in the UK forked a new version that is still supported. If you aren’t familiar with this plugin, you should take a few minutes to read more about it: https://www.npmjs.com/package/serverless-plugin-optimize.

Why use the Optimizer on our Functions and Packages?

The most important reason to use the optimizer is probably package size. Serverless is great, but unless you specify what to include or exclude in each package, Serverless will bundle everything except development dependencies by default. This means that EVERY function and EVERY node dependency gets added to EVERY package zip. If you have several functions as part of your Serverless project, even if only some have several dependencies or make use of common modules, every deployed package will contain all of those files. This means your package size will be much larger than is needed to run a specific function. This can result in longer cold start times, higher memory usage, and slower deployments.

Another great reason to use the optimizer is for auto-dependency detection and minification of functions. Serverless lets you include/exclude files, but the Optimizer Plugin goes the extra mile and does this for you. Any required files (including local and node modules) will automatically be included along with your main function handler module. It then combines those into one file and minifies it to make the final package size even smaller.

Installing the Optimizer Plugin

Installing the plugin is super easy since it is part of the Serverless plugin repository. Run the following command in your project’s directory:

This will add a dev dependency in your package.json file as well as an entry in your serverless.yml file’s plugins section:

Alternatively, you can install this with a package manager (npm install serverless-plugin-optimize --save-dev) and then the add the entry to the plugins section yourself.

You also need to make sure that you’ve got your package settings set to package functions individually. This can be done by adding the following to your serverless.yml file:

And that’s it! Whenever you deploy, package, or locally invoke your functions, they will be run through the optimizer.

A Quick Example

It probably makes sense to show you a quick example of what the optimizer does. I’ve created a sample Serverless project called “test-optimize” and added two functions to it: func1 and func2. func1 requires the mysql package and func2 requires a tiny local data module from the lib directory.

func1:

func2:

Without the Optimizer plugin, the deployed packages both have the same code size:

If we look at the contents of each package, they also both contain EXACTLY the same files, including an extra func3.js file that isn’t used, the package.json files, and every node module.

test-optimize-dev-func1:

test-optimize-dev-func2:

If we enable to Optimizer and deploy them, we get the following:

Notice how the code sizes are dramatically different. If we look at the contents of each package now, we see that all of the extraneous files are gone AND the necessary dependencies have been included in one, minified file.

test-optimize-dev-func1:

test-optimize-dev-func2:

Not only have we significantly reduced our packages sizes (which makes deployments a lot faster), we removed all unnecessary files. The only drawback is that this will affect dynamic module loading. However, you can use the includePaths configuration for the Optimizer Plugin and include them in your package manually.

Speeding up Development

Once you start using the Optimizer Plugin, you might notice that invoking functions locally takes more time because it has to optimize the function before executing it. Depending on the size of the function, the Optimizer could take several seconds to run. This is less than ideal and may force us to disable the plugin while developing, but then forget to turn it back on.

The solution is to disable the optimizer based on your current stage! You can configure the Optimizer at a global project level by adding an optimize entry in the custom section of your serverless.yml file, but you can also configure the Optimizer for each function by adding an optimize entry under a function. If we set this to false, then the function won’t be optimized. However, we want to do this conditionally based on stage. We can accomplish this by adding a custom variable (we’ll call it enable_optimize) and add an entry for the stage we want to disable (let’s use local):

Then we can add an optimize entry under the functions that we would like to disable optimization per stage. Notice the value is referencing our custom.enable_optimize with the current stage and defaulting to true if it doesn’t exist.

Now invoking the function with the local stage will skip optimizing the function:

However, invoking with the dev stage will:

Now you can run your functions locally without needing to wait for the Optimizer to run every time.

More Serverless Workflow Optimizations

There are a lot of ways to optimize your Serverless workflows. You can read my post: How To: Manage Serverless Environment Variables Per Stage to help you make managing connections to multiple resources much easier.

If you want to save yourself from misspelling stage names, you can check out Serverless Stage Manager. This allows you to restrict the stage names used for full-stack and function deployments.

If you plan on connecting to a database with your serverless functions, you can read How To: Reuse Database Connections in AWS Lambda and How To: Manage RDS Connections from AWS Lambda Serverless Functions.

If you’re new to serverless, or are bit unsure about how to secure your serverless applications, read my post Securing Serverless: A Newbie’s Guide. This will give you an overview of all the things you need to think about to protect your application and customer data.

Finally, if you’re building a REST API with AWS Lambda and API Gateway, check out Lambda API for a blazing fast, built-for-serverless alternative to using other web frameworks like Express, Fastify, Hapi, etc.

Tags: , , ,


Did you like this post? 👍  Do you want more? 🙌  Follow me on Twitter or check out some of the projects I’m working on. You can sign up for my WEEKLY newsletter too. You'll get links to my new posts (like this one), industry happenings, project updates and much more! 📪

Sign Up for my WEEKLY email newsletter


I respect your privacy and I will NEVER sell, rent or share your email address.

3 thoughts on “How To: Optimize the Serverless Optimizer Plugin”

  1. Hi Jeremy,
    I used your config and I’m baffled.

    I’ve got one javascript file with 3 lambda handlers – it’s a rest API code. I also use aws-sdk and aws-xray-sdk.
    After using configuration you described the ‘sls package’ is taking a lot of time on my pretty fast PC.
    $ sls package
    Serverless: Optimize: starting engines
    Serverless: Optimize: platform-dev-subscribe
    Serverless: Optimize: platform-dev-unsubscribe
    Serverless: Optimize: platform-dev-list
    Serverless: Packaging service…
    Serverless: Excluding development dependencies…
    Serverless: Excluding development dependencies…
    Serverless: Excluding development dependencies…

    I’m using Windows 10. Also, I can see sls package command is spinning node process that is using 20 to 50% of my CPU. Is there anyway to force node to use more CPU and finish sooner?

    1. I did some tests. I measured deploy times in 3 configurations, always deploying in ‘dev’ stage:
      1. Plugin enabled, package individually disabled
      2. Plugin enabled, package individually enabled
      3. No plugin at all

      The fastest was the last option.

      ================================================= test 1
      # package:
      # individually: true
      custom:
      enable_optimize:
      local: false
      # dev: false

      > powershell -Command “Measure-Command {npm run deploy-dev}”
      Days : 0
      Hours : 0
      Minutes : 3
      Seconds : 19
      Milliseconds : 205
      Ticks : 1992058211
      TotalDays : 0,00230562292939815
      TotalHours : 0,0553349503055556
      TotalMinutes : 3,32009701833333
      TotalSeconds : 199,2058211
      TotalMilliseconds : 199205,8211

      ================================================= test 2

      package:
      individually: true
      custom:
      enable_optimize:
      local: false
      # dev: false

      > powershell -Command “Measure-Command {npm run deploy-dev}”

      Days : 0
      Hours : 0
      Minutes : 8
      Seconds : 23
      Milliseconds : 231
      Ticks : 5032313335
      TotalDays : 0,00582443673032407
      TotalHours : 0,139786481527778
      TotalMinutes : 8,38718889166667
      TotalSeconds : 503,2313335
      TotalMilliseconds : 503231,3335

      =================================================== test 3 – no plugin

      > powershell -Command “Measure-Command {npm run deploy-dev}”

      Days : 0
      Hours : 0
      Minutes : 2
      Seconds : 48
      Milliseconds : 364
      Ticks : 1683645592
      TotalDays : 0,00194866387962963
      TotalHours : 0,0467679331111111
      TotalMinutes : 2,80607598666667
      TotalSeconds : 168,3645592
      TotalMilliseconds : 168364,5592

  2. Pawel,
    The optimizer plugin does take awhile to run, although, 8+ minutes does seem excessive. I’m curious if it has to do with the extra packaging required for X-Ray. I’ll run some tests and see what I find.

    Unfortunately, there is no way that I know of to split the workload.

    – Jeremy

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.