GCS Media Storage

Store agent media in a Google Cloud Storage bucket with GCSMediaStorage.

GCSMediaStorage uploads media to a Google Cloud Storage bucket and keeps only a MediaReference in the database.

Usage

Install the required packages:

uv pip install "agno[gcs]" openai sqlalchemy

Authenticate with gcloud auth application-default login, or pass a service-account JSON through credentials_path.

Choose an existing bucket and credentials authorized to read, write, and delete its objects. The backend does not create the bucket. Set the bucket name before running Python and set the model key before any Agent run:

export MEDIA_GCS_BUCKET="your-existing-bucket"
export OPENAI_API_KEY="your-api-key"
media_storage_gcs.py
import os

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.media.storage import GCSMediaStorage
from agno.models.openai import OpenAIResponses

storage = GCSMediaStorage(
    bucket=os.environ["MEDIA_GCS_BUCKET"],
    project=os.getenv("GCP_PROJECT"),
    prefix="agno/media/",
)

agent = Agent(
    model=OpenAIResponses(id="gpt-5.5"),
    db=SqliteDb(db_file="tmp/data.db"),
    media_storage=storage,
)

Async

AsyncGCSMediaStorage takes the same parameters. google-cloud-storage has no native async API, so each call runs in a worker thread.

from agno.media.storage import AsyncGCSMediaStorage

storage = AsyncGCSMediaStorage(bucket=os.environ["MEDIA_GCS_BUCKET"])

URLs

Signing a URL needs a credential that carries a private key. Pass credentials_path, or point GOOGLE_APPLICATION_CREDENTIALS at a service-account JSON.

storage = GCSMediaStorage(
    bucket=os.environ["MEDIA_GCS_BUCKET"],
    credentials_path="/path/to/service-account.json",
)

Application-default credentials from gcloud auth or a VM carry only a token. With those, get_url returns None and AgentOS streams the bytes through its media route.

public=True returns the public object URL instead of a signed one. It grants no access by itself: when uniform bucket-level access is enabled, the bucket must already grant roles/storage.objectViewer to allUsers or the URL answers 403.

Bucket access defaults depend on how the bucket was created and organization policy. Check the bucket's setting; the gcloud creation command exposes it explicitly.

Params

ParameterTypeDefaultDescription
bucketstr-Name of the destination GCS bucket.
prefixstr"agno/media/"Key prefix for stored objects.
credentials_pathOptional[str]NonePath to a service-account JSON file. Supplies the private key used to sign URLs, as does GOOGLE_APPLICATION_CREDENTIALS.
projectOptional[str]NoneGCP project ID.
presigned_url_expiryint3600Signed URL lifetime in seconds. Above the V4 maximum of seven days, signing fails and URLs fall back to streaming.
publicboolFalseReturn the public object URL instead of a signed one. Grants no access by itself.
persist_remote_urlsboolFalseDownload and store media passed as a URL. Skipped when False.

See the full example here.