AWS
S3
Pickle
Python
Cloud Storage

Writing a pickle file to an s3 bucket in AWS

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

Writing a pickle file to Amazon S3 is a common Python workflow when you need to store trained models, cached objects, or intermediate analysis results. The simplest approach is to serialize the object to bytes with pickle and upload those bytes with boto3. The main thing to remember is that pickle is a Python-specific format and should only be loaded from trusted sources.

Serialize to Bytes First

You do not need to write a temporary file to disk unless you want one. In many cases, it is cleaner to pickle the object in memory and upload the resulting bytes directly.

python
1import pickle
2
3payload = {
4    "model_name": "baseline",
5    "accuracy": 0.91,
6    "features": ["age", "income", "score"],
7}
8
9pickled_bytes = pickle.dumps(payload)
10print(len(pickled_bytes))

pickle.dumps returns a bytes object, which is exactly what S3 upload methods can accept.

Upload Directly with put_object

For small and medium payloads, put_object is the most direct way to send the pickled bytes to S3.

python
1import boto3
2import pickle
3
4s3 = boto3.client("s3", region_name="us-east-1")
5
6data = {
7    "model_name": "baseline",
8    "accuracy": 0.91,
9    "features": ["age", "income", "score"],
10}
11
12body = pickle.dumps(data)
13
14s3.put_object(
15    Bucket="my-example-bucket",
16    Key="artifacts/model.pkl",
17    Body=body,
18    ContentType="application/octet-stream",
19)

This uploads the serialized object to s3://my-example-bucket/artifacts/model.pkl. The content type is optional, but setting it makes the object metadata clearer.

Use BytesIO with upload_fileobj

If you prefer a file-like upload API, wrap the bytes in io.BytesIO. This is useful when reusing code paths that already expect a stream.

python
1import io
2import pickle
3import boto3
4
5s3 = boto3.client("s3")
6
7data = ["alpha", "beta", "gamma"]
8buffer = io.BytesIO()
9pickle.dump(data, buffer)
10buffer.seek(0)
11
12s3.upload_fileobj(buffer, "my-example-bucket", "exports/list.pkl")

The result is the same. The difference is just the interface you use to send the content.

Read the Pickle Back

Testing the round trip is a good idea, especially when the object will be consumed by another job later.

python
1import boto3
2import pickle
3
4s3 = boto3.client("s3")
5
6response = s3.get_object(Bucket="my-example-bucket", Key="artifacts/model.pkl")
7body = response["Body"].read()
8loaded = pickle.loads(body)
9
10print(loaded["model_name"])

This confirms that the object was stored and can be deserialized correctly. It also highlights the trust model: pickle.loads executes Python deserialization logic and should never be used on untrusted data.

Credentials and Configuration

The upload code assumes boto3 can already find AWS credentials. The usual options are:

  • environment variables
  • the shared AWS credentials file
  • an IAM role attached to the runtime environment

Hardcoding access keys in the script is the wrong approach. If the code runs in AWS, using an IAM role is usually the cleanest option.

It is also worth being explicit about the bucket region when the environment is complex. Region mismatches are a common source of confusion during development.

When Pickle Is a Bad Storage Format

Pickle is convenient, but it is not portable in the way JSON, CSV, or Parquet are. It is most appropriate when:

  • both writer and reader are Python
  • the object structure is complex
  • exact Python object reconstruction matters

It is less appropriate when the data must be inspected manually, shared across languages, or loaded from untrusted sources. In those cases, another format is often safer and easier to operate.

Add Basic Error Handling

S3 uploads can fail because of missing credentials, denied permissions, network problems, or incorrect bucket names. Handle those failures close to the upload.

python
1import boto3
2import botocore
3import pickle
4
5s3 = boto3.client("s3")
6
7try:
8    s3.put_object(
9        Bucket="my-example-bucket",
10        Key="artifacts/safe.pkl",
11        Body=pickle.dumps({"status": "ok"}),
12    )
13except botocore.exceptions.BotoCoreError as exc:
14    print(f"S3 upload failed: {exc}")

This keeps the failure visible instead of silently dropping important artifacts.

Common Pitfalls

  • Writing a temporary local file when an in-memory upload would be simpler.
  • Loading pickle data from an untrusted source and creating a security risk.
  • Hardcoding AWS credentials into application code.
  • Forgetting to rewind a BytesIO buffer before calling upload_fileobj.
  • Choosing pickle for data that really should be stored in a language-neutral format.

Summary

  • Serialize the object with pickle.dumps or pickle.dump before uploading to S3.
  • 'put_object is the simplest direct upload path for bytes.'
  • 'upload_fileobj works well with BytesIO if you want a stream-based API.'
  • Only unpickle trusted data.
  • Let boto3 use proper AWS credentials from the environment or IAM roles.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.