Skip to main content

3 - Firebase Hosting + GitHub Actions: Automatic Deploy

How to configure automatic deployment to Firebase Hosting with GitHub Actions: preview channels, staging/production environments and rollback.

What do you think about this article?

At a Glance

How to configure automatic deployment to Firebase Hosting with GitHub Actions: preview channels, staging/production environments and rollback.

Introduction: Automatic Deployment with Firebase and GitHub Actions

Manual deployment is the enemy of productivity and reliability. Every time you run firebase deploy from your terminal, you introduce the risk of human error: forgetting to build, deploying from the wrong branch, or skipping tests. In this article we will build a completely automated deployment system using Firebase Hosting and GitHub Actions.

Firebase Hosting is a hosting platform for static and dynamic web applications that offers a global CDN, automatic SSL certificates, preview channels for Pull Requests, and instant rollback. Combined with GitHub Actions, it creates a workflow where every push to main is automatically deployed to production, and every Pull Request generates a preview accessible via URL.

What You Will Learn in This Article

  • How to set up Firebase CLI and initialize a project
  • The structure of firebase.json and its options
  • How to create automatic preview channels for Pull Requests
  • The complete production deployment workflow
  • Rollback strategies for handling production errors
  • How to configure a custom domain
  • Caching headers and redirect rules
  • Firebase Hosting compared to alternatives

1. Firebase CLI Setup

The first step is to install and configure the Firebase CLI. This CLI is the main tool for interacting with Firebase from the terminal and CI/CD pipelines.


# Install Firebase CLI globally
npm install -g firebase-tools

# Verify the installation
firebase --version

# Login (opens browser for authentication)
firebase login

# For CI/CD environments, use a token
firebase login:ci
# This generates a token to save as a GitHub secret

# Initialize Firebase in the project
firebase init hosting
      

During initialization, Firebase will ask some configuration questions. Here are the recommended answers for an Angular project:

  • Public directory: dist/my-app/browser (the Angular build output folder)
  • Configure as single-page app: Yes (rewrites all URLs to index.html)
  • Set up automatic builds with GitHub: Yes (automatically configures workflows)
  • Overwrite index.html: No (we use our own)

2. firebase.json Configuration

The firebase.json file is the heart of Firebase Hosting configuration. Here is a complete and optimized configuration for an Angular project:


{
  "hosting": {
    "public": "dist/my-app/browser",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites": [
      {
        "source": "**",
        "destination": "/index.html"
      }
    ],
    "headers": [
      {
        "source": "**/*.@(js|css)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      },
      {
        "source": "**/*.@(jpg|jpeg|gif|png|svg|webp|ico)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=86400"
          }
        ]
      },
      {
        "source": "**/*.@(woff|woff2|ttf|eot)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      },
      {
        "source": "**",
        "headers": [
          {
            "key": "X-Frame-Options",
            "value": "SAMEORIGIN"
          },
          {
            "key": "X-Content-Type-Options",
            "value": "nosniff"
          },
          {
            "key": "X-XSS-Protection",
            "value": "1; mode=block"
          },
          {
            "key": "Referrer-Policy",
            "value": "strict-origin-when-cross-origin"
          }
        ]
      }
    ]
  }
}
      

Key Sections of firebase.json

Section Purpose Detail
public Directory of files to serve Must match the Angular build output
rewrites SPA routing All requests are redirected to index.html
headers Caching and security Aggressive caching for hashed assets, global security headers
ignore Files to exclude Configuration files and hidden directories

Redirect Rules

Firebase Hosting supports HTTP redirects for handling legacy URLs or forcing HTTPS:


{
  "hosting": {
    "redirects": [
      {
        "source": "/old-blog/:slug",
        "destination": "/blog/:slug",
        "type": 301
      },
      {
        "source": "/api/**",
        "destination": "https://api.example.com/v1/:splat",
        "type": 302
      },
      {
        "source": "/docs",
        "destination": "https://docs.example.com",
        "type": 301
      }
    ]
  }
}
      

3. Preview Channels for Pull Requests

Preview channels are one of the most powerful features of Firebase Hosting. Every Pull Request automatically gets a unique URL where changes can be viewed before merging. Here is the complete workflow:


# .github/workflows/firebase-preview.yml
name: Firebase Preview Deploy

on:
  pull_request:
    branches: [main]

jobs:
  build-and-preview:
    name: Build and Deploy Preview
    runs-on: ubuntu-latest
    permissions:
      checks: write
      pull-requests: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npx ng test --watch=false --browsers=ChromeHeadless

      - name: Build application
        run: npx ng build --configuration=production

      - name: Deploy to Firebase Preview Channel
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
          projectId: my-angular-project
          expires: 7d
      

After deployment, the action automatically adds a comment to the Pull Request with the preview URL. The expires: 7d parameter configures automatic expiration of the preview channel after 7 days.

How to Obtain the Firebase Service Account

  1. Go to the Firebase Console
  2. Select project > Project settings > Service accounts
  3. Click "Generate new private key"
  4. Download the JSON file
  5. Copy the content and add it as a GitHub secret named FIREBASE_SERVICE_ACCOUNT

4. Production Deployment Workflow

The production workflow runs only on pushes to the main branch and includes all quality checks before deployment:


# .github/workflows/firebase-production.yml
name: Firebase Production Deploy

on:
  push:
    branches: [main]

concurrency:
  group: production-deploy
  cancel-in-progress: false

jobs:
  test:
    name: Run Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx ng lint
      - run: npx ng test --watch=false --browsers=ChromeHeadless

  build:
    name: Build Application
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npx ng build --configuration=production
      - uses: actions/upload-artifact@v4
        with:
          name: production-build
          path: dist/
          retention-days: 1

  deploy:
    name: Deploy to Production
    runs-on: ubuntu-latest
    needs: build
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: production-build
          path: dist/

      - name: Deploy to Firebase Hosting
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
          channelId: live
          projectId: my-angular-project
      

Concurrency for Production Deploys

Note the cancel-in-progress: false setting for the production deploy. Unlike CI where we can cancel previous builds, for production deployment it is better to complete the ongoing deploy before starting a new one, to avoid inconsistent states.

5. Rollback Strategy

Despite tests and reviews, sometimes a production deploy introduces issues. Firebase Hosting offers several rollback strategies:

Rollback from the Firebase Console

The Firebase Console maintains deployment history. To rollback to a previous version, go to Hosting > Release history and click "Rollback" next to the desired version. This process is instant because Firebase keeps all files from previous releases on the CDN.

Rollback via CLI


# List recent releases
firebase hosting:releases:list --limit 10

# Rollback to a specific release
firebase hosting:rollback --release RELEASE_ID

# Alternative: clone a previous release
firebase hosting:clone SOURCE_SITE:SOURCE_CHANNEL TARGET_SITE:live
      

Automated Rollback via GitHub Actions


# .github/workflows/rollback.yml
name: Emergency Rollback

on:
  workflow_dispatch:
    inputs:
      commit-sha:
        description: 'Commit SHA to rollback to'
        required: true
        type: string
      reason:
        description: 'Reason for rollback'
        required: true
        type: string

jobs:
  rollback:
    name: Rollback to Previous Version
    runs-on: ubuntu-latest
    environment: production
    steps:
      - name: Checkout specific commit
        uses: actions/checkout@v4
        with:
          ref: ${{ inputs.commit-sha }}

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - run: npm ci
      - run: npx ng build --configuration=production

      - name: Deploy rollback version
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: ${{ secrets.GITHUB_TOKEN }}
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
          channelId: live
          projectId: my-angular-project

      - name: Notify team
        run: |
          echo "Rollback completed"
          echo "Commit: ${{ inputs.commit-sha }}"
          echo "Reason: ${{ inputs.reason }}"
      

6. Custom Domain Configuration

Firebase Hosting supports custom domains with a free SSL certificate. The configuration process requires domain ownership verification through DNS records.

Configuration Steps

  1. Go to Firebase Console > Hosting > "Add custom domain"
  2. Enter your domain (e.g. example.com)
  3. Firebase will provide the DNS records to configure at your registrar
  4. Add the TXT records for verification and A records for routing
  5. Wait for DNS propagation (up to 48 hours, typically 1-4 hours)
  6. Firebase will automatically issue an SSL certificate

DNS records to configure:

# Domain ownership verification
TXT  @  firebase-hosting-verification=xxxxxxxxxxxxx

# Routing to Firebase CDN
A    @  151.101.1.195
A    @  151.101.65.195

# www subdomain (optional)
CNAME  www  my-project.web.app
      

7. Optimized Caching Headers

Proper caching configuration is fundamental for performance. Angular generates files with hashes in their names (e.g. main.abc123.js), which can be cached for an entire year. The index.html file however must never be cached, because it contains references to the hashed files:


{
  "hosting": {
    "headers": [
      {
        "source": "/index.html",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache, no-store, must-revalidate"
          }
        ]
      },
      {
        "source": "ngsw.json",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "no-cache"
          }
        ]
      },
      {
        "source": "**/*.@(js|css)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      },
      {
        "source": "**/*.@(jpg|jpeg|png|gif|svg|webp|avif|ico)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=604800"
          }
        ]
      },
      {
        "source": "**/*.@(woff|woff2)",
        "headers": [
          {
            "key": "Cache-Control",
            "value": "public, max-age=31536000, immutable"
          }
        ]
      }
    ]
  }
}
      

8. Comparison with Alternatives

Firebase Hosting is not the only option for deploying Angular applications. Here is a comparison with the most popular alternatives:

Firebase Hosting vs Alternatives

Feature Firebase Vercel Netlify AWS S3 + CloudFront
Setup Simple Very simple Very simple Complex
Free SSL Yes Yes Yes Yes (ACM)
Global CDN Yes Yes Yes Yes
Preview deploy Yes Yes (automatic) Yes (automatic) Manual
Rollback Instant Instant Instant Manual
Free plan 10 GB storage, 360 MB/day transfer 100 GB bandwidth 100 GB bandwidth Pay-as-you-go
SSR Support Yes (Cloud Functions) Yes (native) Yes (Functions) Yes (Lambda@Edge)

9. Testing the Deploy Locally

Before configuring automatic deployment, it is useful to test the Firebase configuration locally:


# Build the application
npm run build

# Test Firebase Hosting locally
firebase emulators:start --only hosting

# The site will be available at http://localhost:5000
# This simulates Firebase Hosting behavior exactly:
# - SPA rewrites
# - Custom headers
# - Redirect rules
# - Clean URLs

# Alternative: serve the build directory directly
firebase serve --only hosting
      

Pre-Deploy Checklist

  • The production build generates without errors
  • All tests pass
  • The firebase.json file points to the correct directory
  • Caching headers are configured correctly
  • Redirects for legacy URLs are in place
  • The local emulator shows the site correctly
  • The Firebase service account is configured as a GitHub secret
  • Branch protection rules are active

Conclusion

In this article we built a completely automated deployment system with Firebase Hosting and GitHub Actions. We configured preview channels for Pull Requests, production workflows with quality checks, rollback strategies for handling emergencies, and optimized caching and security configuration.

The main advantage of this approach is confidence: once configured, every change that passes review and tests is automatically deployed, without manual intervention and without the possibility of errors. And if something goes wrong, rollback is just a click away.

In the next article we will tackle a crucial security topic: secret management in CI/CD pipelines, exploring how to protect API keys, service tokens, and credentials during the build and deployment process.

Frequently asked questions

What is the main advantage of using an automated deployment system with Firebase Hosting and GitHub Actions?

The main advantage is confidence, as every change that passes review and tests is automatically deployed without manual intervention or possibility of errors.

How can you rollback to a previous version if a production deploy introduces issues?

You can rollback from the Firebase Console by going to Hosting Release history and clicking "Rollback" next to the desired version, which is an instant process because Firebase keeps all files from previous releases on the CDN.

What is the purpose of preview channels for Pull Requests in Firebase Hosting?

Preview channels automatically generate a unique URL where changes can be viewed before merging, allowing you to test and review changes without affecting production.

Why should you complete an ongoing deploy before starting a new one during production deployment?

It is better to complete the ongoing deploy first to avoid inconsistent states, as opposed to CI pipelines where previous builds can be canceled.

Try These Tools

Free developer tools related to this topic.

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects