Boto3
S3
AWS
Python
Cloud Storage

How to close Boto S3 connection?

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

With boto3, S3 does not behave like a traditional database connection that you open, use, and close on every request. The client reuses HTTP connections internally, so the real question is when to release client resources and when to close streaming responses such as get_object.

How boto3 manages S3 connections

When you create an S3 client, botocore builds an HTTP-backed client object that handles authentication, retries, and connection pooling. In a short script, it is often fine to create the client, use it, and let the process exit naturally.

Here is a minimal example:

python
1import boto3
2
3session = boto3.session.Session(region_name="us-east-1")
4s3 = session.client("s3")
5
6response = s3.list_buckets()
7for bucket in response["Buckets"]:
8    print(bucket["Name"])

This code works without any explicit cleanup, but long-running applications should think more carefully about resource lifetime.

Session, client, and resource are not the same thing

It helps to separate the three layers in the SDK:

  • a Session stores configuration such as region and credentials lookup
  • a client performs low-level API calls such as put_object and get_object
  • a resource offers a higher-level object interface built on top of clients

When people ask how to "close the S3 connection," they are usually talking about the client or an active response stream. The session itself is mostly configuration state, not a persistent socket that needs constant manual shutdown.

Closing the client explicitly

If you want deterministic cleanup, call close() when the client is no longer needed:

python
1import boto3
2
3session = boto3.session.Session(region_name="us-east-1")
4s3 = session.client("s3")
5
6try:
7    response = s3.list_buckets()
8    for bucket in response["Buckets"]:
9        print(bucket["Name"])
10finally:
11    s3.close()

That releases the HTTP resources owned by the client. It is a good pattern for scripts or jobs with a clear end of life.

Closing object streams matters more

The most common place developers forget cleanup is not the client itself, but the streaming body returned by get_object. If you download an object as a stream, close that body after reading it:

python
1import boto3
2
3s3 = boto3.client("s3", region_name="us-east-1")
4response = s3.get_object(Bucket="example-bucket", Key="report.txt")
5body = response["Body"]
6
7try:
8    content = body.read().decode("utf-8")
9    print(content)
10finally:
11    body.close()
12    s3.close()

If the body stays open, the underlying HTTP connection can remain occupied longer than necessary.

Reuse clients in real applications

In most applications, the best pattern is to create the client once and reuse it rather than opening and closing it for every operation:

python
1import boto3
2
3s3 = boto3.client("s3", region_name="us-east-1")
4
5
6def upload_text(bucket: str, key: str, content: str) -> None:
7    s3.put_object(
8        Bucket=bucket,
9        Key=key,
10        Body=content.encode("utf-8"),
11        ContentType="text/plain",
12    )
13
14
15upload_text("example-bucket", "notes/hello.txt", "hello from boto3")

This pattern is common in web services, background workers, and AWS Lambda functions. Reuse is faster and creates less connection churn.

When explicit closure is optional

For a short command-line script, explicit closure is often mostly about clarity and neat cleanup. The operating system will reclaim resources when the process exits. The cases where manual cleanup matters most are:

  • loops that create many clients
  • long-running server processes
  • code paths that stream data from S3

Those are the situations where poor cleanup becomes visible as wasted sockets or unnecessary resource pressure.

Common Pitfalls

  • Creating a new S3 client inside every helper function instead of reusing one.
  • Forgetting to close response["Body"] after get_object.
  • Expecting close() to fix permission, region, or credential errors.
  • Treating an S3 client like a stateful database connection that must be reopened constantly.

Summary

  • 'boto3 S3 clients use HTTP connection pooling under the hood.'
  • Call client.close() when you want deterministic cleanup at the end of a client lifecycle.
  • Always close streaming bodies returned by operations such as get_object.
  • In long-running applications, reuse S3 clients instead of recreating them repeatedly.

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.