Deploy GCP Vertex AI Feature Online Stores

The gcp:vertex/aiFeatureOnlineStore:AiFeatureOnlineStore resource, part of the Pulumi GCP provider, provisions 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 connectivity.

Feature stores require a GCP project with Vertex AI enabled and may reference VPC infrastructure for private endpoints. The examples are intentionally small. Combine them with your own FeatureView and Feature resources to build complete ML 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 block provisions a Cloud Bigtable instance that scales between minNodeCount and maxNodeCount based on CPU utilization. When CPU exceeds cpuUtilizationTarget (50% in this example), Bigtable adds nodes automatically. This eliminates manual capacity planning while controlling costs during low-traffic periods.

Use optimized storage with private connectivity

Applications requiring private network access can use optimized storage with 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 block selects Google-managed storage optimized for feature serving. The dedicatedServingEndpoint with privateServiceConnectConfig isolates traffic from the public internet, routing queries through Private Service Connect. The projectAllowlists property controls which GCP projects can access the endpoint.

Beyond these examples

These snippets focus on specific feature store features: Bigtable and optimized storage backends, autoscaling configuration, and Private Service Connect networking. They’re intentionally minimal rather than full ML serving deployments.

The examples assume pre-existing infrastructure such as a GCP project with Vertex AI API enabled, and VPC and Private Service Connect configuration for private endpoints. They focus on configuring the feature store container rather than provisioning the complete ML infrastructure.

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

  • Customer-managed encryption keys (encryptionSpec)
  • Force destroy behavior (forceDestroy)
  • Embedding management (deprecated field)
  • FeatureView and Feature resources that use the store

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's the difference between Bigtable and Optimized storage types?
Bigtable requires manual autoscaling configuration with minNodeCount, maxNodeCount, and cpuUtilizationTarget. Optimized storage automatically enables embedding management and requires a dedicatedServingEndpoint.
When do I need to configure a dedicated serving endpoint?
A dedicated serving endpoint is required when using Optimized storage type or enabling embedding management. It provides a different endpoint from the common Vertex service endpoint.
Resource Management & Lifecycle
What properties are immutable after creation?
The name, project, and region properties cannot be changed after the feature store is created. Changing these requires recreating the resource.
What happens if I delete a feature store with forceDestroy enabled?
Setting forceDestroy to true will delete all FeatureViews and Features associated with the store. Without this flag, deletion fails if child resources exist.
Why don't my labels match what I configured?
The labels field is non-authoritative and only manages labels in your configuration. Use effectiveLabels to see all labels on the resource, including those set by other clients or services.
Networking & Access
How do I enable Private Service Connect for my feature store?
Configure dedicatedServingEndpoint.privateServiceConnectConfig with enablePrivateServiceConnect set to true and specify allowed projects in projectAllowlists.
Deprecated Features
Should I still use embeddingManagement in my configuration?
No, embeddingManagement is deprecated. Embedding management is now automatically enabled when using Optimized storage type, so this field is no longer needed.
What are the naming constraints for a feature online store?
The name can be up to 60 characters using [a-z0-9_], and the first character cannot be a number.

Using a different cloud?

Explore analytics guides for other cloud providers: