Deploy GCP Vertex AI Feature Online Stores

The gcp:vertex/aiFeatureOnlineStore:AiFeatureOnlineStore resource, part of the Pulumi GCP provider, defines a Vertex AI Feature Online Store container that serves ML features and embedding indexes at low latency. This guide focuses on two capabilities: Bigtable storage with autoscaling and optimized storage with private network access.

Feature stores require a GCP project with Vertex AI enabled and may reference VPC networks for private connectivity. The examples are intentionally small. Combine them with your own FeatureView and Feature resources to build complete feature serving pipelines.

Configure Bigtable storage with autoscaling

Most feature stores use Bigtable as the backing storage, with autoscaling to handle variable query loads.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const featureOnlineStore = new gcp.vertex.AiFeatureOnlineStore("feature_online_store", {
    name: "example_feature_online_store",
    labels: {
        foo: "bar",
    },
    region: "us-central1",
    bigtable: {
        autoScaling: {
            minNodeCount: 1,
            maxNodeCount: 3,
            cpuUtilizationTarget: 50,
        },
    },
});
import pulumi
import pulumi_gcp as gcp

feature_online_store = gcp.vertex.AiFeatureOnlineStore("feature_online_store",
    name="example_feature_online_store",
    labels={
        "foo": "bar",
    },
    region="us-central1",
    bigtable={
        "auto_scaling": {
            "min_node_count": 1,
            "max_node_count": 3,
            "cpu_utilization_target": 50,
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := vertex.NewAiFeatureOnlineStore(ctx, "feature_online_store", &vertex.AiFeatureOnlineStoreArgs{
			Name: pulumi.String("example_feature_online_store"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Region: pulumi.String("us-central1"),
			Bigtable: &vertex.AiFeatureOnlineStoreBigtableArgs{
				AutoScaling: &vertex.AiFeatureOnlineStoreBigtableAutoScalingArgs{
					MinNodeCount:         pulumi.Int(1),
					MaxNodeCount:         pulumi.Int(3),
					CpuUtilizationTarget: pulumi.Int(50),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var featureOnlineStore = new Gcp.Vertex.AiFeatureOnlineStore("feature_online_store", new()
    {
        Name = "example_feature_online_store",
        Labels = 
        {
            { "foo", "bar" },
        },
        Region = "us-central1",
        Bigtable = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableArgs
        {
            AutoScaling = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs
            {
                MinNodeCount = 1,
                MaxNodeCount = 3,
                CpuUtilizationTarget = 50,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.vertex.AiFeatureOnlineStore;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreBigtableAutoScalingArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var featureOnlineStore = new AiFeatureOnlineStore("featureOnlineStore", AiFeatureOnlineStoreArgs.builder()
            .name("example_feature_online_store")
            .labels(Map.of("foo", "bar"))
            .region("us-central1")
            .bigtable(AiFeatureOnlineStoreBigtableArgs.builder()
                .autoScaling(AiFeatureOnlineStoreBigtableAutoScalingArgs.builder()
                    .minNodeCount(1)
                    .maxNodeCount(3)
                    .cpuUtilizationTarget(50)
                    .build())
                .build())
            .build());

    }
}
resources:
  featureOnlineStore:
    type: gcp:vertex:AiFeatureOnlineStore
    name: feature_online_store
    properties:
      name: example_feature_online_store
      labels:
        foo: bar
      region: us-central1
      bigtable:
        autoScaling:
          minNodeCount: 1
          maxNodeCount: 3
          cpuUtilizationTarget: 50

The bigtable property configures Cloud Bigtable as the storage backend. The autoScaling block defines capacity bounds: minNodeCount and maxNodeCount set the scaling range, while cpuUtilizationTarget triggers scaling when CPU usage crosses the threshold. Vertex AI adjusts node count automatically based on query load.

Use optimized storage with private connectivity

For low-latency serving with private network access, the optimized storage type supports Private Service Connect.

import * as pulumi from "@pulumi/pulumi";
import * as gcp from "@pulumi/gcp";

const project = gcp.organizations.getProject({});
const featureonlinestore = new gcp.vertex.AiFeatureOnlineStore("featureonlinestore", {
    name: "example_feature_online_store_optimized",
    labels: {
        foo: "bar",
    },
    region: "us-central1",
    optimized: {},
    dedicatedServingEndpoint: {
        privateServiceConnectConfig: {
            enablePrivateServiceConnect: true,
            projectAllowlists: [project.then(project => project.number)],
        },
    },
});
import pulumi
import pulumi_gcp as gcp

project = gcp.organizations.get_project()
featureonlinestore = gcp.vertex.AiFeatureOnlineStore("featureonlinestore",
    name="example_feature_online_store_optimized",
    labels={
        "foo": "bar",
    },
    region="us-central1",
    optimized={},
    dedicated_serving_endpoint={
        "private_service_connect_config": {
            "enable_private_service_connect": True,
            "project_allowlists": [project.number],
        },
    })
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/organizations"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/vertex"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		project, err := organizations.LookupProject(ctx, &organizations.LookupProjectArgs{}, nil)
		if err != nil {
			return err
		}
		_, err = vertex.NewAiFeatureOnlineStore(ctx, "featureonlinestore", &vertex.AiFeatureOnlineStoreArgs{
			Name: pulumi.String("example_feature_online_store_optimized"),
			Labels: pulumi.StringMap{
				"foo": pulumi.String("bar"),
			},
			Region:    pulumi.String("us-central1"),
			Optimized: &vertex.AiFeatureOnlineStoreOptimizedArgs{},
			DedicatedServingEndpoint: &vertex.AiFeatureOnlineStoreDedicatedServingEndpointArgs{
				PrivateServiceConnectConfig: &vertex.AiFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigArgs{
					EnablePrivateServiceConnect: pulumi.Bool(true),
					ProjectAllowlists: pulumi.StringArray{
						pulumi.String(project.Number),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    var project = Gcp.Organizations.GetProject.Invoke();

    var featureonlinestore = new Gcp.Vertex.AiFeatureOnlineStore("featureonlinestore", new()
    {
        Name = "example_feature_online_store_optimized",
        Labels = 
        {
            { "foo", "bar" },
        },
        Region = "us-central1",
        Optimized = null,
        DedicatedServingEndpoint = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreDedicatedServingEndpointArgs
        {
            PrivateServiceConnectConfig = new Gcp.Vertex.Inputs.AiFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigArgs
            {
                EnablePrivateServiceConnect = true,
                ProjectAllowlists = new[]
                {
                    project.Apply(getProjectResult => getProjectResult.Number),
                },
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.organizations.OrganizationsFunctions;
import com.pulumi.gcp.organizations.inputs.GetProjectArgs;
import com.pulumi.gcp.vertex.AiFeatureOnlineStore;
import com.pulumi.gcp.vertex.AiFeatureOnlineStoreArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreOptimizedArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreDedicatedServingEndpointArgs;
import com.pulumi.gcp.vertex.inputs.AiFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigArgs;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var project = OrganizationsFunctions.getProject(GetProjectArgs.builder()
            .build());

        var featureonlinestore = new AiFeatureOnlineStore("featureonlinestore", AiFeatureOnlineStoreArgs.builder()
            .name("example_feature_online_store_optimized")
            .labels(Map.of("foo", "bar"))
            .region("us-central1")
            .optimized(AiFeatureOnlineStoreOptimizedArgs.builder()
                .build())
            .dedicatedServingEndpoint(AiFeatureOnlineStoreDedicatedServingEndpointArgs.builder()
                .privateServiceConnectConfig(AiFeatureOnlineStoreDedicatedServingEndpointPrivateServiceConnectConfigArgs.builder()
                    .enablePrivateServiceConnect(true)
                    .projectAllowlists(project.number())
                    .build())
                .build())
            .build());

    }
}
resources:
  featureonlinestore:
    type: gcp:vertex:AiFeatureOnlineStore
    properties:
      name: example_feature_online_store_optimized
      labels:
        foo: bar
      region: us-central1
      optimized: {}
      dedicatedServingEndpoint:
        privateServiceConnectConfig:
          enablePrivateServiceConnect: true
          projectAllowlists:
            - ${project.number}
variables:
  project:
    fn::invoke:
      function: gcp:organizations:getProject
      arguments: {}

The optimized property selects the managed storage backend optimized for feature serving. The dedicatedServingEndpoint with privateServiceConnectConfig enables private network access: enablePrivateServiceConnect activates the feature, and projectAllowlists specifies which GCP projects can connect. This keeps feature serving traffic within your VPC.

Beyond these examples

These snippets focus on specific feature store features: Bigtable storage with autoscaling and optimized storage with private connectivity. They’re intentionally minimal rather than full ML serving pipelines.

The examples assume pre-existing infrastructure such as a GCP project with Vertex AI API enabled and a VPC network for Private Service Connect when using optimized storage. They focus on configuring the feature store container rather than provisioning FeatureViews or Features.

To keep things focused, common feature store patterns are omitted, including:

  • Embedding management configuration (deprecated, auto-enabled for optimized storage)
  • Force destroy for cleanup (forceDestroy)
  • FeatureView and Feature resources that use this store
  • IAM permissions for feature serving

These omissions are intentional: the goal is to illustrate how each storage backend is wired, not provide drop-in ML serving modules. See the Vertex AI Feature Online Store resource reference for all available configuration options.

Let's deploy GCP Vertex AI Feature Online Stores

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Storage Configuration
What storage types are available for Feature Online Store?

You can choose between two storage types:

  1. Bigtable - Configure with bigtable and autoScaling settings (minNodeCount, maxNodeCount, cpuUtilizationTarget)
  2. Optimized - Configure with optimized for managed storage
How do I configure autoscaling for Bigtable storage?
Set bigtable.autoScaling with minNodeCount, maxNodeCount, and cpuUtilizationTarget. For example, min 1 node, max 3 nodes, with 50% CPU target.
Resource Lifecycle & Immutability
What properties can't I change after creating the store?
The name, region, and project properties are immutable and cannot be changed after creation.
What are the naming constraints for Feature Online Store?
The name can be up to 60 characters using only [a-z0-9_], and the first character cannot be a number.
What happens to FeatureViews and Features when I delete the store?
By default, they remain after deletion. Set forceDestroy to true to automatically delete all associated FeatureViews and Features.
Networking & Connectivity
How do I enable private connectivity for my Feature Online Store?
Configure dedicatedServingEndpoint with privateServiceConnectConfig, setting enablePrivateServiceConnect to true and specifying projectAllowlists.
When do I need to configure dedicatedServingEndpoint?
You need dedicatedServingEndpoint when using Optimized storage type or enabling EmbeddingManagement. Otherwise, a public endpoint is used by default.
Configuration & Metadata
Why is embeddingManagement deprecated?
The embeddingManagement field is no longer needed because embedding management is automatically enabled when you specify Optimized storage type.
What's the difference between labels and effectiveLabels?
The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels on the resource, including those from other clients and services.

Using a different cloud?

Explore analytics guides for other cloud providers: