Configure GCP Firebase App Hosting Traffic

The gcp:firebase/appHostingTraffic:AppHostingTraffic resource, part of the Pulumi GCP provider, controls how production traffic is routed to Firebase App Hosting builds. This guide focuses on three capabilities: manual traffic routing to specific builds, automatic rollouts from Git branches, and rollout policy pause controls.

Traffic configuration requires an existing AppHostingBackend, and manual splits require an AppHostingBuild. Automatic rollouts assume Git repository integration is configured. The examples are intentionally small. Combine them with your own Firebase project, backend, and build resources.

Route traffic to a specific build

When deploying a Firebase App Hosting backend, you often need to control which build receives production traffic for initial deployment or version testing.

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

//## Include these blocks only once per project if you are starting from scratch ###
const serviceAccount = new gcp.serviceaccount.Account("service_account", {
    project: "my-project-name",
    accountId: "firebase-app-hosting-compute",
    displayName: "Firebase App Hosting compute service account",
    createIgnoreAlreadyExists: true,
});
const fah = new gcp.projects.Service("fah", {
    project: "my-project-name",
    service: "firebaseapphosting.googleapis.com",
});
const exampleAppHostingBackend = new gcp.firebase.AppHostingBackend("example", {
    project: "my-project-name",
    location: "asia-east1",
    backendId: "traffic-tg",
    appId: "1:0000000000:web:674cde32020e16fbce9dbd",
    servingLocality: "GLOBAL_ACCESS",
    serviceAccount: serviceAccount.email,
}, {
    dependsOn: [fah],
});
const exampleAppHostingBuild = new gcp.firebase.AppHostingBuild("example", {
    project: exampleAppHostingBackend.project,
    location: exampleAppHostingBackend.location,
    backend: exampleAppHostingBackend.backendId,
    buildId: "target-build",
    source: {
        container: {
            image: "us-docker.pkg.dev/cloudrun/container/hello",
        },
    },
});
const example = new gcp.firebase.AppHostingTraffic("example", {
    project: exampleAppHostingBackend.project,
    location: exampleAppHostingBackend.location,
    backend: exampleAppHostingBackend.backendId,
    target: {
        splits: [{
            build: exampleAppHostingBuild.name,
            percent: 100,
        }],
    },
});
const appHostingSaRunner = new gcp.projects.IAMMember("app_hosting_sa_runner", {
    project: "my-project-name",
    role: "roles/firebaseapphosting.computeRunner",
    member: serviceAccount.member,
});
import pulumi
import pulumi_gcp as gcp

### Include these blocks only once per project if you are starting from scratch ###
service_account = gcp.serviceaccount.Account("service_account",
    project="my-project-name",
    account_id="firebase-app-hosting-compute",
    display_name="Firebase App Hosting compute service account",
    create_ignore_already_exists=True)
fah = gcp.projects.Service("fah",
    project="my-project-name",
    service="firebaseapphosting.googleapis.com")
example_app_hosting_backend = gcp.firebase.AppHostingBackend("example",
    project="my-project-name",
    location="asia-east1",
    backend_id="traffic-tg",
    app_id="1:0000000000:web:674cde32020e16fbce9dbd",
    serving_locality="GLOBAL_ACCESS",
    service_account=service_account.email,
    opts = pulumi.ResourceOptions(depends_on=[fah]))
example_app_hosting_build = gcp.firebase.AppHostingBuild("example",
    project=example_app_hosting_backend.project,
    location=example_app_hosting_backend.location,
    backend=example_app_hosting_backend.backend_id,
    build_id="target-build",
    source={
        "container": {
            "image": "us-docker.pkg.dev/cloudrun/container/hello",
        },
    })
example = gcp.firebase.AppHostingTraffic("example",
    project=example_app_hosting_backend.project,
    location=example_app_hosting_backend.location,
    backend=example_app_hosting_backend.backend_id,
    target={
        "splits": [{
            "build": example_app_hosting_build.name,
            "percent": 100,
        }],
    })
app_hosting_sa_runner = gcp.projects.IAMMember("app_hosting_sa_runner",
    project="my-project-name",
    role="roles/firebaseapphosting.computeRunner",
    member=service_account.member)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/firebase"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// ## Include these blocks only once per project if you are starting from scratch ###
		serviceAccount, err := serviceaccount.NewAccount(ctx, "service_account", &serviceaccount.AccountArgs{
			Project:                   pulumi.String("my-project-name"),
			AccountId:                 pulumi.String("firebase-app-hosting-compute"),
			DisplayName:               pulumi.String("Firebase App Hosting compute service account"),
			CreateIgnoreAlreadyExists: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		fah, err := projects.NewService(ctx, "fah", &projects.ServiceArgs{
			Project: pulumi.String("my-project-name"),
			Service: pulumi.String("firebaseapphosting.googleapis.com"),
		})
		if err != nil {
			return err
		}
		exampleAppHostingBackend, err := firebase.NewAppHostingBackend(ctx, "example", &firebase.AppHostingBackendArgs{
			Project:         pulumi.String("my-project-name"),
			Location:        pulumi.String("asia-east1"),
			BackendId:       pulumi.String("traffic-tg"),
			AppId:           pulumi.String("1:0000000000:web:674cde32020e16fbce9dbd"),
			ServingLocality: pulumi.String("GLOBAL_ACCESS"),
			ServiceAccount:  serviceAccount.Email,
		}, pulumi.DependsOn([]pulumi.Resource{
			fah,
		}))
		if err != nil {
			return err
		}
		exampleAppHostingBuild, err := firebase.NewAppHostingBuild(ctx, "example", &firebase.AppHostingBuildArgs{
			Project:  exampleAppHostingBackend.Project,
			Location: exampleAppHostingBackend.Location,
			Backend:  exampleAppHostingBackend.BackendId,
			BuildId:  pulumi.String("target-build"),
			Source: &firebase.AppHostingBuildSourceArgs{
				Container: &firebase.AppHostingBuildSourceContainerArgs{
					Image: pulumi.String("us-docker.pkg.dev/cloudrun/container/hello"),
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = firebase.NewAppHostingTraffic(ctx, "example", &firebase.AppHostingTrafficArgs{
			Project:  exampleAppHostingBackend.Project,
			Location: exampleAppHostingBackend.Location,
			Backend:  exampleAppHostingBackend.BackendId,
			Target: &firebase.AppHostingTrafficTargetArgs{
				Splits: firebase.AppHostingTrafficTargetSplitArray{
					&firebase.AppHostingTrafficTargetSplitArgs{
						Build:   exampleAppHostingBuild.Name,
						Percent: pulumi.Int(100),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = projects.NewIAMMember(ctx, "app_hosting_sa_runner", &projects.IAMMemberArgs{
			Project: pulumi.String("my-project-name"),
			Role:    pulumi.String("roles/firebaseapphosting.computeRunner"),
			Member:  serviceAccount.Member,
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    //## Include these blocks only once per project if you are starting from scratch ###
    var serviceAccount = new Gcp.ServiceAccount.Account("service_account", new()
    {
        Project = "my-project-name",
        AccountId = "firebase-app-hosting-compute",
        DisplayName = "Firebase App Hosting compute service account",
        CreateIgnoreAlreadyExists = true,
    });

    var fah = new Gcp.Projects.Service("fah", new()
    {
        Project = "my-project-name",
        ServiceName = "firebaseapphosting.googleapis.com",
    });

    var exampleAppHostingBackend = new Gcp.Firebase.AppHostingBackend("example", new()
    {
        Project = "my-project-name",
        Location = "asia-east1",
        BackendId = "traffic-tg",
        AppId = "1:0000000000:web:674cde32020e16fbce9dbd",
        ServingLocality = "GLOBAL_ACCESS",
        ServiceAccount = serviceAccount.Email,
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            fah,
        },
    });

    var exampleAppHostingBuild = new Gcp.Firebase.AppHostingBuild("example", new()
    {
        Project = exampleAppHostingBackend.Project,
        Location = exampleAppHostingBackend.Location,
        Backend = exampleAppHostingBackend.BackendId,
        BuildId = "target-build",
        Source = new Gcp.Firebase.Inputs.AppHostingBuildSourceArgs
        {
            Container = new Gcp.Firebase.Inputs.AppHostingBuildSourceContainerArgs
            {
                Image = "us-docker.pkg.dev/cloudrun/container/hello",
            },
        },
    });

    var example = new Gcp.Firebase.AppHostingTraffic("example", new()
    {
        Project = exampleAppHostingBackend.Project,
        Location = exampleAppHostingBackend.Location,
        Backend = exampleAppHostingBackend.BackendId,
        Target = new Gcp.Firebase.Inputs.AppHostingTrafficTargetArgs
        {
            Splits = new[]
            {
                new Gcp.Firebase.Inputs.AppHostingTrafficTargetSplitArgs
                {
                    Build = exampleAppHostingBuild.Name,
                    Percent = 100,
                },
            },
        },
    });

    var appHostingSaRunner = new Gcp.Projects.IAMMember("app_hosting_sa_runner", new()
    {
        Project = "my-project-name",
        Role = "roles/firebaseapphosting.computeRunner",
        Member = serviceAccount.Member,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.projects.Service;
import com.pulumi.gcp.projects.ServiceArgs;
import com.pulumi.gcp.firebase.AppHostingBackend;
import com.pulumi.gcp.firebase.AppHostingBackendArgs;
import com.pulumi.gcp.firebase.AppHostingBuild;
import com.pulumi.gcp.firebase.AppHostingBuildArgs;
import com.pulumi.gcp.firebase.inputs.AppHostingBuildSourceArgs;
import com.pulumi.gcp.firebase.inputs.AppHostingBuildSourceContainerArgs;
import com.pulumi.gcp.firebase.AppHostingTraffic;
import com.pulumi.gcp.firebase.AppHostingTrafficArgs;
import com.pulumi.gcp.firebase.inputs.AppHostingTrafficTargetArgs;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
        //## Include these blocks only once per project if you are starting from scratch ###
        var serviceAccount = new Account("serviceAccount", AccountArgs.builder()
            .project("my-project-name")
            .accountId("firebase-app-hosting-compute")
            .displayName("Firebase App Hosting compute service account")
            .createIgnoreAlreadyExists(true)
            .build());

        var fah = new Service("fah", ServiceArgs.builder()
            .project("my-project-name")
            .service("firebaseapphosting.googleapis.com")
            .build());

        var exampleAppHostingBackend = new AppHostingBackend("exampleAppHostingBackend", AppHostingBackendArgs.builder()
            .project("my-project-name")
            .location("asia-east1")
            .backendId("traffic-tg")
            .appId("1:0000000000:web:674cde32020e16fbce9dbd")
            .servingLocality("GLOBAL_ACCESS")
            .serviceAccount(serviceAccount.email())
            .build(), CustomResourceOptions.builder()
                .dependsOn(fah)
                .build());

        var exampleAppHostingBuild = new AppHostingBuild("exampleAppHostingBuild", AppHostingBuildArgs.builder()
            .project(exampleAppHostingBackend.project())
            .location(exampleAppHostingBackend.location())
            .backend(exampleAppHostingBackend.backendId())
            .buildId("target-build")
            .source(AppHostingBuildSourceArgs.builder()
                .container(AppHostingBuildSourceContainerArgs.builder()
                    .image("us-docker.pkg.dev/cloudrun/container/hello")
                    .build())
                .build())
            .build());

        var example = new AppHostingTraffic("example", AppHostingTrafficArgs.builder()
            .project(exampleAppHostingBackend.project())
            .location(exampleAppHostingBackend.location())
            .backend(exampleAppHostingBackend.backendId())
            .target(AppHostingTrafficTargetArgs.builder()
                .splits(AppHostingTrafficTargetSplitArgs.builder()
                    .build(exampleAppHostingBuild.name())
                    .percent(100)
                    .build())
                .build())
            .build());

        var appHostingSaRunner = new IAMMember("appHostingSaRunner", IAMMemberArgs.builder()
            .project("my-project-name")
            .role("roles/firebaseapphosting.computeRunner")
            .member(serviceAccount.member())
            .build());

    }
}
resources:
  example:
    type: gcp:firebase:AppHostingTraffic
    properties:
      project: ${exampleAppHostingBackend.project}
      location: ${exampleAppHostingBackend.location}
      backend: ${exampleAppHostingBackend.backendId}
      target:
        splits:
          - build: ${exampleAppHostingBuild.name}
            percent: 100
  exampleAppHostingBuild:
    type: gcp:firebase:AppHostingBuild
    name: example
    properties:
      project: ${exampleAppHostingBackend.project}
      location: ${exampleAppHostingBackend.location}
      backend: ${exampleAppHostingBackend.backendId}
      buildId: target-build
      source:
        container:
          image: us-docker.pkg.dev/cloudrun/container/hello
  exampleAppHostingBackend:
    type: gcp:firebase:AppHostingBackend
    name: example
    properties:
      project: my-project-name
      location: asia-east1
      backendId: traffic-tg
      appId: 1:0000000000:web:674cde32020e16fbce9dbd
      servingLocality: GLOBAL_ACCESS
      serviceAccount: ${serviceAccount.email}
    options:
      dependsOn:
        - ${fah}
  ### Include these blocks only once per project if you are starting from scratch ###
  serviceAccount:
    type: gcp:serviceaccount:Account
    name: service_account
    properties:
      project: my-project-name
      accountId: firebase-app-hosting-compute
      displayName: Firebase App Hosting compute service account
      createIgnoreAlreadyExists: true
  appHostingSaRunner:
    type: gcp:projects:IAMMember
    name: app_hosting_sa_runner
    properties:
      project: my-project-name
      role: roles/firebaseapphosting.computeRunner
      member: ${serviceAccount.member}
  fah:
    type: gcp:projects:Service
    properties:
      project: my-project-name
      service: firebaseapphosting.googleapis.com

The target property defines manual traffic routing. Inside splits, you specify which build receives traffic and what percentage. Setting percent to 100 directs all traffic to that build. This gives you explicit control over which version serves production requests.

Enable automatic rollouts from a Git branch

Once initial deployment is complete, automatic rollouts deploy new builds whenever code is pushed to a specific branch.

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

//## Include these blocks only once per project if you are starting from scratch ###
const serviceAccount = new gcp.serviceaccount.Account("service_account", {
    project: "my-project-name",
    accountId: "firebase-app-hosting-compute",
    displayName: "Firebase App Hosting compute service account",
    createIgnoreAlreadyExists: true,
});
const fah = new gcp.projects.Service("fah", {
    project: "my-project-name",
    service: "firebaseapphosting.googleapis.com",
});
const exampleAppHostingBackend = new gcp.firebase.AppHostingBackend("example", {
    project: "my-project-name",
    location: "asia-east1",
    backendId: "traffic-rp",
    appId: "1:0000000000:web:674cde32020e16fbce9dbd",
    servingLocality: "GLOBAL_ACCESS",
    serviceAccount: serviceAccount.email,
}, {
    dependsOn: [fah],
});
const example = new gcp.firebase.AppHostingTraffic("example", {
    project: exampleAppHostingBackend.project,
    location: exampleAppHostingBackend.location,
    backend: exampleAppHostingBackend.backendId,
    rolloutPolicy: {
        codebaseBranch: "main",
    },
});
const appHostingSaRunner = new gcp.projects.IAMMember("app_hosting_sa_runner", {
    project: "my-project-name",
    role: "roles/firebaseapphosting.computeRunner",
    member: serviceAccount.member,
});
import pulumi
import pulumi_gcp as gcp

### Include these blocks only once per project if you are starting from scratch ###
service_account = gcp.serviceaccount.Account("service_account",
    project="my-project-name",
    account_id="firebase-app-hosting-compute",
    display_name="Firebase App Hosting compute service account",
    create_ignore_already_exists=True)
fah = gcp.projects.Service("fah",
    project="my-project-name",
    service="firebaseapphosting.googleapis.com")
example_app_hosting_backend = gcp.firebase.AppHostingBackend("example",
    project="my-project-name",
    location="asia-east1",
    backend_id="traffic-rp",
    app_id="1:0000000000:web:674cde32020e16fbce9dbd",
    serving_locality="GLOBAL_ACCESS",
    service_account=service_account.email,
    opts = pulumi.ResourceOptions(depends_on=[fah]))
example = gcp.firebase.AppHostingTraffic("example",
    project=example_app_hosting_backend.project,
    location=example_app_hosting_backend.location,
    backend=example_app_hosting_backend.backend_id,
    rollout_policy={
        "codebase_branch": "main",
    })
app_hosting_sa_runner = gcp.projects.IAMMember("app_hosting_sa_runner",
    project="my-project-name",
    role="roles/firebaseapphosting.computeRunner",
    member=service_account.member)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/firebase"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// ## Include these blocks only once per project if you are starting from scratch ###
		serviceAccount, err := serviceaccount.NewAccount(ctx, "service_account", &serviceaccount.AccountArgs{
			Project:                   pulumi.String("my-project-name"),
			AccountId:                 pulumi.String("firebase-app-hosting-compute"),
			DisplayName:               pulumi.String("Firebase App Hosting compute service account"),
			CreateIgnoreAlreadyExists: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		fah, err := projects.NewService(ctx, "fah", &projects.ServiceArgs{
			Project: pulumi.String("my-project-name"),
			Service: pulumi.String("firebaseapphosting.googleapis.com"),
		})
		if err != nil {
			return err
		}
		exampleAppHostingBackend, err := firebase.NewAppHostingBackend(ctx, "example", &firebase.AppHostingBackendArgs{
			Project:         pulumi.String("my-project-name"),
			Location:        pulumi.String("asia-east1"),
			BackendId:       pulumi.String("traffic-rp"),
			AppId:           pulumi.String("1:0000000000:web:674cde32020e16fbce9dbd"),
			ServingLocality: pulumi.String("GLOBAL_ACCESS"),
			ServiceAccount:  serviceAccount.Email,
		}, pulumi.DependsOn([]pulumi.Resource{
			fah,
		}))
		if err != nil {
			return err
		}
		_, err = firebase.NewAppHostingTraffic(ctx, "example", &firebase.AppHostingTrafficArgs{
			Project:  exampleAppHostingBackend.Project,
			Location: exampleAppHostingBackend.Location,
			Backend:  exampleAppHostingBackend.BackendId,
			RolloutPolicy: &firebase.AppHostingTrafficRolloutPolicyArgs{
				CodebaseBranch: pulumi.String("main"),
			},
		})
		if err != nil {
			return err
		}
		_, err = projects.NewIAMMember(ctx, "app_hosting_sa_runner", &projects.IAMMemberArgs{
			Project: pulumi.String("my-project-name"),
			Role:    pulumi.String("roles/firebaseapphosting.computeRunner"),
			Member:  serviceAccount.Member,
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    //## Include these blocks only once per project if you are starting from scratch ###
    var serviceAccount = new Gcp.ServiceAccount.Account("service_account", new()
    {
        Project = "my-project-name",
        AccountId = "firebase-app-hosting-compute",
        DisplayName = "Firebase App Hosting compute service account",
        CreateIgnoreAlreadyExists = true,
    });

    var fah = new Gcp.Projects.Service("fah", new()
    {
        Project = "my-project-name",
        ServiceName = "firebaseapphosting.googleapis.com",
    });

    var exampleAppHostingBackend = new Gcp.Firebase.AppHostingBackend("example", new()
    {
        Project = "my-project-name",
        Location = "asia-east1",
        BackendId = "traffic-rp",
        AppId = "1:0000000000:web:674cde32020e16fbce9dbd",
        ServingLocality = "GLOBAL_ACCESS",
        ServiceAccount = serviceAccount.Email,
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            fah,
        },
    });

    var example = new Gcp.Firebase.AppHostingTraffic("example", new()
    {
        Project = exampleAppHostingBackend.Project,
        Location = exampleAppHostingBackend.Location,
        Backend = exampleAppHostingBackend.BackendId,
        RolloutPolicy = new Gcp.Firebase.Inputs.AppHostingTrafficRolloutPolicyArgs
        {
            CodebaseBranch = "main",
        },
    });

    var appHostingSaRunner = new Gcp.Projects.IAMMember("app_hosting_sa_runner", new()
    {
        Project = "my-project-name",
        Role = "roles/firebaseapphosting.computeRunner",
        Member = serviceAccount.Member,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.projects.Service;
import com.pulumi.gcp.projects.ServiceArgs;
import com.pulumi.gcp.firebase.AppHostingBackend;
import com.pulumi.gcp.firebase.AppHostingBackendArgs;
import com.pulumi.gcp.firebase.AppHostingTraffic;
import com.pulumi.gcp.firebase.AppHostingTrafficArgs;
import com.pulumi.gcp.firebase.inputs.AppHostingTrafficRolloutPolicyArgs;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
        //## Include these blocks only once per project if you are starting from scratch ###
        var serviceAccount = new Account("serviceAccount", AccountArgs.builder()
            .project("my-project-name")
            .accountId("firebase-app-hosting-compute")
            .displayName("Firebase App Hosting compute service account")
            .createIgnoreAlreadyExists(true)
            .build());

        var fah = new Service("fah", ServiceArgs.builder()
            .project("my-project-name")
            .service("firebaseapphosting.googleapis.com")
            .build());

        var exampleAppHostingBackend = new AppHostingBackend("exampleAppHostingBackend", AppHostingBackendArgs.builder()
            .project("my-project-name")
            .location("asia-east1")
            .backendId("traffic-rp")
            .appId("1:0000000000:web:674cde32020e16fbce9dbd")
            .servingLocality("GLOBAL_ACCESS")
            .serviceAccount(serviceAccount.email())
            .build(), CustomResourceOptions.builder()
                .dependsOn(fah)
                .build());

        var example = new AppHostingTraffic("example", AppHostingTrafficArgs.builder()
            .project(exampleAppHostingBackend.project())
            .location(exampleAppHostingBackend.location())
            .backend(exampleAppHostingBackend.backendId())
            .rolloutPolicy(AppHostingTrafficRolloutPolicyArgs.builder()
                .codebaseBranch("main")
                .build())
            .build());

        var appHostingSaRunner = new IAMMember("appHostingSaRunner", IAMMemberArgs.builder()
            .project("my-project-name")
            .role("roles/firebaseapphosting.computeRunner")
            .member(serviceAccount.member())
            .build());

    }
}
resources:
  example:
    type: gcp:firebase:AppHostingTraffic
    properties:
      project: ${exampleAppHostingBackend.project}
      location: ${exampleAppHostingBackend.location}
      backend: ${exampleAppHostingBackend.backendId}
      rolloutPolicy:
        codebaseBranch: main
  exampleAppHostingBackend:
    type: gcp:firebase:AppHostingBackend
    name: example
    properties:
      project: my-project-name
      location: asia-east1
      backendId: traffic-rp
      appId: 1:0000000000:web:674cde32020e16fbce9dbd
      servingLocality: GLOBAL_ACCESS
      serviceAccount: ${serviceAccount.email}
    options:
      dependsOn:
        - ${fah}
  ### Include these blocks only once per project if you are starting from scratch ###
  serviceAccount:
    type: gcp:serviceaccount:Account
    name: service_account
    properties:
      project: my-project-name
      accountId: firebase-app-hosting-compute
      displayName: Firebase App Hosting compute service account
      createIgnoreAlreadyExists: true
  appHostingSaRunner:
    type: gcp:projects:IAMMember
    name: app_hosting_sa_runner
    properties:
      project: my-project-name
      role: roles/firebaseapphosting.computeRunner
      member: ${serviceAccount.member}
  fah:
    type: gcp:projects:Service
    properties:
      project: my-project-name
      service: firebaseapphosting.googleapis.com

The rolloutPolicy property enables automatic deployments. The codebaseBranch property specifies which Git branch triggers new rollouts. When you push to this branch, Firebase App Hosting automatically builds and deploys the new version without requiring manual traffic split updates.

Pause automatic rollouts while keeping policy configuration

During maintenance or troubleshooting, you may need to pause automatic deployments without removing the rollout policy entirely.

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

//## Include these blocks only once per project if you are starting from scratch ###
const serviceAccount = new gcp.serviceaccount.Account("service_account", {
    project: "my-project-name",
    accountId: "firebase-app-hosting-compute",
    displayName: "Firebase App Hosting compute service account",
    createIgnoreAlreadyExists: true,
});
const fah = new gcp.projects.Service("fah", {
    project: "my-project-name",
    service: "firebaseapphosting.googleapis.com",
});
const exampleAppHostingBackend = new gcp.firebase.AppHostingBackend("example", {
    project: "my-project-name",
    location: "asia-east1",
    backendId: "traffic-rpd",
    appId: "1:0000000000:web:674cde32020e16fbce9dbd",
    servingLocality: "GLOBAL_ACCESS",
    serviceAccount: serviceAccount.email,
}, {
    dependsOn: [fah],
});
const example = new gcp.firebase.AppHostingTraffic("example", {
    project: exampleAppHostingBackend.project,
    location: exampleAppHostingBackend.location,
    backend: exampleAppHostingBackend.backendId,
    rolloutPolicy: {
        disabled: true,
        codebaseBranch: "main",
    },
});
const appHostingSaRunner = new gcp.projects.IAMMember("app_hosting_sa_runner", {
    project: "my-project-name",
    role: "roles/firebaseapphosting.computeRunner",
    member: serviceAccount.member,
});
import pulumi
import pulumi_gcp as gcp

### Include these blocks only once per project if you are starting from scratch ###
service_account = gcp.serviceaccount.Account("service_account",
    project="my-project-name",
    account_id="firebase-app-hosting-compute",
    display_name="Firebase App Hosting compute service account",
    create_ignore_already_exists=True)
fah = gcp.projects.Service("fah",
    project="my-project-name",
    service="firebaseapphosting.googleapis.com")
example_app_hosting_backend = gcp.firebase.AppHostingBackend("example",
    project="my-project-name",
    location="asia-east1",
    backend_id="traffic-rpd",
    app_id="1:0000000000:web:674cde32020e16fbce9dbd",
    serving_locality="GLOBAL_ACCESS",
    service_account=service_account.email,
    opts = pulumi.ResourceOptions(depends_on=[fah]))
example = gcp.firebase.AppHostingTraffic("example",
    project=example_app_hosting_backend.project,
    location=example_app_hosting_backend.location,
    backend=example_app_hosting_backend.backend_id,
    rollout_policy={
        "disabled": True,
        "codebase_branch": "main",
    })
app_hosting_sa_runner = gcp.projects.IAMMember("app_hosting_sa_runner",
    project="my-project-name",
    role="roles/firebaseapphosting.computeRunner",
    member=service_account.member)
package main

import (
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/firebase"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/projects"
	"github.com/pulumi/pulumi-gcp/sdk/v9/go/gcp/serviceaccount"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// ## Include these blocks only once per project if you are starting from scratch ###
		serviceAccount, err := serviceaccount.NewAccount(ctx, "service_account", &serviceaccount.AccountArgs{
			Project:                   pulumi.String("my-project-name"),
			AccountId:                 pulumi.String("firebase-app-hosting-compute"),
			DisplayName:               pulumi.String("Firebase App Hosting compute service account"),
			CreateIgnoreAlreadyExists: pulumi.Bool(true),
		})
		if err != nil {
			return err
		}
		fah, err := projects.NewService(ctx, "fah", &projects.ServiceArgs{
			Project: pulumi.String("my-project-name"),
			Service: pulumi.String("firebaseapphosting.googleapis.com"),
		})
		if err != nil {
			return err
		}
		exampleAppHostingBackend, err := firebase.NewAppHostingBackend(ctx, "example", &firebase.AppHostingBackendArgs{
			Project:         pulumi.String("my-project-name"),
			Location:        pulumi.String("asia-east1"),
			BackendId:       pulumi.String("traffic-rpd"),
			AppId:           pulumi.String("1:0000000000:web:674cde32020e16fbce9dbd"),
			ServingLocality: pulumi.String("GLOBAL_ACCESS"),
			ServiceAccount:  serviceAccount.Email,
		}, pulumi.DependsOn([]pulumi.Resource{
			fah,
		}))
		if err != nil {
			return err
		}
		_, err = firebase.NewAppHostingTraffic(ctx, "example", &firebase.AppHostingTrafficArgs{
			Project:  exampleAppHostingBackend.Project,
			Location: exampleAppHostingBackend.Location,
			Backend:  exampleAppHostingBackend.BackendId,
			RolloutPolicy: &firebase.AppHostingTrafficRolloutPolicyArgs{
				Disabled:       pulumi.Bool(true),
				CodebaseBranch: pulumi.String("main"),
			},
		})
		if err != nil {
			return err
		}
		_, err = projects.NewIAMMember(ctx, "app_hosting_sa_runner", &projects.IAMMemberArgs{
			Project: pulumi.String("my-project-name"),
			Role:    pulumi.String("roles/firebaseapphosting.computeRunner"),
			Member:  serviceAccount.Member,
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Gcp = Pulumi.Gcp;

return await Deployment.RunAsync(() => 
{
    //## Include these blocks only once per project if you are starting from scratch ###
    var serviceAccount = new Gcp.ServiceAccount.Account("service_account", new()
    {
        Project = "my-project-name",
        AccountId = "firebase-app-hosting-compute",
        DisplayName = "Firebase App Hosting compute service account",
        CreateIgnoreAlreadyExists = true,
    });

    var fah = new Gcp.Projects.Service("fah", new()
    {
        Project = "my-project-name",
        ServiceName = "firebaseapphosting.googleapis.com",
    });

    var exampleAppHostingBackend = new Gcp.Firebase.AppHostingBackend("example", new()
    {
        Project = "my-project-name",
        Location = "asia-east1",
        BackendId = "traffic-rpd",
        AppId = "1:0000000000:web:674cde32020e16fbce9dbd",
        ServingLocality = "GLOBAL_ACCESS",
        ServiceAccount = serviceAccount.Email,
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            fah,
        },
    });

    var example = new Gcp.Firebase.AppHostingTraffic("example", new()
    {
        Project = exampleAppHostingBackend.Project,
        Location = exampleAppHostingBackend.Location,
        Backend = exampleAppHostingBackend.BackendId,
        RolloutPolicy = new Gcp.Firebase.Inputs.AppHostingTrafficRolloutPolicyArgs
        {
            Disabled = true,
            CodebaseBranch = "main",
        },
    });

    var appHostingSaRunner = new Gcp.Projects.IAMMember("app_hosting_sa_runner", new()
    {
        Project = "my-project-name",
        Role = "roles/firebaseapphosting.computeRunner",
        Member = serviceAccount.Member,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.serviceaccount.Account;
import com.pulumi.gcp.serviceaccount.AccountArgs;
import com.pulumi.gcp.projects.Service;
import com.pulumi.gcp.projects.ServiceArgs;
import com.pulumi.gcp.firebase.AppHostingBackend;
import com.pulumi.gcp.firebase.AppHostingBackendArgs;
import com.pulumi.gcp.firebase.AppHostingTraffic;
import com.pulumi.gcp.firebase.AppHostingTrafficArgs;
import com.pulumi.gcp.firebase.inputs.AppHostingTrafficRolloutPolicyArgs;
import com.pulumi.gcp.projects.IAMMember;
import com.pulumi.gcp.projects.IAMMemberArgs;
import com.pulumi.resources.CustomResourceOptions;
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) {
        //## Include these blocks only once per project if you are starting from scratch ###
        var serviceAccount = new Account("serviceAccount", AccountArgs.builder()
            .project("my-project-name")
            .accountId("firebase-app-hosting-compute")
            .displayName("Firebase App Hosting compute service account")
            .createIgnoreAlreadyExists(true)
            .build());

        var fah = new Service("fah", ServiceArgs.builder()
            .project("my-project-name")
            .service("firebaseapphosting.googleapis.com")
            .build());

        var exampleAppHostingBackend = new AppHostingBackend("exampleAppHostingBackend", AppHostingBackendArgs.builder()
            .project("my-project-name")
            .location("asia-east1")
            .backendId("traffic-rpd")
            .appId("1:0000000000:web:674cde32020e16fbce9dbd")
            .servingLocality("GLOBAL_ACCESS")
            .serviceAccount(serviceAccount.email())
            .build(), CustomResourceOptions.builder()
                .dependsOn(fah)
                .build());

        var example = new AppHostingTraffic("example", AppHostingTrafficArgs.builder()
            .project(exampleAppHostingBackend.project())
            .location(exampleAppHostingBackend.location())
            .backend(exampleAppHostingBackend.backendId())
            .rolloutPolicy(AppHostingTrafficRolloutPolicyArgs.builder()
                .disabled(true)
                .codebaseBranch("main")
                .build())
            .build());

        var appHostingSaRunner = new IAMMember("appHostingSaRunner", IAMMemberArgs.builder()
            .project("my-project-name")
            .role("roles/firebaseapphosting.computeRunner")
            .member(serviceAccount.member())
            .build());

    }
}
resources:
  example:
    type: gcp:firebase:AppHostingTraffic
    properties:
      project: ${exampleAppHostingBackend.project}
      location: ${exampleAppHostingBackend.location}
      backend: ${exampleAppHostingBackend.backendId}
      rolloutPolicy:
        disabled: true
        codebaseBranch: main
  exampleAppHostingBackend:
    type: gcp:firebase:AppHostingBackend
    name: example
    properties:
      project: my-project-name
      location: asia-east1
      backendId: traffic-rpd
      appId: 1:0000000000:web:674cde32020e16fbce9dbd
      servingLocality: GLOBAL_ACCESS
      serviceAccount: ${serviceAccount.email}
    options:
      dependsOn:
        - ${fah}
  ### Include these blocks only once per project if you are starting from scratch ###
  serviceAccount:
    type: gcp:serviceaccount:Account
    name: service_account
    properties:
      project: my-project-name
      accountId: firebase-app-hosting-compute
      displayName: Firebase App Hosting compute service account
      createIgnoreAlreadyExists: true
  appHostingSaRunner:
    type: gcp:projects:IAMMember
    name: app_hosting_sa_runner
    properties:
      project: my-project-name
      role: roles/firebaseapphosting.computeRunner
      member: ${serviceAccount.member}
  fah:
    type: gcp:projects:Service
    properties:
      project: my-project-name
      service: firebaseapphosting.googleapis.com

Setting disabled to true within rolloutPolicy pauses automatic rollouts while preserving the codebaseBranch configuration. This lets you temporarily stop deployments without reconfiguring the policy when you’re ready to resume.

Beyond these examples

These snippets focus on specific traffic configuration features: manual traffic splits to specific builds, automatic rollouts from Git branches, and rollout policy pause controls. They’re intentionally minimal rather than full deployment pipelines.

The examples reference pre-existing infrastructure such as Firebase App Hosting backend (AppHostingBackend), Firebase App Hosting build (AppHostingBuild for manual splits), service account with firebaseapphosting.computeRunner role, Firebase project and app configuration, and Git repository integration (for automatic rollouts). They focus on traffic routing configuration rather than provisioning the underlying backend or build resources.

To keep things focused, common traffic patterns are omitted, including:

  • Multi-build traffic splits (canary deployments with multiple builds)
  • Gradual rollout percentages (splitting traffic between builds)
  • Traffic monitoring and rollback strategies
  • Build trigger configuration and CI/CD integration

These omissions are intentional: the goal is to illustrate how traffic routing is wired, not provide drop-in deployment pipelines. See the Firebase App Hosting Traffic resource reference for all available configuration options.

Let's configure GCP Firebase App Hosting Traffic

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Traffic Control Modes
What's the difference between target and rolloutPolicy?
target lets you manually control traffic by specifying builds and percentages. rolloutPolicy enables automated rollouts triggered by code changes on a specified branch. Use target for manual control, rolloutPolicy for continuous deployment.
Can I disable the rollout policy without removing it?
Yes, set rolloutPolicy.disabled to true while keeping codebaseBranch configured. This pauses automated rollouts without losing your configuration.
Configuration & Setup
What IAM role does the service account need?
The service account requires roles/firebaseapphosting.computeRunner to run the backend.
What API must be enabled before creating traffic configuration?
Enable firebaseapphosting.googleapis.com in your project. The examples show this as a dependency using gcp.projects.Service.
Traffic Management
How do I split traffic between multiple builds?
Use target.splits with an array of builds and their percentages. Each split specifies a build name and percent value. The percentages must total 100.
What happens if my traffic percentages don't add up to 100?
The percentages in target.splits must add up to exactly 100, as stated in the property description.
Resource Behavior
Why doesn't my current traffic match what I configured in target?
Traffic changes are eventually consistent. The currents output shows actual traffic allocation and may differ from target for some time until the desired state is reached.
What properties can't I change after creation?
The backend, location, and project properties are immutable and cannot be changed after the resource is created.

Using a different cloud?

Explore integration guides for other cloud providers: