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, either on the live site or in preview channels. This guide focuses on three capabilities: publishing versions to live sites, deploying to preview channels, and disabling site content.

Releases reference HostingSite and HostingVersion resources that define the site identity and content configuration. The examples are intentionally small. Combine them with your own version content and channel strategy.

Deploy a version to the live site

Most deployments publish content to the default “live” channel, making it immediately visible at the main site URL.

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 accessible. The siteId identifies which site receives the deployment, and the message property records deployment context for tracking. Without a channelId, the release targets the default “live” channel.

Deploy a version to a preview channel

Teams use preview channels to test changes at separate URLs 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 rather than the live site. This creates a unique URL (typically https://SITE_ID--CHANNEL_ID.web.app) where stakeholders can review content. The version remains isolated from live traffic until you create a separate release targeting the live channel.

Take a site offline without deleting it

During maintenance or decommissioning, you can disable hosting without removing the site resource.

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 stops Firebase Hosting from serving any content, as if the site never existed. This configuration omits versionName because disabling doesn’t reference specific content. The site resource remains intact, allowing you to re-enable hosting later by creating a new release with a versionName.

Beyond these examples

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

The examples rely on pre-existing infrastructure such as Firebase projects with Hosting enabled and HostingSite resources. They focus on controlling version visibility rather than defining version content.

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

  • Version content configuration (redirects, rewrites, headers)
  • Rollback to previous versions (ROLLBACK type)
  • Channel expiration and retention policies
  • Custom domain configuration

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

Release Types & Deployment
Do I need to specify the type property when creating a release?
No, if you provide a versionName, the type is automatically determined as DEPLOY or ROLLBACK. You only need to explicitly set type when using SITE_DISABLE.
How do I disable my Firebase Hosting site?
Create a release with type: "SITE_DISABLE" and omit the versionName property. This prevents the site from serving content.
Why can't I provide a versionName when disabling my site?
The versionName parameter must be empty when type is SITE_DISABLE. Disabling a site doesn’t deploy any version.
Channels & Targeting
What's the difference between deploying to a site versus a channel?
Without a channelId, releases deploy to the default “live” channel (your main site). Providing a channelId deploys to a specific preview channel.
How do I deploy to a specific channel instead of the live site?
Set the channelId property to your target channel’s ID. Create the channel first using gcp.firebase.HostingChannel.
Configuration & Constraints
Can I update a release after it's been created?
No, all release properties (siteId, type, channelId, versionName, message) are immutable and cannot be changed after creation.
Why am I getting an error about the version not belonging to my site?
The version specified in versionName must belong to the same site as your siteId. Ensure both reference the same Firebase Hosting site.
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: