In this blog post, I’ll show you how to deploy your Hugo blog automatically to Google Firebase Hosting .

I’ll assume that you already have

  • a Github account with a repository to sync your blog
  • setup firebase hosting via firebase init hosting to publish the blog via the CLI

Here, we will add a Github Actions workflow for CI/CD, so the blog is automatically built and published whenever we push to the repo.

If you setup your Hugo blog to host in Render , CloudFlare Pages , or Azure , they should have already setup a Github Action workflow using a framework/build preset. In that case, you do not need to do anything extra.

If, like me, you use Firebase which is framework-agnostic, then there is an additional step after you finished setting up hosting with its CLI. There are some hiccups from the Firebase CLI and I think that my bad experience motivates me to write this post to share all my mistakes, so you don’t waste as much time as I did!

What’s the problem?

When some steps during firebase init hosting asked if you want to:

  • setup automatic builds and deploys with Github (Q6-9 in official doc )
  • authenticate your Firebase account
  • setup a Github workflow

I made the assumption that they have setup CI/CD. In fact, firebase init hosting merely takes care of its own hosting by creating firebase.json and .firebaserc files to store configuration and project settings. Nothing about github is done!

Since Firebase is framework-agnostic, we need to perform an additional step after hosting is configured. Here’s how:

The right steps

We need to run firebase init hosting:github after firebase init hosting is completed.

note: When I setup a new Hugo blog, I thought I could shoot one bird with 2 stones just by doing firebase init hosting:github. This will fail as it needs to look up firebase.json and .firebaserc (created by firebase init hosting) for project information to work on.

  1. Specify your Github repository

  2. CLI will automatically creates a Firebase project Service account with deploy permission, and authenticate with GitHub to upload this service account JSON to the repo’s secrets store as FIREBASE_SERVICE_ACCOUNT.

    You can verify this by going to your Github repo -> Settings -> Secrets and variables -> Actions. You should see FIREBASE_SERVICE_ACCOUNT in Repository Secrets.

  3. The CLI now detects I’ve added a predeploy script "predeploy": "hugo", to firebase.json , and prompts me to use it in the new workflow.

    note: You will assume that you should specify the workflow to run hugo to build your files. Don’t believe it! Just press ENTER to accept the default. I notice that it really doesn’t matter what you input, as it will default back to building for node.js. Don’t worry, we will manually override this value in step 5. predeploy

  4. Say yes to the remaining options. It will finally create 2 yaml files in the .github/workflows/ folder.

  • firebase-hosting-merge.yml: configure automatic deployment when pull requests (PRs) are merged into main branch.
  • firebase-hosting-pull-request.yml: configure deployment to preview environment when a pull request is created. Both use the official FirebaseExtended action to deploy.

Let’s look at firebase-hosting-merge.yml. What the script does is

  1. checkout the repo files
  2. build (using npm)
  3. deploy to Firebase
name: Deploy to Firebase Hosting on merge
'on':
  push:
    branches:
      - main
jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build
      - uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          repoToken: '${{ secrets.GITHUB_TOKEN }}'
          firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_YOUR-FIREBASE-PROJECTID }}'
          channelId: live
          projectId: YOUR-FIREBASE-PROJECTID
  1. Since it ignores what we enter in step 3 to run hugo, we will edit the workflow file to take care of Hugo builds

    1. We might be tempted to just swap out the run: npm ci and replace it with run: hugo to build our files and call it a day. I did that and it was still missing parts!

      A proper workflow is

      1. check out repo files
      2. setup the Hugo environment
      3. build Hugo site
      4. deploy

      Clearly what’s missing from Firebase CLI’s default workflow is 2 and 3. So let’s patch our yml with the following

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2.5.0 # Action to set up Hugo environment
        with:
          hugo-version: 'latest' # Specifies the Hugo version to use
      
      - name: Build Hugo site
        run: hugo --minify # Runs the Hugo build command with minification
      
  2. Now recall how you acquired your Hugo theme. You can check its readme or github.

    • Is it by git clone? Then it’s ok, please jump to the next step.
    • Is it by using a submodule? (check the existence of .gitmodules file in your project root folder) If so, make sure you specify the checkout action with
      - uses: actions/checkout@v4 # Updated to v4
        with:
          submodules: true # Added for Hugo themes that are Git submodules
      

    This is another mistake I made. Without this, the checkout process will not fetch layouts and css from the theme’s original repo, and the build will not complete. My old process was building and deploying locally (which contains all the submodule theme files) so this is new to me. Worst yet, Github Action status will show everything green and checked, with the errors hidden as mere warnings. For your first few pushes, make sure to go to Github -> Actions, click the workflow run, click build_and_deploy job and expand Build Hugo Site to check if the files are successfully built. You can see that the layouts are missing. If you just glance at the top level Action status, everything looks good. action status

  3. For your theme, does it use SCSS and SASS? Again confirm with its readme. If so, make sure extended is added to the Setup Hugo step, like

    - name: Setup Hugo
      uses: peaceiris/actions-hugo@v2.5.0 # Action to set up Hugo environment
      with:
         hugo-version: 'latest' # Specifies the Hugo version to use
         extended: true # <--- ADD THIS LINE TO USE THE EXTENDED HUGO VERSION 
    
  4. Commit and push the new files to your repo.

  5. Let’s test editing some .md files in your desktop and push to Github. Verify that the updates are properly deployed to Firebase.

Now we’re all done and we can use both desktop or mobile Github clients to sync our blog to Github, wherever we are. The Action workflow will automatically pick up the changes, build and deploy to Firebase.