Explore
CI/CD for MVPs: GitHub Actions With Nuxt and Firebase

CI/CD for MVPs: GitHub Actions With Nuxt and Firebase

How to automate your test and deploy pipeline with GitHub Actions so you ship faster and break production less often.

CI/CD for MVPs: GitHub Actions With Nuxt and Firebase

Most MVP teams deploy manually. Someone runs npm run build, uploads files, or pushes to a branch that triggers a Vercel deploy. It works — until it doesn't. A build with a broken import. A deploy that overwrites a working version. A regression nobody caught because there was no test run.

A basic CI/CD pipeline prevents these problems. With GitHub Actions, setting one up for a Nuxt + Firebase project takes under two hours and saves far more than that in the first few weeks.


Why CI/CD Is Worth Setting Up Even for a Two-Person Team

The argument against CI/CD for MVPs usually goes: "We're too small and too fast-moving for that overhead." But the overhead of CI/CD is a one-time cost. The overhead of shipping broken code is recurring.

What a simple pipeline gives you:

  • Confidence — every push is checked before it reaches production
  • Speed — no manual deploy steps; merging to main triggers deployment automatically
  • History — a log of every deploy, what changed, and whether tests passed
  • Preview environments — feature branches can get their own deployed URL for review

For a Nuxt + Firebase project, the pipeline you need is straightforward: run linting and tests on every pull request, deploy to Firebase Hosting on every merge to main.


The Pipeline You Need: Lint, Test, Build, Deploy

A minimal, useful pipeline for an MVP:

On pull request:
  1. Install dependencies
  2. Run linting (ESLint)
  3. Run type checking (tsc)
  4. Run unit tests (Vitest)
  5. Build the project (verify it compiles)

On merge to main:
  1. Build the project
  2. Deploy to Firebase Hosting

That's it. No staging environments, no multi-region deploys, no approval gates — those come later. This pipeline prevents the most common problems without adding ceremony.


GitHub Actions Basics: Triggers, Jobs, and Steps

GitHub Actions workflows are YAML files in .github/workflows/. They trigger on GitHub events (push, pull_request, workflow_dispatch), define jobs that run on virtual machines, and steps within each job that run shell commands or pre-built actions.

The structure:

name: Workflow Name

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  job-name:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Step name
        run: some-command

Jobs run in parallel by default. If one job depends on another completing successfully, add needs: [job-name].


Writing the Workflow: Nuxt Build and Firebase Hosting Deploy

Create .github/workflows/deploy.yml:

name: CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Type check
        run: npm run typecheck

      - name: Test
        run: npm run test

  deploy:
    runs-on: ubuntu-latest
    needs: [test]
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    steps:
      - uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build
        env:
          NUXT_PUBLIC_FIREBASE_API_KEY: ${{ secrets.FIREBASE_API_KEY }}

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

The needs: [test] ensures deployment only happens after the test job succeeds. The if: condition restricts deployment to pushes to main, not pull requests.


Securing Your Pipeline: GitHub Secrets and Firebase Service Accounts

The workflow above uses two secrets: FIREBASE_API_KEY and FIREBASE_SERVICE_ACCOUNT. These are stored in your GitHub repository's Settings → Secrets and variables → Actions.

FIREBASE_SERVICE_ACCOUNT is the one that needs setup:

  1. Go to Firebase Console → Project Settings → Service accounts
  2. Click "Generate new private key" — download the JSON file
  3. Copy the entire JSON content
  4. Add it as a GitHub secret named FIREBASE_SERVICE_ACCOUNT

The GITHUB_TOKEN is automatically provided by GitHub Actions — you don't need to create it.

For other environment variables your Nuxt app needs at build time, add them as GitHub secrets and reference them in the workflow's env: block, as shown with NUXT_PUBLIC_FIREBASE_API_KEY above.


What to Add Next: Preview Channels, Slack Notifications, Coverage

Once the basic pipeline is running, the most useful additions:

Firebase preview channels — deploy pull requests to temporary URLs for review before merging. The FirebaseExtended/action-hosting-deploy action supports this with channelId: ${{ github.head_ref }} instead of live.

Slack or email notifications on failure — add a step at the end of the deploy job that runs if: failure() and sends a notification. The slackapi/slack-github-action handles this in a few lines.

Test coverage reporting — Vitest can output a coverage report; upload it as a GitHub Actions artifact or use a coverage service like Codecov.

None of these are urgent. Get the basic pipeline running first, validate that it's catching real issues, then add features based on what your team actually needs.

A working CI/CD pipeline is one of those things that feels like overhead until the first time it prevents you from shipping a broken deploy. Let's talk if you want it set up as part of your MVP.