Skip to main content
Bifrost persists two types of data - config (providers, virtual keys, governance rules) and logs (request/response records). Each has its own store. A vector store is required for semantic caching.
If you use PostgreSQL for any store, the target database must be UTF8 encoded. See PostgreSQL UTF8 Requirement.

config_store

When config_store is omitted, Bifrost creates a default SQLite config store in the app directory. Set config_store.enabled to false only when you want file-only configuration with no config-backed Web UI/API edits. See Source of Truth & Reconciliation.

SQLite (Default)

Simplest setup - no external database required. Bifrost stores configuration in a local SQLite file.

PostgreSQL

Production-grade storage suitable for high-availability and high-throughput deployments.
Use password_command for short-lived database credentials such as AWS RDS IAM auth tokens:

Disabled (file-only mode)

Use this when you want Bifrost to read all configuration from config.json only - no configuration database and no config-backed Web UI/API edits.
This is the recommended setup for multinode OSS deployments where a shared config.json is the single source of truth.

logs_store

SQLite

PostgreSQL

logs_store supports the same dynamic PostgreSQL credential fields as config_store: For high log volumes, increase max_open_conns:

ClickHouse

A column-oriented backend built for high-volume log ingestion and fast analytical queries. Best suited for large-scale deployments where log throughput and dashboard query performance on big time ranges matter more than operational simplicity.
ClickHouse is a logs_store-only backend. The config_store supports only sqlite and postgres — pair a ClickHouse logs store with a SQLite or PostgreSQL config store (see Mixed Backend Examples).
host is the only required field; the rest have sensible defaults. TLS + HTTP protocol against a managed ClickHouse (e.g. ClickHouse Cloud):
Clustered ClickHouse — set cluster so tables are created with replicated engines across the cluster:
With ClickHouse, retention_days is enforced by a native table TTL rather than a background delete job. Setting it to 0 (or omitting it) leaves the TTL unset, so ClickHouse itself never expires rows. Background cleanup is controlled separately by client_config.log_retention_days, so set that to your desired horizon as well. matview_refresh_interval and matview_refresh_timeout do not apply — they are PostgreSQL-only settings for materialized views.

Disabled

Log Retention

Set retention_days to automatically purge old log entries. 0 disables retention-based cleanup.

Materialized View Refresh Interval (PostgreSQL only)

The PostgreSQL logs store backs the dashboard’s stats and histograms with materialized views, refreshed in the background. The default cadence is 1 minute, which keeps dashboard data near real-time but issues a REFRESH MATERIALIZED VIEW CONCURRENTLY every minute — an expensive operation that can be too aggressive on smaller or CPU-constrained database instances. Set matview_refresh_interval (Go duration string) to slow down refreshes when near-real-time accuracy isn’t critical:
Notes
  • Refreshes are already activity-gated: when no INSERT/UPDATE/DELETE has hit the logs table since the last refresh, the scheduled tick short-circuits without touching the views. So idle clusters don’t pay for the configured cadence — they only pay when there’s actual log activity.
  • Dashboard freshness lag will be at most the configured interval. Stats and histograms over the last 24 hours come straight from the raw logs table (no matview), so short-window dashboards stay real-time regardless of this setting.
  • A 10-minute safety-net refresh runs even on totally idle clusters so the rolling 30-day filter dropdown window evicts aged-out values.
When to raise it:
  • Your database instance is CPU-constrained and matview refreshes are showing up as a hot consumer.
  • Your team mostly looks at multi-day trends, not minute-by-minute dashboards.
When to leave it at the default:
  • The database has consistent CPU headroom.
  • Operators rely on near-real-time dashboards (e.g. live incident triage).
When to turn it off:
  • You don’t use the Bifrost dashboard (e.g. Bifrost runs headless behind your own observability stack). With "off", the views are neither created nor refreshed, and any dashboard query transparently uses the raw tables.

Object Storage for Logs

Offload LLM request/response logs and MCP tool logs from the database to S3 or GCS. The database retains lightweight index records and fetches full payloads on demand. For MCP logs, the full tool log is stored in object storage and the database keeps dashboard/table fields plus a 200-character input preview.

AWS S3

Required IAM permissions The IAM user or role needs the following permissions on your bucket:
IAM role (instance profile / IRSA) - omit access_key_id and secret_access_key:
  1. Attach this IAM policy to whichever AWS principal Bifrost authenticates as: the IAM user behind access_key_id/secret_access_key, or the IAM role behind role_arn:
  1. If you’re using a customer-managed key (not the AWS-managed aws/s3 key), it also needs permission granted separately on the key’s own policy (in the KMS console). Add that same IAM user or role ARN there too (AWS guide). AWS-managed keys don’t allow their key policy to be edited, so this step doesn’t apply if you’re using the default aws/s3 key.
Default encryption applies KMS without requiring encryption headers from the uploader. Bifrost’s IAM identity still needs the KMS permissions above regardless of encryption mode. If instead your bucket policy denies uploads that don’t include the encryption header, note that Bifrost does not send that header, so uploads will fail under that policy.

Google Cloud Storage

Omit credentials_json to use Application Default Credentials (Workload Identity, GCE metadata, gcloud auth).

MinIO (Self-Hosted)


vector_store

A vector store is required for semantic caching. Choose from Weaviate, Redis/Valkey, Qdrant, or Pinecone.

Weaviate

Redis / Valkey

AWS MemoryDB (cluster mode):

Qdrant

Pinecone

Pinecone is external-only.

Mixed Backend Examples

Each store is configured independently, so you can run the config store and logs store on different backends — or even different database instances. This is useful when config and logs have different scaling, cost, or retention profiles.
In config.json each store carries its own config block, so the two stores can point at entirely separate hosts. The Helm chart shares one PostgreSQL connection across both stores by default; set storage.logsStore.postgres.enabled: true to point the logs store at a separate PostgreSQL instance. See Separate PostgreSQL for Logs.

Config on PostgreSQL #1, Logs on PostgreSQL #2

Keep configuration on a small, highly-available Postgres while sending high-volume logs to a separate Postgres instance sized for write throughput — so log traffic never competes with config reads:

Config on PostgreSQL, Logs on ClickHouse

Run configuration on PostgreSQL (transactional, backs the Web UI) while sending logs to ClickHouse for high-volume ingestion and fast analytics. ClickHouse is a logs-store-only backend, so this pairing is the recommended shape for analytics-heavy deployments:

Full Storage Example