Reduce Lambda package size: 7 ways to cut deployment time and cost

Lambda cold starts are painful. Slow cold starts are usually fat packages. Your function downloads 50MB from S3, unpacks it, loads dependencies, initialises runtime. Meanwhile your user is waiting. An invocation that should take 100ms takes 5 seconds.

Kill package bloat. Most Lambda packages are at least 50% larger than they need to be. Unused dependencies, development files, test suites. All bundled for production. Stop doing that.

1. Audit and remove dependencies

Start here. It's the easiest win. Look at your dependencies. Half of them probably aren't used.

For Node.js, run npm ls. Look at what you're importing. Can you delete something? Do you really need the full lodash library for a single utility function? Did you install express and never refactor away from it?

For Python, use pipdeptree or depcheck. Find unused packages. Delete them.

Do this carefully. Some packages are dependencies of dependencies. Removing pandas removes the functions that use it. Test locally after.

Once you've cut obvious dead weight, look harder. Can you replace a bulky library with something smaller? Do you really need moment.js for date handling? Built-in Date object works fine. Do you need axios for HTTP when fetch is built-in?

A typical Lambda function can drop 30-50% by removing dead dependencies.

2. Use Lambda Layers

Lambda Layers keep large dependencies separate from your code. You package your function code in one place, shared libraries in another.

Create a layer. Put AWS SDK, pandas, numpy, whatever. Upload once. Five functions reference it. Update once. All five use the new version.

Layers work especially well for data science functions. Pandas alone is 30-80MB. Put it in a layer. Function code shrinks from 85MB to 3MB.

Downside: layers add complexity. You need to manage versions. Track which layer versions work with which function versions. Use VCS to track layer definitions alongside function code.

Layers are region-specific. Deploy your function to us-west-2? Deploy your layers there too.

Start with one layer: shared dependencies. Once that's working, add more if needed.

3. Minify and compress code

For JavaScript, use Terser or UglifyJS. Webpack does this automatically. Strips comments, renames variables, removes unused code. Reduces size 20-40%.

For Python, there are tools but they're less commonly used. Simple approach: use a build step that removes docstrings and comments before packaging. Saves 15-25%.

Compress everything with gzip. AWS Lambda decompresses on upload automatically. Add 60-80% more size reduction.

Takes 5 minutes to set up. Usually saves 10-20% of package size.

4. Remove non-code files

README files. Test suites. Configuration files. Hidden git folders. Sample data. None of this runs in production.

Create an exclusion list. .npmignore for Node.js, exclude patterns in serverless.yml for Serverless Framework. Be explicit about what you're keeping, not what you're cutting.

Common exclusions:

  • tests/**
  • *.md
  • .git
  • pycache
  • .pytest_cache
  • node_modules/.bin
  • docs/**

If you're automating deployment, use build scripts. Copy only production files to a staging directory. Package that.

Can shrink packages 20-30%.

5. Package each function separately

Don't bundle ten functions into one package. Package each one individually with only its dependencies.

A webhook function doesn't need data science libraries. An API function doesn't need the full application. Each function gets smaller.

Use AWS SAM or Serverless Framework. They support this natively.

Downside: increases deployment complexity. You now have ten deployment artifacts instead of one. CI/CD gets more involved.

Benefit: smaller packages, faster cold starts, cleaner code.

6. Use lighter alternatives

Don't always reach for the massive library.

Do you need Lodash or just a utility? Write it yourself. It's three lines.

Do you need Moment.js or just the built-in Date object? Use Date.

Do you need a full ORM or just a SQL builder? Use the builder.

Replace axios with fetch. Replace jQuery with vanilla JS (if still using it). Use compiled languages where possible. A Rust function is 10MB compressed. A JavaScript equivalent is 50MB.

Trade-off: you lose features, you gain size.

7. Use Provisioned Concurrency or SnapStart

If size optimization isn't enough, use Provisioned Concurrency to keep instances warm. No cold start means big packages don't matter.

Costs money (roughly £0.015 per concurrency-hour) but eliminates cold start problems.

SnapStart (Java only) takes a snapshot of the initialised JVM. Reuses the snapshot instead of starting from scratch. Major speed improvement.

Use this when package optimization has hit limits or size reduction is complex.

Real numbers

Start: 85MB package

Remove dependencies: 52MB (39% reduction)

Minify and compress: 18MB (65% reduction from original)

Remove non-code: 12MB (86% reduction)

Use layers: 2MB function + 80MB layer (parallel, not sequential)

Small package means:

  • Faster deployment
  • Faster cold starts
  • Faster updates
  • Lower transfer costs

Where Critical Cloud comes in

Cold start optimization only matters if you measure it. You need to know: how long are cold starts actually taking? Has my optimization helped? Is it still hurting?

We're a Powered by Datadog accredited partner. We measure Lambda cold starts, package size, memory usage, everything. You get visibility into whether your optimization actually worked.

If Lambda cold starts are a problem or you're not sure where time is being spent, see how Critical Support works.