"How do I get a local S3?" usually ends with one of two answers: mock the calls with moto, or run an AWS-only emulator. Both work for simple cases — but a mock re-implements S3's behavior, and the gaps (ETags, multipart, list pagination, presigned URLs) are exactly where bugs hide. The alternative is to run real object storage locally and talk to it with the standard SDK.
Real backend, standard SDK
Vyomi serves the S3 API from MinIO — actual object storage, not an in-memory imitation — so the semantics match production. You change one thing: the endpoint.
import boto3
s3 = boto3.client("s3", endpoint_url="http://localhost:9000",
aws_access_key_id="test", aws_secret_access_key="test",
region_name="us-east-1")
s3.create_bucket(Bucket="demo")
s3.put_object(Bucket="demo", Key="report.csv", Body=b"a,b,c\n1,2,3\n")
obj = s3.get_object(Bucket="demo", Key="report.csv")
print(obj["ETag"], obj["Body"].read())
The things mocks get wrong
- ETags — real storage returns content-derived ETags; code that compares them for change-detection breaks against a mock that fakes them.
- List pagination —
list_objects_v2withContinuationTokenbehaves like the real thing, so your pagination loop is actually exercised. - Presigned URLs — generate one locally and it's a real, working URL against the local endpoint:
url = s3.generate_presigned_url("get_object",
Params={"Bucket": "demo", "Key": "report.csv"}, ExpiresIn=3600)
# fetch `url` with plain requests — no SDK needed
Same code, three clouds
Because the endpoint is the only change, the identical pattern gives you a local
GCS bucket (google-cloud-storage) and a local Azure Blob container
(azure-storage-blob) from the same appliance. That's the line between a
single-cloud emulator and a multi-cloud digital twin.
New to this? Start with How to run AWS locally, or see how the options stack up in the emulator comparison.