ZeroBucket
Your database. Your images. Zero buckets.
ZeroBucket is a database-native image storage library. It lets you store and retrieve images using the PostgreSQL database you already have, instead of standing up a separate object-storage service like S3.
from zerobucket import ZeroBucket
images = ZeroBucket(database_url="postgresql://...")
image_id = images.put("avatar.jpg")
image = images.get(image_id)
print(image.mime_type) # "image/jpeg"
print(image.size_bytes) # 1116478
print(image.data) # raw bytes, ready to serve
Full documentation, architecture notes, and benchmark results live in the GitHub repository.
Installation
pip install zerobucket
Requires Python 3.10+ and PostgreSQL 13+ (uses gen_random_uuid(), built
in since Postgres 13).
Quick reference
| Method | What it does |
|---|---|
images.put(image, filename=None) |
Validates, checksums, and stores an image. Accepts a file path, raw bytes, or a file-like object (including framework upload objects). Returns the new image's id. |
images.get(image_id) |
Returns an Image (data, mime_type, filename, width, height, size_bytes, checksum_sha256). Raises ImageNotFoundError if missing. |
images.metadata(image_id) |
Same fields as get() but without the raw bytes — cheap existence/info check. |
images.exists(image_id) |
Returns True/False. |
images.delete(image_id) |
Deletes the image. Returns True if it existed. |
images.close() |
Releases database connections. ZeroBucket also works as a context manager. |
from zerobucket import ZeroBucket, ImageNotFoundError, ImageValidationError
images = ZeroBucket(
database_url="postgresql://user:pass@localhost/mydb",
max_bytes=8 * 1024 * 1024, # default: 8MB
)
image_id = images.put("photo.jpg")
image_id = images.put(open("photo.jpg", "rb"))
image_id = images.put(request.files["avatar"]) # framework upload objects
image = images.get(image_id)
info = images.metadata(image_id)
try:
images.get("nonexistent-id")
except ImageNotFoundError:
...
try:
images.put("not-actually-an-image.txt")
except ImageValidationError:
...
Serving from a web API
from flask import Flask, Response
app = Flask(__name__)
images = ZeroBucket(database_url=DATABASE_URL)
@app.route("/images/<image_id>")
def serve_image(image_id):
image = images.get(image_id)
return Response(image.data, mimetype=image.mime_type)
What it validates
- Format: JPEG, PNG, WebP — detected from actual file content, never
from filename extension or a client-supplied
Content-Typeheader. - Corruption: truncated or malformed images are decoded and rejected before they reach the database.
- Decompression bombs: a tiny compressed file that decodes to an enormous pixel grid is rejected, not silently allocated.
- Size: configurable via
max_bytes(default 8MB) — see the benchmark results for why.
Limitations (read before using in production)
- Not built for large files or high-volume media. Full images are read into memory on both ends of every request — no streaming, no range requests, no CDN.
- No deduplication yet. A SHA-256 checksum is stored on every row, but duplicate uploads currently create duplicate rows.
- No resize/optimization pipeline yet. ZeroBucket validates and stores what you give it; it doesn't transform it.
- PostgreSQL only, for now. The storage layer is abstracted for future adapters, but only Postgres exists today.
See the full README and roadmap on GitHub for more detail, including why BYTEA storage doesn't compress image bytes any further than they already are.
License
MIT — see LICENSE.
Release files for zerobucket 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| zerobucket-0.1.1.tar.gz | 12.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| zerobucket-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.4 kB
Release files / zerobucket-0.1.1.tar.gz
| Download URL | zerobucket-0.1.1.tar.gz |
|---|---|
| Size | 12.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f2e99841cc8c07c2fa091251e37c92a94c9958450a6c798efb483ade8d07d91
|
|
BLAKE2b-256 checksum How to use checksums |
701a724a8b2679e10fa98730b1560b622c9d45f006ade135716e0bb07b009f3f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|
Release files / zerobucket-0.1.1-py3-none-any.whl
| Download URL | zerobucket-0.1.1-py3-none-any.whl |
|---|---|
| Size | 12.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
082f346b254da34470092243c7aa1fcf119e6e85af71413593f2b40b730c133c
|
|
BLAKE2b-256 checksum How to use checksums |
61d5127dc29b970930ee14427ed4b9c55ba978e3cc3a8eee5272ba28be4a4b9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.9
|