Back to Feast

Feast Production Deployment Topologies

docs/how-to-guides/production-deployment-topologies.md

0.63.065.6 KB
Original Source

Feast Production Deployment Topologies

Table of Contents


Overview

This guide defines three production-ready deployment topologies for Feast on Kubernetes using the Feast Operator. Each topology addresses a different stage of organizational maturity, from getting started safely to running large-scale multi-tenant deployments.

TopologyTarget audienceKey traits
Minimal ProductionSmall teams, POCs moving to productionSingle namespace, no HA, simple setup
Standard ProductionMost production workloadsHA registry, autoscaling, TLS, RBAC
Enterprise ProductionLarge orgs, multi-tenantNamespace isolation, managed stores, full observability

Beyond the core topologies, this guide also covers:

{% hint style="info" %} Prerequisites: All topologies assume a running Kubernetes cluster with the Feast Operator installed. Familiarity with Feast concepts and components is recommended. {% endhint %}


1. Minimal Production

When to use

  • Small teams with a single ML use case
  • POCs graduating to production
  • Low-traffic, non-critical workloads where simplicity is more important than availability

Architecture

mermaid
graph TD
    subgraph Kubernetes["Kubernetes (single namespace)"]
        Operator["Feast Operator"]
        Registry["Registry Server
(1 replica)"]
        OnlineServer["Online Feature Server
(1 replica)"]
        MaterializationJob["Materialization Job
(feast materialize / CronJob)"]
        NotebookPod(["Notebook / Training Pod
(Feast SDK — remote online + registry)"])
    end

    subgraph BackingStores["Backing Stores (inside or outside Kubernetes)"]
        Redis["Online Store
(e.g. Redis for prod; SQLite for dev)"]
        OfflineStore["Offline Store
(e.g. DuckDB/PVC for dev; S3/MinIO for prod)"]
    end

    Operator -->|manages| Registry
    Operator -->|manages| OnlineServer
    OnlineServer -->|reads/writes| Redis
    OnlineServer -->|reads metadata| Registry
    MaterializationJob -->|reads source data| OfflineStore
    MaterializationJob -->|writes features| Redis
    NotebookPod -->|online features REST| OnlineServer
    NotebookPod -->|metadata gRPC| Registry

    Client(["Client / ML Service"]) -->|REST port 6566| OnlineServer

    style Kubernetes fill:#f0f4ff,stroke:#3366cc,color:#000
    style BackingStores fill:#fafafa,stroke:#999,color:#000
    style Operator fill:#e8f5e9,stroke:#388e3c,color:#000
    style Registry fill:#fff3e0,stroke:#f57c00,color:#000
    style OnlineServer fill:#e3f2fd,stroke:#1976d2,color:#000
    style Redis fill:#fce4ec,stroke:#c62828,color:#000
    style OfflineStore fill:#f3e5f5,stroke:#7b1fa2,color:#000
    style MaterializationJob fill:#fff8e1,stroke:#f9a825,color:#000
    style NotebookPod fill:#e8f5e9,stroke:#388e3c,color:#000

Components

ComponentConfigurationNotes
Feast OperatorDefault installManages all Feast CRDs
RegistryREST, 1 replicaSingle point of metadata
Online Feature Server1 replica, no autoscalingServes online features
Online StoreRedis standalone (example)SQLite is simplest for development; Redis for production. See supported online stores for all options
Offline StoreFile-based or MinIODuckDB or file-based for development; MinIO/S3 for production. See supported offline stores for all options
Compute EngineIn-process (default)Suitable for small datasets and development; use Spark, Ray, or Snowflake Engine for larger workloads

Sample FeatureStore CR

yaml
apiVersion: feast.dev/v1
kind: FeatureStore
metadata:
  name: minimal-production
spec:
  feastProject: my_project
  services:
    onlineStore:
      persistence:
        store:
          type: redis
          secretRef:
            name: feast-online-store
      server:
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: "1"
            memory: 1Gi
    offlineStore:
      persistence:
        file:
          type: duckdb  # Use type: file for generic file-based; swap for S3/MinIO in production
          pvc:
            create:
              storageClassName: standard
              resources:
                requests:
                  storage: 10Gi
            mountPath: /data/offline
    registry:
      local:
        server:
          restAPI: true
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: 500m
              memory: 512Mi

Limitations

{% hint style="warning" %}

  • No high availability — a single replica failure causes downtime
  • No automatic failover — manual intervention required on failure
  • Manual scaling — no HPA configured
  • Limited security — no TLS, no ingress, no RBAC by default {% endhint %}

When to use

  • Most production ML workloads
  • Teams with moderate traffic that need reliability
  • Environments that require TLS, RBAC, and automated scaling

Architecture

mermaid
graph TD
    Client(["Client / ML Service"]) -->|HTTPS| Ingress

    subgraph Kubernetes["Kubernetes"]
        Ingress["Ingress Controller
(TLS termination)"]
        Operator["Feast Operator"]
        MaterializationJob["Materialization Job
(CronJob / batchEngine)"]
        NotebookPod(["Notebook / Training Pod
(Feast SDK — remote online, offline, registry)"])

        subgraph FeastDeployment["Feast Deployment (HPA autoscaled — all containers scale together)"]
            Registry["Registry Server"]
            OnlineServer["Online Feature Server"]
            OfflineServer["Offline Feature Server"]
        end

        Ingress -->|port 6566| OnlineServer
        Operator -->|manages| Registry
        Operator -->|manages| OnlineServer
        Operator -->|manages| OfflineServer
        OnlineServer -->|reads metadata| Registry
        OfflineServer -->|reads metadata| Registry
        MaterializationJob -->|reads metadata| Registry
        NotebookPod -->|online features REST| OnlineServer
        NotebookPod -->|historical features Arrow Flight| OfflineServer
        NotebookPod -->|metadata gRPC| Registry
    end

    subgraph BackingStores["Backing Stores (inside or outside Kubernetes)"]
        RedisCluster["Online Store
(e.g. Redis Cluster, DynamoDB, etc.)"]
        OfflineStore["Offline Store
(e.g. PostgreSQL, Redshift, BigQuery, etc.)"]
    end

    OnlineServer -->|reads/writes| RedisCluster
    MaterializationJob -->|writes features| RedisCluster
    OfflineServer -->|historical features| OfflineStore
    MaterializationJob -->|reads source data| OfflineStore

    style Kubernetes fill:#f0f4ff,stroke:#3366cc,color:#000
    style FeastDeployment fill:#e8f5e9,stroke:#388e3c,color:#000
    style BackingStores fill:#fafafa,stroke:#999,color:#000
    style Ingress fill:#fff9c4,stroke:#f9a825,color:#000
    style Operator fill:#e8f5e9,stroke:#388e3c,color:#000
    style Registry fill:#fff3e0,stroke:#f57c00,color:#000
    style OnlineServer fill:#e3f2fd,stroke:#1976d2,color:#000
    style OfflineServer fill:#ede7f6,stroke:#512da8,color:#000
    style RedisCluster fill:#fce4ec,stroke:#c62828,color:#000
    style OfflineStore fill:#f3e5f5,stroke:#7b1fa2,color:#000
    style MaterializationJob fill:#fff8e1,stroke:#f9a825,color:#000
    style NotebookPod fill:#e8f5e9,stroke:#388e3c,color:#000

Components

Core

ComponentConfigurationNotes
Feast OperatorDefault installManages all Feast CRDs
RegistrySQL-backed (PostgreSQL)Database-backed for consistency and concurrent access
Online Feature ServerHPA (min 2 replicas, max based on peak load)Separate container — serves online features from the online store
Offline Feature ServerScales with the same DeploymentSeparate container — serves historical features and materialization source reads from the offline store

Storage

ComponentConfigurationNotes
Online StoreRedis Cluster (example)Multi-node for availability and low latency; other production stores are also supported — see supported online stores
Offline StorePostgreSQL (example)Platform-agnostic DB-backed store; use Redshift/Athena for AWS, BigQuery for GCP, Spark for S3/MinIO pipelines — see supported offline stores for all options
Compute EngineSpark, Ray (KubeRay), or Snowflake EngineDistributed compute for materialization and historical retrieval at scale

Networking & Security

ComponentConfigurationNotes
IngressTLS-terminatedSecure external access
RBACKubernetes RBACNamespace-scoped permissions
SecretsKubernetes Secrets + ${ENV_VAR} substitutionStore credentials via secretRef / envFrom in the FeatureStore CR; inject into feature_store.yaml with environment variable syntax

Sample FeatureStore CR

yaml
apiVersion: feast.dev/v1
kind: FeatureStore
metadata:
  name: standard-production
spec:
  feastProject: my_project
  authz:
    kubernetes:
      roles:
        - feast-admin-role
        - feast-user-role
  batchEngine:
    configMapRef:
      name: feast-batch-engine
  services:
    scaling:
      autoscaling:
        minReplicas: 2
        maxReplicas: 10  # Set based on your peak load
        metrics:
        - type: Resource
          resource:
            name: cpu
            target:
              type: Utilization
              averageUtilization: 70
    podDisruptionBudgets:
      maxUnavailable: 1
    onlineStore:
      persistence:
        store:
          type: redis
          secretRef:
            name: feast-online-store
      server:
        resources:
          requests:
            cpu: "1"
            memory: 1Gi
          limits:
            cpu: "2"
            memory: 2Gi
    offlineStore:
      persistence:
        store:
          type: postgres
          secretRef:
            name: feast-offline-store
      server:
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: "1"
            memory: 1Gi
    registry:
      local:
        persistence:
          store:
            type: sql
            secretRef:
              name: feast-registry-store
        server:
          restAPI: true
          resources:
            requests:
              cpu: 500m
              memory: 512Mi
            limits:
              cpu: "1"
              memory: 1Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: feast-batch-engine
data:
  config: |
    type: ray
    address: auto  # KubeRay cluster address; replace with explicit URL if not using auto-discovery

{% hint style="success" %} Key features:

  • High availability — multi-replica deployment with auto-injected pod anti-affinity and topology spread constraints
  • Scalable serving — HPA adjusts the shared deployment replicas (all services scale together) based on demand
  • Secure external access — TLS-terminated ingress with RBAC
  • Persistent storage — Online Store (Redis Cluster shown as example; see supported online stores for all options) + Offline Store (PostgreSQL shown as example; see supported offline stores for all options) for durability

See Horizontal Scaling with the Feast Operator for full scaling configuration details. {% endhint %}


3. Enterprise Production

When to use

  • Large organizations with multiple ML teams
  • Multi-tenant environments requiring strict isolation
  • High-scale deployments with governance, compliance, and SLA requirements

Architecture — Isolated Registries (per namespace)

Each team gets its own registry and online feature server in a dedicated namespace. This provides the strongest isolation but has notable trade-offs: feature discovery is siloed per team (no cross-project visibility), and each registry requires its own Feast UI deployment — you cannot view multiple projects in a single UI instance.

mermaid
graph TD
    Client(["Clients / ML Services"]) -->|HTTPS| Gateway

    subgraph Kubernetes["Kubernetes"]
        Gateway["API Gateway / Ingress
(TLS + rate limiting)"]
        Operator["Feast Operator
(cluster-scoped)"]

        subgraph NamespaceA["Namespace A — Team A"]
            subgraph DeployA["Feast Deployment (HPA autoscaled)"]
                RegistryA["Registry Server"]
                OnlineServerA["Online Feature Server"]
                OfflineServerA["Offline Feature Server"]
            end
            NotebookPodA(["Notebook — Team A
(Feast SDK)"])
            MaterializationJobA["Materialization Job"]
        end

        subgraph NamespaceB["Namespace B — Team B"]
            subgraph DeployB["Feast Deployment (HPA autoscaled)"]
                RegistryB["Registry Server"]
                OnlineServerB["Online Feature Server"]
                OfflineServerB["Offline Feature Server"]
            end
            NotebookPodB(["Notebook — Team B
(Feast SDK)"])
            MaterializationJobB["Materialization Job"]
        end

        Gateway -->|port 6566| OnlineServerA
        Gateway -->|port 6566| OnlineServerB
        Operator -->|manages| RegistryA
        Operator -->|manages| OnlineServerA
        Operator -->|manages| OfflineServerA
        Operator -->|manages| RegistryB
        Operator -->|manages| OnlineServerB
        Operator -->|manages| OfflineServerB
        OnlineServerA -->|metadata| RegistryA
        OnlineServerB -->|metadata| RegistryB
        OfflineServerA -->|metadata| RegistryA
        OfflineServerB -->|metadata| RegistryB
        NotebookPodA -.->|online REST| OnlineServerA
        NotebookPodA -.->|Arrow Flight| OfflineServerA
        NotebookPodA -.->|metadata| RegistryA
        NotebookPodB -.->|online REST| OnlineServerB
        NotebookPodB -.->|Arrow Flight| OfflineServerB
        NotebookPodB -.->|metadata| RegistryB
        MaterializationJobA -->|metadata| RegistryA
        MaterializationJobB -->|metadata| RegistryB
    end

    subgraph BackingStores["Backing Stores (inside or outside Kubernetes)"]
        RedisA["Online Store — Team A
(e.g. Redis, DynamoDB, etc.)"]
        RedisB["Online Store — Team B
(e.g. Redis, DynamoDB, etc.)"]
        OfflineStore["Offline Store — shared instance
(e.g. BigQuery, Redshift, etc.)
isolated via per-team datasets / schemas"]
    end

    subgraph Observability["Observability Stack"]
        OTel["OpenTelemetry
(traces + metrics)"]
        Prometheus["Prometheus"]
        Grafana["Grafana"]
        Jaeger["Jaeger"]
    end

    OnlineServerA -->|reads/writes| RedisA
    OnlineServerB -->|reads/writes| RedisB
    OfflineServerA -->|Team A dataset| OfflineStore
    OfflineServerB -->|Team B dataset| OfflineStore
    MaterializationJobA -->|Team A data| OfflineStore
    MaterializationJobA -->|writes| RedisA
    MaterializationJobB -->|Team B data| OfflineStore
    MaterializationJobB -->|writes| RedisB

    style Kubernetes fill:#f0f4ff,stroke:#3366cc,color:#000
    style NamespaceA fill:#e8f5e9,stroke:#388e3c,color:#000
    style NamespaceB fill:#e3f2fd,stroke:#1976d2,color:#000
    style DeployA fill:#c8e6c9,stroke:#2e7d32,color:#000
    style DeployB fill:#bbdefb,stroke:#1565c0,color:#000
    style BackingStores fill:#fafafa,stroke:#999,color:#000
    style Observability fill:#fff3e0,stroke:#f57c00,color:#000
    style Gateway fill:#fff9c4,stroke:#f9a825,color:#000
    style Operator fill:#e8f5e9,stroke:#388e3c,color:#000

Architecture — Shared Registry (cross-namespace)

Alternatively, a single centralized registry server can serve multiple tenant namespaces. Tenant online feature servers connect to the shared registry via the Remote Registry gRPC client. This reduces operational overhead, enables cross-team feature discovery, and allows a single Feast UI deployment to browse all projects — while Feast permissions enforce tenant isolation at the data level.

mermaid
graph TD
    Client(["Clients / ML Services"]) -->|HTTPS| Gateway

    subgraph Kubernetes["Kubernetes"]
        Gateway["API Gateway / Ingress
(TLS + rate limiting)"]

        subgraph SharedInfra["Shared Infrastructure Namespace"]
            Operator["Feast Operator
(cluster-scoped)"]
            subgraph SharedDeploy["Feast Deployment (3 replicas)"]
                SharedRegistry["Registry Server
(gRPC + REST)"]
            end
            SharedDB[("SQL Database
(PostgreSQL)")]
            SharedRegistry -->|persists| SharedDB
        end

        subgraph NamespaceA["Namespace A — Team A"]
            subgraph DeployA2["Feast Deployment (HPA autoscaled)"]
                OnlineServerA["Online Feature Server"]
                OfflineServerA["Offline Feature Server"]
            end
            NotebookPodA(["Notebook — Team A
(Feast SDK)"])
            MaterializationJobA["Materialization Job"]
            ConfigA["registry_type: remote
path: shared-registry:6570"]
        end

        subgraph NamespaceB["Namespace B — Team B"]
            subgraph DeployB2["Feast Deployment (HPA autoscaled)"]
                OnlineServerB["Online Feature Server"]
                OfflineServerB["Offline Feature Server"]
            end
            NotebookPodB(["Notebook — Team B
(Feast SDK)"])
            MaterializationJobB["Materialization Job"]
            ConfigB["registry_type: remote
path: shared-registry:6570"]
        end

        Gateway -->|port 6566| OnlineServerA
        Gateway -->|port 6566| OnlineServerB
        OnlineServerA -->|gRPC port 6570| SharedRegistry
        OnlineServerB -->|gRPC port 6570| SharedRegistry
        OfflineServerA -->|gRPC port 6570| SharedRegistry
        OfflineServerB -->|gRPC port 6570| SharedRegistry
        Operator -->|manages| SharedRegistry
        Operator -->|manages| OnlineServerA
        Operator -->|manages| OfflineServerA
        Operator -->|manages| OnlineServerB
        Operator -->|manages| OfflineServerB
        NotebookPodA -.->|online REST| OnlineServerA
        NotebookPodA -.->|Arrow Flight| OfflineServerA
        NotebookPodA -.->|metadata| SharedRegistry
        NotebookPodB -.->|online REST| OnlineServerB
        NotebookPodB -.->|Arrow Flight| OfflineServerB
        NotebookPodB -.->|metadata| SharedRegistry
        MaterializationJobA -->|gRPC port 6570| SharedRegistry
        MaterializationJobB -->|gRPC port 6570| SharedRegistry
    end

    subgraph BackingStores["Backing Stores (inside or outside Kubernetes)"]
        RedisA["Online Store — Team A
(e.g. Redis, DynamoDB, etc.)"]
        RedisB["Online Store — Team B
(e.g. Redis, DynamoDB, etc.)"]
        OfflineStore["Offline Store — shared instance
(e.g. BigQuery, Redshift, etc.)
isolated via per-team datasets / schemas"]
    end

    OnlineServerA -->|reads/writes| RedisA
    OnlineServerB -->|reads/writes| RedisB
    OfflineServerA -->|Team A dataset| OfflineStore
    OfflineServerB -->|Team B dataset| OfflineStore
    MaterializationJobA -->|Team A data| OfflineStore
    MaterializationJobA -->|writes| RedisA
    MaterializationJobB -->|Team B data| OfflineStore
    MaterializationJobB -->|writes| RedisB

    style Kubernetes fill:#f0f4ff,stroke:#3366cc,color:#000
    style SharedInfra fill:#fff3e0,stroke:#f57c00,color:#000
    style SharedDeploy fill:#ffe0b2,stroke:#e65100,color:#000
    style NamespaceA fill:#e8f5e9,stroke:#388e3c,color:#000
    style NamespaceB fill:#e3f2fd,stroke:#1976d2,color:#000
    style DeployA2 fill:#c8e6c9,stroke:#2e7d32,color:#000
    style DeployB2 fill:#bbdefb,stroke:#1565c0,color:#000
    style BackingStores fill:#fafafa,stroke:#999,color:#000
    style Gateway fill:#fff9c4,stroke:#f9a825,color:#000
    style Operator fill:#e8f5e9,stroke:#388e3c,color:#000
    style OfflineStore fill:#f3e5f5,stroke:#7b1fa2,color:#000

Shared registry client configuration — each tenant's feature_store.yaml points to the centralized registry:

yaml
registry:
  registry_type: remote
  path: shared-registry.feast-system.svc.cluster.local:6570

{% hint style="info" %} Shared vs isolated registries:

Shared RegistryIsolated Registries
Feature discoveryCross-team — all projects visibleSiloed — each team sees only its own
Feast UISingle deployment serves all projectsSeparate UI deployment per registry
IsolationLogical (Feast permissions + tags)Physical (separate metadata stores)
Operational costLower — one registry to manageHigher — N registries to maintain
Best forFeature reuse, shared ML platformRegulatory/compliance separation

Use a shared registry when teams need to discover and reuse features across projects, and rely on Feast permissions for access control. Use isolated registries when regulatory or compliance requirements demand physical separation of metadata. {% endhint %}

Components

Multi-tenancy

AspectConfigurationNotes
Isolation modelNamespace-per-teamPhysical isolation via Kubernetes namespaces
Registry strategyShared (remote) or isolated (per-namespace)See architecture variants above
Network boundariesNetworkPolicy enforcedCross-namespace traffic denied by default (allow-listed for shared registry)

Storage

ComponentConfigurationNotes
Online StoreManaged Redis / DynamoDB / ElasticsearchCloud-managed, per-tenant instances; see supported online stores for all options
Offline StoreExternal data warehouse (Snowflake, BigQuery)Shared or per-tenant access controls; see supported offline stores for all options

Scaling

ComponentConfigurationNotes
FeatureStore DeploymentHPA + Cluster AutoscalerAll services (Online Feature Server, Registry, Offline Feature Server) scale together per tenant; set maxReplicas based on your peak load. Independent scaling across tenants.
ClusterMulti-zone node poolsZone-aware scheduling with auto-injected topology spread constraints

Security

ComponentConfigurationNotes
AuthenticationOIDC via KeycloakCentralized identity provider
AuthorizationFeast permissions + Kubernetes RBACSee Permissions and RBAC below
NetworkNetworkPolicies per namespaceMicrosegmentation
SecretsKubernetes Secrets (secretRef / envFrom)Credentials injected via FeatureStore CR; use Kubernetes-native tooling (e.g. External Secrets Operator) to sync from external vaults if needed

Observability

ComponentPurposeNotes
OpenTelemetryTraces + metrics exportBuilt-in Feast integration; emits spans for feature retrieval, materialization, and registry operations
PrometheusMetrics collectionCollects OpenTelemetry metrics from Online Feature Server + Online Store
GrafanaDashboards + tracesPer-tenant and aggregate views; can display OpenTelemetry traces via Tempo or Jaeger data source
JaegerDistributed tracingVisualize OpenTelemetry traces for request latency analysis and debugging

Reliability & Disaster Recovery

AspectConfigurationNotes
PodDisruptionBudgetsConfigured per deploymentProtects against voluntary disruptions
Multi-zoneTopology spread constraintsAuto-injected by operator when scaling; survives single zone failures
Backup / RestoreSee recovery priority belowStrategy depends on component criticality

Recovery priority guidance

Not all Feast components carry the same recovery urgency. The table below ranks components by restoration priority and provides guidance for RPO (Recovery Point Objective — maximum acceptable data loss) and RTO (Recovery Time Objective — maximum acceptable downtime). Specific targets depend on your backing store SLAs and organizational requirements.

PriorityComponentRPO guidanceRTO guidanceRationale
1 — CriticalRegistry DB (PostgreSQL / MySQL)Minutes (continuous replication or frequent backups)Minutes (failover to standby)Contains all feature definitions and metadata; without it, no service can resolve features
2 — HighOnline Store (Redis / DynamoDB)Reconstructible via materializationMinutes to hours (depends on data volume)Can be fully rebuilt by re-running materialization from the offline store; no unique data to lose
3 — MediumOffline Store (Redshift / BigQuery)Per data warehouse SLAPer data warehouse SLASource of truth for historical data; typically managed by the cloud provider with built-in replication
4 — LowFeast Operator + CRDsN/A (declarative, stored in Git)Minutes (re-apply manifests)Stateless; redeployable from version-controlled manifests

{% hint style="info" %} Key insight: The online store is reconstructible — it can always be rebuilt from the offline store by re-running materialization. This means its RPO is effectively zero (no unique data to lose), but RTO depends on how long full materialization takes for your dataset volume. For large datasets, consider maintaining Redis persistence (RDB snapshots or AOF) to reduce recovery time. {% endhint %}

Backup recommendations by topology

TopologyRegistryOnline StoreOffline Store
MinimalManual file backups; accept downtime on failureNot backed up (re-materialize)N/A (file-based)
StandardAutomated PostgreSQL backups (daily + WAL archiving)Redis RDB snapshots or AOF persistencePer cloud provider SLA
EnterpriseManaged DB replication (multi-AZ); cross-region replicas for DRManaged Redis with automatic failover (ElastiCache Multi-AZ, Memorystore HA)Managed warehouse replication (Redshift cross-region, BigQuery cross-region)

Sample FeatureStore CR (per tenant)

yaml
apiVersion: feast.dev/v1
kind: FeatureStore
metadata:
  name: team-a-production
  namespace: team-a
spec:
  feastProject: team_a
  authz:
    oidc:
      secretRef:
        name: feast-oidc-secret  # Secret keys: client_id, client_secret, auth_discovery_url
  batchEngine:
    configMapRef:
      name: feast-batch-engine
  services:
    scaling:
      autoscaling:
        minReplicas: 3
        maxReplicas: 20  # Set based on your peak load
        metrics:
        - type: Resource
          resource:
            name: cpu
            target:
              type: Utilization
              averageUtilization: 65
    podDisruptionBudgets:
      minAvailable: 2
    onlineStore:
      persistence:
        store:
          type: redis
          secretRef:
            name: feast-online-store
      server:
        resources:
          requests:
            cpu: "2"
            memory: 2Gi
          limits:
            cpu: "4"
            memory: 4Gi
    offlineStore:
      persistence:
        store:
          type: bigquery  # Use snowflake.offline, redshift, etc. as alternatives — see supported offline stores
          secretRef:
            name: feast-offline-store
      server:
        resources:
          requests:
            cpu: "1"
            memory: 1Gi
          limits:
            cpu: "2"
            memory: 2Gi
    registry:
      local:
        persistence:
          store:
            type: sql
            secretRef:
              name: feast-registry-store
        server:
          restAPI: true
          resources:
            requests:
              cpu: "1"
              memory: 1Gi
            limits:
              cpu: "2"
              memory: 2Gi
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: feast-batch-engine
  namespace: team-a
data:
  config: |
    type: spark
    spark_master: k8s://https://kubernetes.default.svc:443
    spark_app_name: feast-materialization

Feast Permissions and RBAC

Feast provides a built-in permissions framework that secures resources at the application level, independently of Kubernetes RBAC. Permissions are defined as Python objects in your feature repository and registered via feast apply.

For full details, see the Permission concept and RBAC architecture docs.

How it works

mermaid
graph LR
    subgraph Client["Client Request"]
        Token["Auth Token
(OIDC / K8s SA)"]
    end

    subgraph Server["Feast Server"]
        Extractor["Token Extractor"]
        Parser["Token Parser"]
        Enforcer["Policy Enforcer"]
    end

    subgraph Registry["Registry"]
        Permissions["Permission Objects"]
    end

    Token --> Extractor
    Extractor --> Parser
    Parser -->|user roles/groups/ns| Enforcer
    Enforcer -->|match resource + action| Permissions
    Enforcer -->|allow / deny| Response(["Response"])

    style Client fill:#e3f2fd,stroke:#1976d2,color:#000
    style Server fill:#fff3e0,stroke:#f57c00,color:#000
    style Registry fill:#e8f5e9,stroke:#388e3c,color:#000

Permission enforcement happens on the server side (Online Feature Server, Offline Feature Server, Registry Server). There is no enforcement when using the Feast SDK with a local provider.

Actions

Feast defines eight granular actions:

ActionDescription
CREATECreate a new Feast object
DESCRIBERead object metadata/state
UPDATEModify an existing object
DELETERemove an object
READ_ONLINERead from the online store
READ_OFFLINERead from the offline store
WRITE_ONLINEWrite to the online store
WRITE_OFFLINEWrite to the offline store

Convenience aliases are provided:

AliasIncludes
ALL_ACTIONSAll eight actions
READREAD_ONLINE + READ_OFFLINE
WRITEWRITE_ONLINE + WRITE_OFFLINE
CRUDCREATE + DESCRIBE + UPDATE + DELETE

Protected resource types

Permissions can be applied to any of these Feast object types:

Project, Entity, FeatureView, OnDemandFeatureView, BatchFeatureView, StreamFeatureView, FeatureService, DataSource, ValidationReference, SavedDataset, Permission

The constant ALL_RESOURCE_TYPES includes all of the above. ALL_FEATURE_VIEW_TYPES includes all feature view subtypes.

Policy types

PolicyMatch criteriaUse case
RoleBasedPolicy(roles=[...])User must have at least one of the listed rolesKubernetes RBAC roles, OIDC roles
GroupBasedPolicy(groups=[...])User must belong to at least one of the listed groupsLDAP/OIDC group membership
NamespaceBasedPolicy(namespaces=[...])User's service account must be in one of the listed namespacesKubernetes namespace-level isolation
CombinedGroupNamespacePolicy(groups=[...], namespaces=[...])User must match at least one group or one namespaceFlexible cross-cutting policies
AllowAllAlways grants accessDevelopment / unsecured resources

Example: Role-based permissions

This is the most common pattern — separate admin and read-only roles:

python
from feast.feast_object import ALL_RESOURCE_TYPES
from feast.permissions.action import READ, ALL_ACTIONS, AuthzedAction
from feast.permissions.permission import Permission
from feast.permissions.policy import RoleBasedPolicy

admin_perm = Permission(
    name="feast_admin_permission",
    types=ALL_RESOURCE_TYPES,
    policy=RoleBasedPolicy(roles=["feast-admin-role"]),
    actions=ALL_ACTIONS,
)

user_perm = Permission(
    name="feast_user_permission",
    types=ALL_RESOURCE_TYPES,
    policy=RoleBasedPolicy(roles=["feast-user-role"]),
    actions=[AuthzedAction.DESCRIBE] + READ,
)

Example: Namespace-based isolation for multi-tenant deployments

Use NamespaceBasedPolicy to restrict access based on the Kubernetes namespace of the calling service account — ideal for the shared-registry enterprise topology.

Each team gets two permissions: full access to its own resources (matched by team tag), and read-only access to resources any team has explicitly published as shared (matched by visibility: shared tag). The two required_tags target different resources — a feature view tagged team: team-b, visibility: shared matches only the second permission for Team A, enabling cross-team discovery without granting write access:

python
from feast.feast_object import ALL_RESOURCE_TYPES
from feast.permissions.action import ALL_ACTIONS, READ, AuthzedAction
from feast.permissions.permission import Permission
from feast.permissions.policy import NamespaceBasedPolicy

# Team A: full access to its own resources
team_a_own = Permission(
    name="team_a_full_access",
    types=ALL_RESOURCE_TYPES,
    required_tags={"team": "team-a"},          # matches only Team A's resources
    policy=NamespaceBasedPolicy(namespaces=["team-a"]),
    actions=ALL_ACTIONS,
)

# Team A: read-only access to shared resources published by ANY team
# e.g. a Team B feature view tagged {team: team-b, visibility: shared}
# satisfies required_tags here but NOT team_a_own above
team_a_read_shared = Permission(
    name="team_a_read_shared",
    types=ALL_RESOURCE_TYPES,
    required_tags={"visibility": "shared"},    # matches shared resources from any team
    policy=NamespaceBasedPolicy(namespaces=["team-a"]),
    actions=[AuthzedAction.DESCRIBE] + READ,
)

# Team B: mirror of the above — full access to its own, read-only to shared
team_b_own = Permission(
    name="team_b_full_access",
    types=ALL_RESOURCE_TYPES,
    required_tags={"team": "team-b"},
    policy=NamespaceBasedPolicy(namespaces=["team-b"]),
    actions=ALL_ACTIONS,
)

team_b_read_shared = Permission(
    name="team_b_read_shared",
    types=ALL_RESOURCE_TYPES,
    required_tags={"visibility": "shared"},
    policy=NamespaceBasedPolicy(namespaces=["team-b"]),
    actions=[AuthzedAction.DESCRIBE] + READ,
)

Example: Combined group + namespace policy

For organizations that use both OIDC groups and Kubernetes namespaces for identity — ideal when platform engineers lack a dedicated namespace but need cross-team visibility, or when OIDC group membership and namespace ownership should independently grant access:

python
from feast.feast_object import ALL_RESOURCE_TYPES
from feast.permissions.action import ALL_ACTIONS, READ, AuthzedAction
from feast.permissions.permission import Permission
from feast.permissions.policy import CombinedGroupNamespacePolicy

# Platform engineers (OIDC group) OR any team namespace can read shared features.
# This covers platform engineers who have no dedicated K8s namespace of their own
# but need cross-team feature discovery.
platform_read_shared = Permission(
    name="platform_read_shared",
    types=ALL_RESOURCE_TYPES,
    required_tags={"visibility": "shared"},
    policy=CombinedGroupNamespacePolicy(
        groups=["ml-platform"],           # OIDC group for platform/infra engineers
        namespaces=["team-a", "team-b"],  # team namespaces from enterprise topology
    ),
    actions=[AuthzedAction.DESCRIBE] + READ,
)

# ML engineers (OIDC) OR team namespace owners have full write access.
# Either identity alone is sufficient — useful during namespace migration or
# when the same person holds both the OIDC role and the team namespace.
ml_engineer_write = Permission(
    name="ml_engineer_full_access",
    types=ALL_RESOURCE_TYPES,
    policy=CombinedGroupNamespacePolicy(
        groups=["ml-engineers"],
        namespaces=["team-a", "team-b"],
    ),
    actions=ALL_ACTIONS,
)

Example: Fine-grained resource filtering

Permissions support name_patterns (regex) and required_tags for targeting specific resources:

python
from feast.feature_view import FeatureView
from feast.data_source import DataSource
from feast.permissions.action import AuthzedAction, READ
from feast.permissions.permission import Permission
from feast.permissions.policy import RoleBasedPolicy

sensitive_fv_perm = Permission(
    name="sensitive_feature_reader",
    types=[FeatureView],
    name_patterns=[".*sensitive.*", ".*pii.*"],
    policy=RoleBasedPolicy(roles=["trusted-reader"]),
    actions=[AuthzedAction.READ_OFFLINE],
)

high_risk_ds_writer = Permission(
    name="high_risk_ds_writer",
    types=[DataSource],
    required_tags={"risk_level": "high"},
    policy=RoleBasedPolicy(roles=["admin", "data_team"]),
    actions=[AuthzedAction.WRITE_ONLINE, AuthzedAction.WRITE_OFFLINE],
)

Authorization configuration

Enable auth enforcement in feature_store.yaml:

yaml
auth:
  type: kubernetes    # or: oidc

For OIDC:

yaml
auth:
  type: oidc
  client_id: feast-client
  auth_server_url: https://keycloak.example.com/realms/feast
  auth_discovery_url: https://keycloak.example.com/realms/feast/.well-known/openid-configuration

{% hint style="warning" %} Permission granting order: Feast uses an affirmative decision strategy — if any matching permission grants access, the request is allowed. Access is denied only when all matching permissions deny the user. If no permission matches a resource + action combination, access is denied. Resources that do not match any configured permission are unsecured. Always define explicit coverage for all critical resources. {% endhint %}

TopologyAuth typePolicy typeGuidance
Minimalno_auth or kubernetesRoleBasedPolicyBasic admin/reader roles
StandardkubernetesRoleBasedPolicyK8s service account roles
Enterprise (isolated)oidc or kubernetesRoleBasedPolicy + GroupBasedPolicyPer-team OIDC groups
Enterprise (shared registry)kubernetesNamespaceBasedPolicy or CombinedGroupNamespacePolicyNamespace isolation with tag-based resource scoping

Infrastructure-Specific Recommendations

Choosing the right online store, offline store, and registry backend depends on your cloud environment and existing infrastructure. The table below maps common deployment environments to recommended Feast components.

Recommendation matrix

mermaid
graph TD
    subgraph AWS["AWS / EKS / ROSA"]
        A_Online["Online: ElastiCache Redis
or DynamoDB"]
        A_Offline["Offline: Redshift
or Snowflake or Athena"]
        A_Registry["Registry: RDS PostgreSQL (SQL)
or S3"]
        A_Compute["Compute: Snowflake Engine
or Spark on EMR
or Ray (KubeRay)"]
    end

    subgraph GCP["GCP / GKE"]
        G_Online["Online: Memorystore Redis
or Bigtable or Datastore"]
        G_Offline["Offline: BigQuery
or Snowflake"]
        G_Registry["Registry: Cloud SQL PostgreSQL
or GCS"]
        G_Compute["Compute: Snowflake Engine
or Spark on Dataproc
or Ray (KubeRay)"]
    end

    subgraph OnPrem["On-Premise / OpenShift"]
        O_Online["Online: Redis
or PostgreSQL"]
        O_Offline["Offline: Spark + MinIO
or PostgreSQL or Trino or Oracle"]
        O_Registry["Registry: PostgreSQL (SQL)"]
        O_Compute["Compute: Spark
or Ray (KubeRay)"]
    end

    style AWS fill:#fff3e0,stroke:#f57c00,color:#000
    style GCP fill:#e3f2fd,stroke:#1976d2,color:#000
    style OnPrem fill:#e8f5e9,stroke:#388e3c,color:#000

AWS / EKS / ROSA

ComponentRecommendedAlternativeNotes
Online StoreRedis (ElastiCache)DynamoDBRedis offers TTL at retrieval, concurrent writes, Java/Go SDK support. DynamoDB is fully managed with zero ops.
Offline StoreRedshiftSnowflake, Athena (contrib), SparkRedshift is the core AWS offline store. Use Snowflake if it's already your warehouse. Athena for S3-native query patterns.
RegistrySQL (RDS PostgreSQL)S3SQL registry required for concurrent materialization writers. S3 registry is simpler but limited to single-writer.
Compute EngineSnowflake EngineSpark on EMR, Ray (KubeRay)Snowflake engine when your offline/online stores are Snowflake. Spark for S3-based pipelines. Ray with KubeRay for Kubernetes-native distributed processing.

{% hint style="info" %} ROSA (Red Hat OpenShift on AWS): Same store recommendations as EKS. Use OpenShift Routes instead of Ingress for TLS termination. Leverage OpenShift's built-in OAuth for auth.type: kubernetes integration. {% endhint %}

GCP / GKE

ComponentRecommendedAlternativeNotes
Online StoreRedis (Memorystore)Bigtable, DatastoreRedis for latency-sensitive workloads. Bigtable for very large-scale feature storage. Datastore is GCP-native and zero-ops.
Offline StoreBigQuerySnowflake, Spark (Dataproc)BigQuery is the core GCP offline store with full feature support.
RegistrySQL (Cloud SQL PostgreSQL)GCSSQL for multi-writer. GCS for simple single-writer setups.
Compute EngineSnowflake EngineSpark on Dataproc, Ray (KubeRay)Use Snowflake engine if your offline store is Snowflake. Spark for BigQuery + GCS pipelines. Ray with KubeRay for Kubernetes-native distributed processing.

On-Premise / OpenShift / Self-Managed Kubernetes

ComponentRecommendedAlternativeNotes
Online StoreRedis (self-managed or operator)PostgreSQL (contrib)Redis for best performance. PostgreSQL if you want to minimize infrastructure components.
Offline StoreSpark + MinIO (contrib)PostgreSQL (contrib), Trino (contrib), Oracle (contrib), DuckDBSpark for scale. PostgreSQL for simpler setups. Oracle for enterprise customers with existing Oracle infrastructure. DuckDB for development only.
RegistrySQL (PostgreSQL)Always use SQL registry in production on-prem. File-based registries do not support concurrent writers.
Compute EngineSparkRay (KubeRay)Run Spark on Kubernetes or standalone. Ray with KubeRay for Kubernetes-native distributed DAG execution.

{% hint style="warning" %} Multi-replica constraint: When scaling any Feast service to multiple replicas (via the Feast Operator), you must use database-backed persistence for all enabled services. File-based stores (SQLite, DuckDB, registry.db) are incompatible with multi-replica deployments. See Scaling Feast for details. {% endhint %}


Air-Gapped / Disconnected Environment Deployments

Production environments in regulated industries (finance, government, defense) often have no outbound internet access from the Kubernetes cluster. The Feast Operator supports air-gapped deployments through custom container images, init container controls, and standard Kubernetes image-pull mechanisms.

Default init container behavior

When feastProjectDir is set on the FeatureStore CR, the operator creates up to two init containers:

  1. feast-init — bootstraps the feature repository by running either git clone (if feastProjectDir.git is set) or feast init (if feastProjectDir.init is set), then writes the generated feature_store.yaml into the repo directory.
  2. feast-apply — runs feast apply to register feature definitions in the registry. Controlled by runFeastApplyOnInit (defaults to true). Skipped when disableInitContainers is true.

In air-gapped environments, git clone will fail because the cluster cannot reach external Git repositories. The solution is to pre-bake the feature repository into a custom container image and disable the init containers entirely.

Air-gapped deployment workflow

mermaid
graph TD
    subgraph BuildEnv["Build Environment (internet access)"]
        Code["Feature repo source code"]
        Base["Base Feast image
(feastdev/feature-server)"]
        Custom["Custom image with
bundled feature repo"]
        Code --> Custom
        Base --> Custom
    end

    subgraph InternalRegistry["Internal Container Registry"]
        Mirror["registry.internal.example.com
/feast/feature-server:v0.61"]
    end

    subgraph AirGappedCluster["Air-Gapped Kubernetes Cluster"]
        SA["ServiceAccount
(imagePullSecrets)"]
        CR["FeatureStore CR
disableInitContainers: true
image: registry.internal..."]
        Deploy["Feast Deployment
(no init containers)"]
        SA --> Deploy
        CR --> Deploy
    end

    Custom -->|push| Mirror
    Mirror -->|pull| Deploy

Steps:

  1. Build a custom container image that bundles the feature repository and all Python dependencies into the Feast base image.
  2. Push the image to your internal container registry.
  3. Set services.disableInitContainers: true on the FeatureStore CR to skip git clone / feast init and feast apply.
  4. Override the image on each service using the per-service image field.
  5. Set imagePullPolicy: IfNotPresent (or Never if images are pre-loaded on nodes).
  6. Configure imagePullSecrets on the namespace's ServiceAccount — the FeatureStore CRD does not expose an imagePullSecrets field, so use the standard Kubernetes approach of attaching secrets to the ServiceAccount that the pods run under.

Sample FeatureStore CR (air-gapped)

yaml
apiVersion: feast.dev/v1
kind: FeatureStore
metadata:
  name: airgap-production
spec:
  feastProject: my_project
  services:
    disableInitContainers: true
    onlineStore:
      persistence:
        store:
          type: redis
          secretRef:
            name: feast-online-store
      server:
        image: registry.internal.example.com/feast/feature-server:v0.61
        imagePullPolicy: IfNotPresent
        resources:
          requests:
            cpu: "1"
            memory: 1Gi
          limits:
            cpu: "2"
            memory: 2Gi
    registry:
      local:
        persistence:
          store:
            type: sql
            secretRef:
              name: feast-registry-store
        server:
          image: registry.internal.example.com/feast/feature-server:v0.61
          imagePullPolicy: IfNotPresent

{% hint style="info" %} Pre-populating the registry: With init containers disabled, feast apply does not run on pod startup. You can populate the registry by:

  1. Running feast apply from your CI/CD pipeline that has network access to the registry DB.
  2. Using the FeatureStore CR's built-in CronJob (spec.cronJob) — the operator creates a Kubernetes CronJob that runs feast apply and feast materialize-incremental on a schedule. The CronJob runs inside the cluster (no external access needed) and can use a custom image just like the main deployment. This is the recommended approach for air-gapped environments.
  3. Running feast apply manually from the build environment before deploying the CR. {% endhint %}

Air-gapped deployment checklist

{% hint style="warning" %} Pre-stage the following artifacts before deploying Feast in an air-gapped environment:

  • Container images — Feast feature server image (with bundled feature repo) pushed to internal registry
  • CRD manifests — Feast Operator CRDs and operator deployment manifests available locally
  • Store credentials — Kubernetes Secrets for online store, offline store, and registry DB connections created in the target namespace
  • Python packages (if using custom on-demand transforms) — bundled into the custom image or available from an internal PyPI mirror
  • ServiceAccount configurationimagePullSecrets attached to the ServiceAccount used by the Feast deployment {% endhint %}

Hybrid Store Configuration

The hybrid store feature allows a single Feast deployment to route feature operations to multiple backends based on tags or data sources. This is useful when different feature views have different latency, cost, or compliance requirements.

Hybrid online store

The HybridOnlineStore routes online operations to different backends based on a configurable tag on the FeatureView.

mermaid
graph LR
    FS["Online Feature Server"] --> Router["HybridOnlineStore
(routes by tag)"]
    Router -->|"tag: dynamodb"| DDB["DynamoDB"]
    Router -->|"tag: redis"| RD["Redis"]

    style Router fill:#fff3e0,stroke:#f57c00,color:#000
    style DDB fill:#e3f2fd,stroke:#1976d2,color:#000
    style RD fill:#fce4ec,stroke:#c62828,color:#000

feature_store.yaml configuration:

yaml
project: my_feature_repo
registry: data/registry.db
provider: local
online_store:
  type: hybrid
  routing_tag: team
  online_stores:
    - type: dynamodb
      conf:
        region: us-east-1
    - type: redis
      conf:
        connection_string: "redis-cluster:6379"
        redis_type: redis_cluster

Feature view with routing tag:

python
from feast import FeatureView

user_features = FeatureView(
    name="user_features",
    entities=[user_entity],
    source=user_source,
    tags={"team": "dynamodb"},  # Routes to DynamoDB backend
)

transaction_features = FeatureView(
    name="transaction_features",
    entities=[txn_entity],
    source=txn_source,
    tags={"team": "redis"},  # Routes to Redis backend
)

The tag value must match the online store type name (e.g. dynamodb, redis, bigtable).

Hybrid offline store

The HybridOfflineStore routes offline operations to different backends based on the batch_source type of each FeatureView.

mermaid
graph LR
    Client["Materialization /
Training Job"] --> Router["HybridOfflineStore
(routes by source type)"]
    Router -->|"SparkSource"| Spark["Spark"]
    Router -->|"RedshiftSource"| RS["Redshift"]

    style Router fill:#fff3e0,stroke:#f57c00,color:#000
    style Spark fill:#e8f5e9,stroke:#388e3c,color:#000
    style RS fill:#e3f2fd,stroke:#1976d2,color:#000

feature_store.yaml configuration:

yaml
project: my_feature_repo
registry: data/registry.db
provider: local
offline_store:
  type: hybrid_offline_store.HybridOfflineStore
  offline_stores:
    - type: spark
      conf:
        spark_master: local[*]
        spark_app_name: feast_spark_app
    - type: redshift
      conf:
        cluster_id: my-redshift-cluster
        region: us-east-1
        database: feast_db
        user: feast_user
        s3_staging_location: s3://my-bucket/feast-staging
        iam_role: arn:aws:iam::123456789012:role/FeastRedshiftRole

Feature views with different sources:

python
from feast import FeatureView, Entity, ValueType
from feast.infra.offline_stores.contrib.spark_offline_store.spark_source import SparkSource
from feast.infra.offline_stores.redshift_source import RedshiftSource

user_features = FeatureView(
    name="user_features",
    entities=[user_entity],
    source=SparkSource(path="s3://bucket/user_features"),  # Routes to Spark
)

activity_features = FeatureView(
    name="user_activity",
    entities=[user_entity],
    source=RedshiftSource(                                 # Routes to Redshift
        table="user_activity",
        event_timestamp_column="event_ts",
    ),
)

{% hint style="warning" %} Hybrid offline store constraint: get_historical_features requires all requested feature views to share the same batch_source type within a single call. You cannot join features across different offline engines in one retrieval request. {% endhint %}


Performance Considerations

For detailed server-level tuning (worker counts, timeouts, keep-alive, etc.), see the Online Server Performance Tuning guide.

Online feature server sizing

Traffic tierReplicasCPU (per pod)Memory (per pod)Notes
Low (<100 RPS)1–2500m–1512Mi–1GiMinimal production
Medium (100–1000 RPS)2–5 (HPA)1–21–2GiStandard production
High (>1000 RPS)5–20 (HPA)2–42–4GiEnterprise, per-tenant

Online store latency guidelines

Storep50 latencyp99 latencyBest for
Redis (single)<1ms<5msLowest latency, small-medium datasets
Redis Cluster<2ms<10msHigh availability + low latency
DynamoDB<5ms<20msServerless, variable traffic
PostgreSQL<5ms<30msOn-prem, simplicity
Remote (HTTP)<10ms<50msClient-server separation

Connection pooling for remote online store

When using the Remote Online Store (client-server architecture), connection pooling significantly reduces latency by reusing TCP/TLS connections:

yaml
online_store:
  type: remote
  path: http://feast-feature-server:80
  connection_pool_size: 50        # Max connections in pool (default: 50)
  connection_idle_timeout: 300    # Idle timeout in seconds (default: 300)
  connection_retries: 3           # Retry count with exponential backoff

Tuning by workload:

Workloadconnection_pool_sizeconnection_idle_timeoutconnection_retries
High-throughput inference1006005
Long-running batch service500 (never close)3
Resource-constrained edge10602

Registry performance

  • SQL registry (PostgreSQL, MySQL) is required for concurrent materialization jobs writing to the registry simultaneously.
  • File-based registries (S3, GCS, local) serialize the entire registry on each write — suitable only for single-writer scenarios.
  • For read-heavy workloads, scale the Registry Server to multiple replicas (all connecting to the same database).

Registry cache tuning at scale

Each Feast server pod maintains its own in-memory copy of the registry metadata. With multiple Gunicorn workers per pod, the total number of independent registry copies is replicas x workers. For example, 5 replicas with 4 workers each means 20 copies of the registry in memory, each refreshing independently.

With the default cache_mode: sync, the refresh is synchronous — when the TTL expires, the next request blocks until the full registry is re-downloaded. At scale, this causes periodic latency spikes across multiple pods simultaneously.

Recommendation: Use cache_mode: thread with a higher TTL in production to avoid refresh storms:

yaml
# In the Operator secret for SQL/DB-backed registries:
registry:
  registry_type: sql
  path: postgresql://<user>:<password>@<host>:5432/feast
  cache_mode: thread
  cache_ttl_seconds: 300

For the server-side refresh interval, set registryTTLSeconds on the CR:

yaml
spec:
  services:
    onlineStore:
      server:
        workerConfigs:
          registryTTLSeconds: 300
Scenariocache_modecache_ttl_secondsregistryTTLSeconds
Development / iterationsync (default)5–105
Production (low-latency)thread300300
Production (frequent schema changes)thread6060

{% hint style="info" %} registryTTLSeconds on the CR controls the server-side refresh interval. cache_ttl_seconds in the registry secret controls the SDK client refresh. In Operator deployments, the CR field is what matters for serving performance. For a deep dive into sync vs thread mode trade-offs, memory impact, and freshness considerations, see the Registry Cache Tuning section in the performance tuning guide. {% endhint %}

Materialization performance

Data volumeRecommended engineNotes
<1M rowsIn-process (default)Simple, no external dependencies
1M–100M rowsSnowflake Engine, Spark, or RayDistributed processing
>100M rowsSpark on Kubernetes / EMR / Dataproc, or Ray via KubeRayFull cluster-scale materialization with distributed DAG execution

For detailed engine configuration, see Scaling Materialization.

Redis sizing guidelines

MetricGuideline
Memory~100 bytes per feature value (varies by data type). For 1M entities x 50 features = ~5GB.
ConnectionsEach online feature server replica opens a connection pool. Plan for replicas x pool_size.
TTLSet key_ttl_seconds in feature_store.yaml to auto-expire stale data and bound memory usage.
Cluster modeUse Redis Cluster for >25GB datasets or >10K connections.

Design Principles

Understanding the following principles helps you choose and customize the right topology.

Control plane vs data plane

mermaid
graph LR
    subgraph ControlPlane["Control Plane"]
        Operator["Feast Operator"]
        Registry["Registry Server"]
    end

    subgraph DataPlane["Data Plane"]
        OnlineServer["Online Feature Server"]
        OfflineServer["Offline Feature Server"]
    end

    subgraph BackingStores["Backing Stores (external)"]
        OnlineStore["Online Store
(e.g. Redis, DynamoDB, PostgreSQL, etc.)"]
        OfflineStore["Offline Store
(e.g. Redshift, BigQuery, Spark, etc.)"]
        RegistryDB["Registry DB
(PostgreSQL / MySQL)"]
    end

    ControlPlane -->|configures| DataPlane
    DataPlane -->|reports status| ControlPlane
    OnlineServer --> OnlineStore
    OfflineServer --> OfflineStore
    Registry --> RegistryDB

    style ControlPlane fill:#e8f5e9,stroke:#388e3c,color:#000
    style DataPlane fill:#e3f2fd,stroke:#1976d2,color:#000
    style BackingStores fill:#fce4ec,stroke:#c62828,color:#000
  • Control plane (Operator + Registry Server) manages feature definitions, metadata, and lifecycle. It changes infrequently and should be highly available.
  • Data plane (Online Feature Server + Offline Feature Server) handles the actual feature reads/writes at request time. It must scale with traffic.
  • Backing stores (databases, object storage) hold the actual data. These are stateful and managed independently.

Stateless vs stateful components

The Feast Operator deploys all Feast services (Online Feature Server, Offline Feature Server, Registry Server) in a single shared Deployment. When scaling (spec.replicas > 1 or HPA autoscaling), all services scale together.

{% hint style="warning" %} Scaling requires DB-backed persistence for all enabled services. The operator enforces this via CRD validation:

  • Online Store — must use DB persistence (e.g. type: redis, type: dynamodb, type: postgres)
  • Offline Store — if enabled, must use DB persistence (e.g. type: redshift, type: bigquery, type: spark, type: postgres)
  • Registry — must use SQL persistence (type: sql), a remote registry, or S3/GCS file-backed registry

File-based stores (SQLite, DuckDB, registry.db) are rejected when replicas > 1 or autoscaling is configured. {% endhint %}

ComponentTypeScalingDB-backed requirement
Online Feature ServerStateless (server)Scales with the shared Deployment (HPA or spec.replicas)Online store must use DB persistence (e.g. Redis, DynamoDB, PostgreSQL)
Offline Feature ServerStateless (server)Scales with the shared Deployment (HPA or spec.replicas)Offline store must use DB persistence (e.g. Redshift, BigQuery, Spark, PostgreSQL)
Registry ServerStateless (server)Scales with the shared Deployment (HPA or spec.replicas)Registry must use SQL, remote, or S3/GCS persistence
Online Store (Redis, DynamoDB, etc.)Stateful (backing store)Scale via managed service or clusteringManaged independently of Feast services
Offline Store (Redshift, BigQuery, etc.)Stateful (backing store)Scale via cloud-managed infrastructureManaged independently of Feast services
Registry DB (PostgreSQL, MySQL)Stateful (backing store)Scale via managed database serviceManaged independently of Feast services

Scalability guidelines

mermaid
graph TD
    Read["Read traffic increase"] -->|scale| FS["Online Feature Server replicas (HPA)"]
    Write["Write / materialization load"] -->|scale| Engine["Compute Engine
(Spark / Ray / Snowflake)"]
    Storage["Data volume growth"] -->|scale| Store["Online / Offline Store capacity"]

    FS -.- Independent["Scale independently"]
    Engine -.- Independent
    Store -.- Independent

    style Read fill:#e3f2fd,stroke:#1976d2,color:#000
    style Write fill:#fff3e0,stroke:#f57c00,color:#000
    style Storage fill:#f3e5f5,stroke:#7b1fa2,color:#000
    style FS fill:#e3f2fd,stroke:#1976d2,color:#000
    style Engine fill:#fff3e0,stroke:#f57c00,color:#000
    style Store fill:#f3e5f5,stroke:#7b1fa2,color:#000
  • Read scaling — increase Online Feature Server replicas; they are stateless and scale linearly.
  • Write scaling — use a distributed compute engine (Spark, Ray/KubeRay, or Snowflake) for materialization.
  • Storage scaling — scale online and offline stores independently based on data volume and query patterns.

For detailed scaling configuration, see Scaling Feast.


Topology Comparison

CapabilityMinimalStandardEnterprise
High availabilityNoYesYes
AutoscalingNoHPAHPA + Cluster Autoscaler
TLS / IngressNoYesYes + API Gateway
RBACNoKubernetes RBACOIDC + fine-grained RBAC
Multi-tenancyNoNoNamespace-per-team
Shared registryN/AN/AOptional (remote registry)
Hybrid storesNoOptionalRecommended for mixed backends
ObservabilityLogs onlyBasic metricsOpenTelemetry + Prometheus + Grafana + Jaeger
Disaster recoveryNoPartialFull backup/restore
Network policiesNoOptionalEnforced
Recommended team size1–33–1515+

Next Steps