---
title: "Amazon S3 Integration with Pixeltable: Media URIs and Destinations"
description: "Insert s3:// URIs into Pixeltable media columns, cache on access, write computed media to a bucket, and mint presigned HTTP URLs."
tool: "Amazon S3"
keywords:
  - Amazon S3 Pixeltable integration
  - s3:// Pixeltable media
  - PIXELTABLE_OUTPUT_MEDIA_DEST
  - presigned URL Pixeltable
  - S3 computed media
url: "https://www.pixeltable.com/integrations/amazon-s3"
---

# Amazon S3 Integration with Pixeltable: Media URIs and Destinations

Insert s3:// URIs into Pixeltable media columns, cache on access, write computed media to a bucket, and mint presigned HTTP URLs.

## The Problem

Media already lives in S3. A pipeline that downloads the bucket into a working directory before it can run a model copies bytes you did not need yet. A pipeline that only stores the URI then cannot resize, transcribe, or sign a URL without a second client. Pixeltable media columns accept s3://, https://, and local paths. The integration is that URI as the stored value, on-demand cache under PIXELTABLE_HOME/file_cache, optional destinations so computed images land in a bucket, and pxtf.net.presigned_url when a browser needs HTTP. Hosted Cloud tables already write to pxt://org:db/buckets/home; this page is the bring-your-own S3 path.

## The Solution

Insert s3://bucket/key into a pxt.Video or pxt.Image column. Pixeltable stores the URI and downloads when a query or computed column reads the file. AWS credentials follow the usual chain: AWS_ACCESS_KEY_ID, ~/.aws/credentials, or an IAM role. Optional s3_profile in config.toml. For computed media, set PIXELTABLE_OUTPUT_MEDIA_DEST=s3://my-bucket/output/ before creating the table, or pxt.Column(..., destination=...) on that field. presigned_url(uri, expiration_seconds) turns a storage URI into a time-limited HTTP URL. HTTP URLs pass through unchanged. Cloud tables do not need this destination; they already use the managed home bucket.

## Implementation

### Insert s3:// URIs as media columns

Store the URI. The object is fetched and cached when a query or computed column needs the bytes.

```python
import pixeltable as pxt

TableModel = pxt.model_base()

class Videos(TableModel, name='videos'):
    video: pxt.Video

# pxt schema update app.py s3_media -f
videos = pxt.get_table('s3_media.videos')
s3_prefix = 's3://multimedia-commons/'
videos.insert([
    {'video': s3_prefix + 'data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4'},
])
videos.collect()
```

The multimedia-commons prefix is the public bucket used in the docs. Private buckets need the AWS credential chain. The same insert shape works with https:// and local paths. Cache lives under ~/.pixeltable/file_cache/ by default.


### Write computed media to a bucket

Set a default output destination, or override one computed column.

```python
# export PIXELTABLE_OUTPUT_MEDIA_DEST=s3://my-app-bucket/generated/
# export PIXELTABLE_INPUT_MEDIA_DEST=s3://my-app-bucket/uploads/
# Optional: s3_profile in ~/.pixeltable/config.toml

import pixeltable as pxt

TableModel = pxt.model_base()

class Photos(TableModel, name='photos'):
    photo: pxt.Image
    thumbnail = pxt.Column(
        value=photo.resize((256, 256)),
        destination='s3://my-app-bucket/thumbnails/',
    )

# pxt schema update app.py s3_photos -f
```

Configure destinations before creating the table. destination on pxt.Column applies to stored computed columns only. Input columns use PIXELTABLE_INPUT_MEDIA_DEST or db_input_media_dest. Hosted Cloud tables already write to pxt://org:db/buckets/home; leave dest unset there unless you want a bucket of your own.


### Mint a presigned HTTP URL

Convert a storage URI into a time-limited URL a browser or player can GET.

```python
import pixeltable.functions as pxtf

class Videos(TableModel, name='videos'):
    video: pxt.Video
    serving_url = pxt.Column(
        value=pxtf.net.presigned_url(video.fileurl, 86400),
        stored=False,
    )

# pxt schema update app.py s3_serve -f
```

fileurl is the stored URI. expiration_seconds is how long the HTTP URL remains valid, so the column is not stored: each read signs a fresh URL. GCS caps expiration at 7 days. Local file:// URIs raise. HTTPS inputs pass through unchanged because they are already public.


### Point credentials at the bucket

Use the AWS chain locally, and Pixeltable Secrets on Cloud.

```bash
# Local / IAM
export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
# or ~/.aws/credentials, or an instance role

# Pixeltable Cloud: store those names under Secrets (or pxt secret set),
# not in PIXELTABLE_API_KEY. PIXELTABLE_API_KEY is the Cloud credential.

# Needed S3 actions: s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket
```

R2, B2, and Tigris use AWS-style keys plus a profile in config.toml (r2_profile, b2_profile, tigris_profile). GCS is gs:// plus Application Default Credentials. Azure is wasb[s]:// or abfs[s]:// plus an account name and key.


## Download-then-process versus URI columns

| Job | Copy the bucket first | s3:// on the column |
| --- | --- | --- |
| Ingest | aws s3 sync into a disk | Insert the URI |
| First model call | After the copy finishes | On access; cache keeps the file |
| Generated stills | A put you write | destination= or OUTPUT_MEDIA_DEST |
| Browser GET | A signing helper | presigned_url on fileurl |

## Benefits

- No bulk download before the first computed column runs
- Same Image, Video, and Audio types for s3://, https://, and local paths
- Computed thumbnails can land in a prefix you choose
- presigned_url is a column, not a signing script
- Cloud home bucket stays the default when dest is unset

## Use Cases

- Transcribe or embed objects that already live in a bucket
- Write generated images to s3://.../generated/
- Serve private media to a player without exposing keys
- Point local Pixeltable at a Cloud home bucket with pxt://org:db/buckets/home
- GCS, R2, B2, or Tigris with the same destination fields

## Performance


| Metric | Value | Description |

| --- | --- | --- |

| Stored value | URI | The object is fetched on first access, then cached under PIXELTABLE_HOME |

| Computed media | Optional dest | Unset dest on Cloud uses the managed home bucket; local default is disk |

| HTTP for browsers | presigned_url | A time-limited GET URL; HTTPS inputs are returned unchanged |

## Requirements

- Python 3.11+
- pip install -U 'pixeltable[serve]'
- AWS credentials with GetObject, and PutObject when dest is S3
- PIXELTABLE_OUTPUT_MEDIA_DEST set before table create when computed media should land in S3

## Resources

- [Load media from S3](https://docs.pixeltable.com/howto/cookbooks/data/data-import-s3) - URI insert, cache, and presigned URLs
- [Cloud Storage](https://docs.pixeltable.com/integrations/cloud-storage) - Destinations, providers, and precedence
- [S3, Hugging Face, and Pixeltable storage](https://www.pixeltable.com/blog/s3-files-huggingface-buckets-pixeltable-storage) - Product write-up of files and buckets