How to load a pickle file from S3 to use in AWS Lambda?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
AWS Lambda functions frequently need access to serialized Python objects such as machine learning models, preprocessed datasets, or configuration structures stored as pickle files in S3. Because Lambda runs in a temporary, constrained environment, loading these files requires careful handling of memory, deserialization, and caching. This guide walks through the standard approach using boto3 and io.BytesIO, then covers optimization techniques for production workloads.
Basic Approach With boto3 and BytesIO
The most direct method uses boto3 to fetch the object from S3 and io.BytesIO to deserialize it in memory without writing to disk.
The response["Body"] is a streaming object. Calling .read() pulls the entire file into memory as bytes. Then pickle.loads() (note the trailing s for "string/bytes") deserializes those bytes back into a Python object.
You can also use pickle.load() (without the s) with a file-like wrapper:
Both approaches produce the same result. The pickle.loads version is slightly more direct since it skips creating the BytesIO wrapper.
Caching in /tmp for Warm Starts
Lambda provides up to 512 MB of storage in the /tmp directory (configurable up to 10 GB). By saving the pickle file to /tmp on the first invocation and checking for it on subsequent invocations, you avoid redundant S3 downloads during warm starts.
The three-tier caching strategy here checks in this order: Python global variable (fastest), /tmp file (avoids S3 call), and finally S3 (cold start). This makes warm invocations nearly instant for model loading.
Packaging Dependencies in a Lambda Layer
Pickle files created with libraries like scikit-learn or pandas require those libraries at deserialization time. Since Lambda has a 250 MB deployment package limit (unzipped), large dependencies should go into a Lambda Layer.
After publishing, attach the layer to your Lambda function. The dependencies become available at import time without inflating your function's deployment package.
Memory and Timeout Considerations
Pickle deserialization loads the entire object into memory. A 100 MB pickle file may require 300 to 500 MB of memory once deserialized, because the in-memory representation of Python objects is larger than their serialized form.
Set your Lambda memory allocation to at least two to three times the pickle file size. Also set the timeout generously for cold starts. A function that downloads and deserializes a 200 MB model may need 30 seconds or more on its first invocation.
Common Pitfalls
- Running out of memory during deserialization: The deserialized Python object is significantly larger than the pickle file on disk. A 50 MB file can easily require 200 MB or more of Lambda memory. Always test with realistic data and monitor the "Max Memory Used" metric.
- Forgetting that
/tmpis not persistent across cold starts: Files saved to/tmpsurvive across warm invocations of the same container, but a cold start gives you a fresh/tmp. Your code must always handle the case where the cached file does not exist. - Pickle version or library mismatch: If you serialize with Python 3.11 and scikit-learn 1.3 but your Lambda runs Python 3.9 with scikit-learn 1.1, deserialization fails with cryptic errors. Pin your library versions to match exactly between the serialization and Lambda environments.
- Loading the model inside the handler on every invocation: Without global caching, every single request pays the full S3 download and deserialization cost. Move the loading logic outside the handler or use a global cache variable as shown above.
- Not setting a sufficient Lambda timeout: Cold-start downloads from S3 can take several seconds for large files. The default 3-second timeout causes the function to be killed before loading completes. Set the timeout to 30 seconds or more for functions that load large pickle files.
Summary
- Use
boto3s3_client.get_object()to fetch the pickle file andpickle.loads()orpickle.load(io.BytesIO(...))to deserialize it. - Cache the deserialized object in a global variable and the raw file in
/tmpto avoid repeated S3 downloads on warm starts. - Package large dependencies like scikit-learn or pandas in a Lambda Layer to stay within the 250 MB deployment limit.
- Allocate two to three times the pickle file size in Lambda memory, since in-memory Python objects are larger than their serialized form.
- Pin Python and library versions to match exactly between the environment where you create the pickle and the Lambda runtime.

