Deploying a web app efficiently is a game-changer for developers. For my financial project management system (PMS) built with Vue.js, I wanted to streamline deployments using Firebase Hosting and GitHub Actions, but without relying on the firebase-tools npm package for initialization. This approach is useful if you want to avoid installing additional npm dependencies or prefer manual configuration for more control. Below, I’ll walk you through the process I used to set up automated deployments, leveraging GitHub Actions for CI/CD and Firebase Hosting for a fast, scalable static site.
This guide assumes you’re comfortable with GitHub, Firebase, and basic terminal commands, and it mirrors the steps I took to achieve a seamless pipeline. We’ll manually configure Firebase, set up GitHub Actions workflows, and automate builds and deployments—all without npm install firebase-tools.
Prerequisites
Before starting, ensure you have:
- Node.js and npm installed (for building your app, e.g.,
npm run build). - A GitHub repository for your project (mine is a Vue.js app, but this works for any static site).
- A Firebase project created in the Firebase Console.
- Your app’s build output in a
distfolder (common for Vue CLI projects). - A local Git repository synced with GitHub.
- A Firebase service account JSON key (downloadable from Firebase Console).
Step 1: Set Up Firebase Hosting Manually
Without the Firebase CLI, we’ll configure Firebase Hosting by creating the necessary files manually.
Create firebase.json
In your project root (e.g., the deployment folder), create a firebase.json file to define Hosting settings. For a Vue.js single-page app (SPA), I used:
{
"hosting": {
"public": "dist",
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
]
}
}
-
public: Specifies the
distfolder, wherenpm run buildoutputs your app. - ignore: Excludes files from deployment (e.g., config and hidden files).
-
rewrites: Routes all URLs to
index.htmlfor SPA functionality.
Create .firebaserc
To link your project to Firebase, create a .firebaserc file in the root:
{
"projects": {
"default": "your-project-id"
}
}
Replace your-project-id with your Firebase project ID from the Firebase Console (e.g., finagle-pms-test in my case). You can find it under Project settings > General.
Update .gitignore
Add Firebase-related files to .gitignore to avoid committing sensitive data:
.firebase/
firebaserc
node_modules/
If .gitignore doesn’t exist, create it. This ensures your local Firebase cache and node modules aren’t pushed to GitHub.
Generate a Service Account Key
- Go to the Firebase Console.
- Navigate to Project settings > Service accounts.
- Click Generate new private key to download a JSON file (e.g.,
your-project-id-firebase-adminsdk-xyz.json). - Store this securely—you’ll need it for GitHub Actions.
Step 2: Test Your Build Locally
Ensure your app builds correctly:
npm run build
This should generate a dist folder with your static assets (e.g., index.html, JS, and CSS files). If you’re using Vue CLI, this is typically configured out of the box. Verify dist/index.html exists, as Firebase Hosting will serve it.
Step 3: Set Up GitHub Actions for CI/CD
Without firebase-tools, we’ll manually create GitHub Actions workflows to build and deploy to Firebase Hosting. These workflows will handle pull request previews and live deployments on merge.
Add Service Account Key to GitHub Secrets
- Go to your GitHub repository:
https://github.com/your-org/your-repo. - Navigate to Settings > Secrets and variables > Actions > New repository secret.
- Name the secret
FIREBASE_SERVICE_ACCOUNT_YOUR_PROJECT_ID(replaceYOUR_PROJECT_IDwith your Firebase project ID, e.g.,FINAGLE_PMS_TEST). - Paste the entire JSON content of the service account key you downloaded.
Create Workflow for Pull Requests
In your project root, create a folder: .github/workflows/firebase-hosting-pull-request.yml. Add:
name: Deploy to Firebase Hosting on PR
on:
push:
branches:
- master # Deploy to live when changes are pushed to master
pull_request:
branches:
- master # Deploy to preview on PRs targeting master
permissions:
checks: write
contents: read
pull-requests: write
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_YOUR_PROJECT_ID }}'
projectId: your-project-id
# Conditional for the event type
channelId: ${{ github.event_name == 'push' && 'live' || 'pr' }} # 'live' for push to master, 'pr' for pull request
Replace your-project-id with your Firebase project ID. This workflow:
- Triggers on pull requests to the
masterbranch. - Checks out code, sets up Node.js, installs dependencies, and builds the app.
- Deploys to a Firebase Hosting preview channel using the service account.
Create Workflow for Live Deployment
Create another file: .github/workflows/firebase-hosting-merge.yml:
name: Deploy to Firebase Hosting on merge
'on':
push:
branches:
- master
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: '${{ secrets.GITHUB_TOKEN }}'
firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_YOUR_PROJECT_ID }}'
channelId: live
projectId: your-project-id
This triggers on pushes to master, deploying to the live Firebase Hosting channel.
Step 4: Push and Test the Workflows
Commit and push your changes:
git add .
git commit -m "Add Firebase Hosting config and GitHub Actions workflows"
git push origin master
Create a feature branch and open a pull request. Check the Actions tab in your GitHub repo to see the PR workflow run. It will:
- Build your app (
npm run build). - Deploy to a Firebase Hosting preview URL (visible in the Action logs).
Merge the PR, and the merge workflow will deploy to the live site.
Step 5: Verify Deployment
After the workflows run:
-
Preview: Find the preview URL in the GitHub Actions logs (e.g.,
https://your-project-id--preview-channel.web.app). -
Live: Visit your live site (e.g.,
https://your-project-id.web.app) after merging.
Ensure your Vue.js app loads correctly, with all routes handled by index.html.
Troubleshooting
-
Build fails? Verify
npm run buildworks locally and outputs todist. - Secret errors? Double-check the service account key in GitHub Secrets.
-
404 on routes? Confirm the
rewritesrule infirebase.json. - Permission issues? Ensure the service account has Firebase Hosting Admin permissions in the Firebase Console.
-
Vue-specific: If using environment variables, add them to GitHub Secrets and reference them in the workflow (e.g.,
env: VUE_APP_API_KEY: ${{ secrets.VUE_APP_API_KEY }}).
Why This Approach Wins
This setup streamlined my PMS deployments without relying on firebase-tools. It’s lightweight, avoids extra npm dependencies, and gives you full control over configuration. Firebase Hosting offers a global CDN, free SSL, and generous free tier, while GitHub Actions provides cost-free CI/CD for public repos. Previews on PRs catch issues early, and live deploys are instant on merge.
Try this for your next project! What’s your favorite way to automate deployments? Share below.
Originally published on October 15, 2025. Crafted with Vue.js and Firebase Hosting.
Top comments (0)