Deploy GCP Firebase Hosting Releases

The gcp:firebase/hostingRelease:HostingRelease resource, part of the Pulumi GCP provider, controls which Firebase Hosting version is publicly accessible on a site or channel. This guide focuses on three capabilities: deploying versions to live sites, publishing to preview channels, and disabling site access.

Releases depend on existing HostingSite and HostingVersion resources. Preview deployments also require HostingChannel. The examples are intentionally small. Combine them with your own site configuration, content versions, and channel strategy.

Deploy a version to the live site

Most deployments publish content to the default “live” channel, making it immediately accessible to all visitors.

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

const _default = new gcp.firebase.HostingSite("default", {
    project: "my-project-name",
    siteId: "site-id",
});
const defaultHostingVersion = new gcp.firebase.HostingVersion("default", {
    siteId: _default.siteId,
    config: {
        redirects: [{
            glob: "/google/**",
            statusCode: 302,
            location: "https://www.google.com",
        }],
    },
});
const defaultHostingRelease = new gcp.firebase.HostingRelease("default", {
    siteId: _default.siteId,
    versionName: defaultHostingVersion.name,
    message: "Test release",
});
import pulumi
import pulumi_gcp as gcp

default = gcp.firebase.HostingSite("default",
    project="my-project-name",
    site_id="site-id")
default_hosting_version = gcp.firebase.HostingVersion("default",
    site_id=default.site_id,
    config={
        "redirects": [{
            "glob": "/google/**",
            "status_code": 302,
            "location": "https://www.google.com",
        }],
    })
default_hosting_release = gcp.firebase.HostingRelease("default",
    site_id=default.site_id,
    version_name=default_hosting_version.name,
    message="Test release")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := firebase.NewHostingSite(ctx, "default", &firebase.HostingSiteArgs{
			Project: pulumi.String("my-project-name"),
			SiteId:  pulumi.String("site-id"),
		})
		if err != nil {
			return err
		}
		defaultHostingVersion, err := firebase.NewHostingVersion(ctx, "default", &firebase.HostingVersionArgs{
			SiteId: _default.SiteId,
			Config: &firebase.HostingVersionConfigArgs{
				Redirects: firebase.HostingVersionConfigRedirectArray{
					&firebase.HostingVersionConfigRedirectArgs{
						Glob:       pulumi.String("/google/**"),
						StatusCode: pulumi.Int(302),
						Location:   pulumi.String("https://www.google.com"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		_, err = firebase.NewHostingRelease(ctx, "default", &firebase.HostingReleaseArgs{
			SiteId:      _default.SiteId,
			VersionName: defaultHostingVersion.Name,
			Message:     pulumi.String("Test release"),
		})
		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 @default = new Gcp.Firebase.HostingSite("default", new()
    {
        Project = "my-project-name",
        SiteId = "site-id",
    });

    var defaultHostingVersion = new Gcp.Firebase.HostingVersion("default", new()
    {
        SiteId = @default.SiteId,
        Config = new Gcp.Firebase.Inputs.HostingVersionConfigArgs
        {
            Redirects = new[]
            {
                new Gcp.Firebase.Inputs.HostingVersionConfigRedirectArgs
                {
                    Glob = "/google/**",
                    StatusCode = 302,
                    Location = "https://www.google.com",
                },
            },
        },
    });

    var defaultHostingRelease = new Gcp.Firebase.HostingRelease("default", new()
    {
        SiteId = @default.SiteId,
        VersionName = defaultHostingVersion.Name,
        Message = "Test release",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firebase.HostingSite;
import com.pulumi.gcp.firebase.HostingSiteArgs;
import com.pulumi.gcp.firebase.HostingVersion;
import com.pulumi.gcp.firebase.HostingVersionArgs;
import com.pulumi.gcp.firebase.inputs.HostingVersionConfigArgs;
import com.pulumi.gcp.firebase.HostingRelease;
import com.pulumi.gcp.firebase.HostingReleaseArgs;
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 default_ = new HostingSite("default", HostingSiteArgs.builder()
            .project("my-project-name")
            .siteId("site-id")
            .build());

        var defaultHostingVersion = new HostingVersion("defaultHostingVersion", HostingVersionArgs.builder()
            .siteId(default_.siteId())
            .config(HostingVersionConfigArgs.builder()
                .redirects(HostingVersionConfigRedirectArgs.builder()
                    .glob("/google/**")
                    .statusCode(302)
                    .location("https://www.google.com")
                    .build())
                .build())
            .build());

        var defaultHostingRelease = new HostingRelease("defaultHostingRelease", HostingReleaseArgs.builder()
            .siteId(default_.siteId())
            .versionName(defaultHostingVersion.name())
            .message("Test release")
            .build());

    }
}
resources:
  default:
    type: gcp:firebase:HostingSite
    properties:
      project: my-project-name
      siteId: site-id
  defaultHostingVersion:
    type: gcp:firebase:HostingVersion
    name: default
    properties:
      siteId: ${default.siteId}
      config:
        redirects:
          - glob: /google/**
            statusCode: 302
            location: https://www.google.com
  defaultHostingRelease:
    type: gcp:firebase:HostingRelease
    name: default
    properties:
      siteId: ${default.siteId}
      versionName: ${defaultHostingVersion.name}
      message: Test release

When you create a release with a versionName, Firebase Hosting makes that version’s content publicly available. The siteId identifies which site receives the deployment. The message property documents what changed in this release. Without a channelId, the release targets the default “live” channel.

Deploy a version to a preview channel

Preview channels let teams test changes in isolated environments before promoting to production.

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

const _default = new gcp.firebase.HostingSite("default", {
    project: "my-project-name",
    siteId: "site-with-channel",
});
const defaultHostingVersion = new gcp.firebase.HostingVersion("default", {
    siteId: _default.siteId,
    config: {
        redirects: [{
            glob: "/google/**",
            statusCode: 302,
            location: "https://www.google.com",
        }],
    },
});
const defaultHostingChannel = new gcp.firebase.HostingChannel("default", {
    siteId: _default.siteId,
    channelId: "channel-id",
});
const defaultHostingRelease = new gcp.firebase.HostingRelease("default", {
    siteId: _default.siteId,
    channelId: defaultHostingChannel.channelId,
    versionName: defaultHostingVersion.name,
    message: "Test release in channel",
});
import pulumi
import pulumi_gcp as gcp

default = gcp.firebase.HostingSite("default",
    project="my-project-name",
    site_id="site-with-channel")
default_hosting_version = gcp.firebase.HostingVersion("default",
    site_id=default.site_id,
    config={
        "redirects": [{
            "glob": "/google/**",
            "status_code": 302,
            "location": "https://www.google.com",
        }],
    })
default_hosting_channel = gcp.firebase.HostingChannel("default",
    site_id=default.site_id,
    channel_id="channel-id")
default_hosting_release = gcp.firebase.HostingRelease("default",
    site_id=default.site_id,
    channel_id=default_hosting_channel.channel_id,
    version_name=default_hosting_version.name,
    message="Test release in channel")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := firebase.NewHostingSite(ctx, "default", &firebase.HostingSiteArgs{
			Project: pulumi.String("my-project-name"),
			SiteId:  pulumi.String("site-with-channel"),
		})
		if err != nil {
			return err
		}
		defaultHostingVersion, err := firebase.NewHostingVersion(ctx, "default", &firebase.HostingVersionArgs{
			SiteId: _default.SiteId,
			Config: &firebase.HostingVersionConfigArgs{
				Redirects: firebase.HostingVersionConfigRedirectArray{
					&firebase.HostingVersionConfigRedirectArgs{
						Glob:       pulumi.String("/google/**"),
						StatusCode: pulumi.Int(302),
						Location:   pulumi.String("https://www.google.com"),
					},
				},
			},
		})
		if err != nil {
			return err
		}
		defaultHostingChannel, err := firebase.NewHostingChannel(ctx, "default", &firebase.HostingChannelArgs{
			SiteId:    _default.SiteId,
			ChannelId: pulumi.String("channel-id"),
		})
		if err != nil {
			return err
		}
		_, err = firebase.NewHostingRelease(ctx, "default", &firebase.HostingReleaseArgs{
			SiteId:      _default.SiteId,
			ChannelId:   defaultHostingChannel.ChannelId,
			VersionName: defaultHostingVersion.Name,
			Message:     pulumi.String("Test release in channel"),
		})
		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 @default = new Gcp.Firebase.HostingSite("default", new()
    {
        Project = "my-project-name",
        SiteId = "site-with-channel",
    });

    var defaultHostingVersion = new Gcp.Firebase.HostingVersion("default", new()
    {
        SiteId = @default.SiteId,
        Config = new Gcp.Firebase.Inputs.HostingVersionConfigArgs
        {
            Redirects = new[]
            {
                new Gcp.Firebase.Inputs.HostingVersionConfigRedirectArgs
                {
                    Glob = "/google/**",
                    StatusCode = 302,
                    Location = "https://www.google.com",
                },
            },
        },
    });

    var defaultHostingChannel = new Gcp.Firebase.HostingChannel("default", new()
    {
        SiteId = @default.SiteId,
        ChannelId = "channel-id",
    });

    var defaultHostingRelease = new Gcp.Firebase.HostingRelease("default", new()
    {
        SiteId = @default.SiteId,
        ChannelId = defaultHostingChannel.ChannelId,
        VersionName = defaultHostingVersion.Name,
        Message = "Test release in channel",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firebase.HostingSite;
import com.pulumi.gcp.firebase.HostingSiteArgs;
import com.pulumi.gcp.firebase.HostingVersion;
import com.pulumi.gcp.firebase.HostingVersionArgs;
import com.pulumi.gcp.firebase.inputs.HostingVersionConfigArgs;
import com.pulumi.gcp.firebase.HostingChannel;
import com.pulumi.gcp.firebase.HostingChannelArgs;
import com.pulumi.gcp.firebase.HostingRelease;
import com.pulumi.gcp.firebase.HostingReleaseArgs;
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 default_ = new HostingSite("default", HostingSiteArgs.builder()
            .project("my-project-name")
            .siteId("site-with-channel")
            .build());

        var defaultHostingVersion = new HostingVersion("defaultHostingVersion", HostingVersionArgs.builder()
            .siteId(default_.siteId())
            .config(HostingVersionConfigArgs.builder()
                .redirects(HostingVersionConfigRedirectArgs.builder()
                    .glob("/google/**")
                    .statusCode(302)
                    .location("https://www.google.com")
                    .build())
                .build())
            .build());

        var defaultHostingChannel = new HostingChannel("defaultHostingChannel", HostingChannelArgs.builder()
            .siteId(default_.siteId())
            .channelId("channel-id")
            .build());

        var defaultHostingRelease = new HostingRelease("defaultHostingRelease", HostingReleaseArgs.builder()
            .siteId(default_.siteId())
            .channelId(defaultHostingChannel.channelId())
            .versionName(defaultHostingVersion.name())
            .message("Test release in channel")
            .build());

    }
}
resources:
  default:
    type: gcp:firebase:HostingSite
    properties:
      project: my-project-name
      siteId: site-with-channel
  defaultHostingVersion:
    type: gcp:firebase:HostingVersion
    name: default
    properties:
      siteId: ${default.siteId}
      config:
        redirects:
          - glob: /google/**
            statusCode: 302
            location: https://www.google.com
  defaultHostingChannel:
    type: gcp:firebase:HostingChannel
    name: default
    properties:
      siteId: ${default.siteId}
      channelId: channel-id
  defaultHostingRelease:
    type: gcp:firebase:HostingRelease
    name: default
    properties:
      siteId: ${default.siteId}
      channelId: ${defaultHostingChannel.channelId}
      versionName: ${defaultHostingVersion.name}
      message: Test release in channel

The channelId property routes the release to a specific preview channel instead of the live site. Each channel gets its own URL for testing. This configuration extends basic deployment by adding channel isolation, letting you validate changes before making them public.

Take a site offline without deleting it

During maintenance or decommissioning, you can disable sites while preserving their configuration.

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

const _default = new gcp.firebase.HostingSite("default", {
    project: "my-project-name",
    siteId: "site-id",
});
const defaultHostingRelease = new gcp.firebase.HostingRelease("default", {
    siteId: _default.siteId,
    type: "SITE_DISABLE",
    message: "Take down site",
});
import pulumi
import pulumi_gcp as gcp

default = gcp.firebase.HostingSite("default",
    project="my-project-name",
    site_id="site-id")
default_hosting_release = gcp.firebase.HostingRelease("default",
    site_id=default.site_id,
    type="SITE_DISABLE",
    message="Take down site")
package main

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := firebase.NewHostingSite(ctx, "default", &firebase.HostingSiteArgs{
			Project: pulumi.String("my-project-name"),
			SiteId:  pulumi.String("site-id"),
		})
		if err != nil {
			return err
		}
		_, err = firebase.NewHostingRelease(ctx, "default", &firebase.HostingReleaseArgs{
			SiteId:  _default.SiteId,
			Type:    pulumi.String("SITE_DISABLE"),
			Message: pulumi.String("Take down site"),
		})
		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 @default = new Gcp.Firebase.HostingSite("default", new()
    {
        Project = "my-project-name",
        SiteId = "site-id",
    });

    var defaultHostingRelease = new Gcp.Firebase.HostingRelease("default", new()
    {
        SiteId = @default.SiteId,
        Type = "SITE_DISABLE",
        Message = "Take down site",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.gcp.firebase.HostingSite;
import com.pulumi.gcp.firebase.HostingSiteArgs;
import com.pulumi.gcp.firebase.HostingRelease;
import com.pulumi.gcp.firebase.HostingReleaseArgs;
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 default_ = new HostingSite("default", HostingSiteArgs.builder()
            .project("my-project-name")
            .siteId("site-id")
            .build());

        var defaultHostingRelease = new HostingRelease("defaultHostingRelease", HostingReleaseArgs.builder()
            .siteId(default_.siteId())
            .type("SITE_DISABLE")
            .message("Take down site")
            .build());

    }
}
resources:
  default:
    type: gcp:firebase:HostingSite
    properties:
      project: my-project-name
      siteId: site-id
  defaultHostingRelease:
    type: gcp:firebase:HostingRelease
    name: default
    properties:
      siteId: ${default.siteId}
      type: SITE_DISABLE
      message: Take down site

Setting type to SITE_DISABLE makes Firebase Hosting act as if the site never existed, blocking all traffic. This release type doesn’t reference a versionName because it’s not deploying content. The site remains configured and can be re-enabled by deploying a new version.

Beyond these examples

These snippets focus on specific release-level features: live site deployment, preview channel isolation, and site disabling. They’re intentionally minimal rather than full hosting configurations.

The examples reference pre-existing infrastructure such as Firebase Hosting sites (HostingSite), hosting versions with content configuration (HostingVersion), and preview channels (HostingChannel) for non-production deployments. They focus on controlling version visibility rather than defining site content or infrastructure.

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

  • Rollback to previous versions (type: ROLLBACK)
  • Version content configuration (redirects, rewrites, headers)
  • Custom domain setup and SSL certificates
  • Release scheduling and automation

These omissions are intentional: the goal is to illustrate how each release feature is wired, not provide drop-in hosting modules. See the Firebase Hosting Release resource reference for all available configuration options.

Let's deploy GCP Firebase Hosting Releases

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Provider & Setup
Why do I need to use the beta provider for Firebase Hosting releases?
This resource is in beta and requires the terraform-provider-google-beta provider.
Release Types & Deployment
Do I need to specify the release type when deploying a version?
No, the type is automatically inferred as DEPLOY or ROLLBACK when you provide versionName. You only need to explicitly set type when using SITE_DISABLE.
How do I disable or take down my Firebase Hosting site?
Create a release with type set to SITE_DISABLE and omit the versionName property.
Why can't I use versionName with SITE_DISABLE?
The versionName parameter must be empty when the release type is SITE_DISABLE, as disabling a site doesn’t deploy any version.
Channels & Targeting
What's the difference between releasing to a site vs a channel?
Omitting channelId releases to the default “live” channel (your main site). Specifying channelId releases to a preview channel for testing before going live.
What happens if I don't specify a channelId?
The release will belong to the default “live” channel, making it publicly accessible on your main site URL.
Configuration & Constraints
What properties can I change after creating a release?
None. All properties (siteId, type, channelId, message, versionName) are immutable and require resource replacement to change.
What are the requirements for versionName?
The versionName must be in the format sites/SITE_ID/versions/VERSION_ID and the version must belong to the same site specified in siteId.
What's the maximum length for a release message?
The message property can be up to 512 characters.

Using a different cloud?

Explore integration guides for other cloud providers: