BlogTutorial

Screenshot Any Webpage in CI/CD with Zero Config

GitHub Actions examples using @openkova/cli — no API key, no SaaS account.

Two steps: install chromium-browser + @openkova/cli in your CI runner, then call kova screenshot <url>. That's it. The PNG is a plain file — upload it as an artifact, diff it, email it, or commit it. No SaaS account. MIT-licensed.

Why screenshot in CI?

Screenshots in CI are useful for several things beyond visual regression:

Prerequisites in any CI environment

@openkova/cli needs a Chrome or Chromium binary. In most CI environments you install it with the system package manager:

# Ubuntu / Debian (GitHub Actions ubuntu-latest, GitLab runners)
sudo apt-get update && sudo apt-get install -y chromium-browser

# Alpine (lightweight Docker-based CI)
apk add --no-cache chromium

# macOS (GitHub Actions macos-latest)
brew install --cask google-chrome

Set CHROMIUM_PATH if the binary is not on $PATH:

export CHROMIUM_PATH=/usr/bin/chromium-browser

Example 1: screenshot a deploy preview URL

Capture the preview URL generated by your deployment step and upload it as an artifact so PR reviewers see the rendered output:

# .github/workflows/preview-screenshot.yml
name: Deploy Preview Screenshot
on: [pull_request]

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

      - name: Install Chromium
        run: sudo apt-get update && sudo apt-get install -y chromium-browser

      - name: Install kova
        run: npm install -g @openkova/cli

      - name: Deploy to preview environment
        id: deploy
        run: |
          # Replace with your actual deploy command
          PREVIEW_URL=$(npx vercel --token ${{ secrets.VERCEL_TOKEN }} --yes 2>&1 | tail -1)
          echo "url=$PREVIEW_URL" >> $GITHUB_OUTPUT

      - name: Screenshot preview
        run: |
          kova screenshot "${{ steps.deploy.outputs.url }}" \
            --full-page --output preview.png
        env:
          CHROMIUM_PATH: /usr/bin/chromium-browser

      - name: Upload screenshot
        uses: actions/upload-artifact@v4
        with:
          name: deploy-preview
          path: preview.png

Example 2: screenshot a locally running app

Start your app as a background process in the same CI job, wait for it to be ready, then capture it. This works for any framework — Next.js, Vite, Django, Rails, etc.:

# .github/workflows/app-screenshot.yml
name: App Screenshot
on: [pull_request]

jobs:
  screenshot:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install dependencies
        run: npm ci

      - name: Install Chromium and kova
        run: |
          sudo apt-get update && sudo apt-get install -y chromium-browser
          npm install -g @openkova/cli

      - name: Build app
        run: npm run build

      - name: Start app (background)
        run: npm run start &

      - name: Wait for app to be ready
        run: npx wait-on http://localhost:3000 --timeout 30000

      - name: Screenshot pages
        run: |
          mkdir -p screenshots
          kova screenshot http://localhost:3000          --full-page --output screenshots/home.png
          kova screenshot http://localhost:3000/about    --full-page --output screenshots/about.png
          kova screenshot http://localhost:3000/pricing  --full-page --output screenshots/pricing.png
          # Capture mobile viewport
          kova screenshot http://localhost:3000 --viewport mobile --full-page --output screenshots/home-mobile.png
        env:
          CHROMIUM_PATH: /usr/bin/chromium-browser

      - name: Upload screenshots
        uses: actions/upload-artifact@v4
        with:
          name: page-screenshots
          path: screenshots/

Example 3: generate OG images during build

Generate all Open Graph images from HTML templates at build time instead of at request time. This avoids serverless cold starts, external API calls, and usage caps:

# .github/workflows/generate-og.yml
name: Generate OG Images
on:
  push:
    branches: [main]
    paths: ['content/**', 'templates/**']

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

      - name: Install Chromium and kova
        run: |
          sudo apt-get update && sudo apt-get install -y chromium-browser
          npm install -g @openkova/cli

      - name: Generate OG images
        run: |
          mkdir -p public/og
          for post in content/posts/*.md; do
            title=$(grep '^title:' "$post" | sed 's/title: //')
            slug=$(basename "$post" .md)

            # Inject title into template and pipe to kova
            sed "s/{{TITLE}}/$title/" templates/og.html | \
              kova snippet --format png > "public/og/$slug.png"
          done
        env:
          CHROMIUM_PATH: /usr/bin/chromium-browser

      - name: Commit OG images
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git add public/og/
          git diff --staged --quiet || git commit -m "chore: regenerate OG images"
          git push

Example 4: PDF invoice generation

Convert an HTML invoice template to PDF as a CI artifact, then upload it to S3 or attach it to a release:

- name: Generate invoice PDF
  run: |
    kova screenshot ./templates/invoice.html \
      --format pdf \
      --output invoice-$(date +%Y%m%d).pdf
  env:
    CHROMIUM_PATH: /usr/bin/chromium-browser

- name: Upload to S3
  run: |
    aws s3 cp invoice-*.pdf s3://my-bucket/invoices/

GitLab CI

The same pattern works in GitLab CI. Use a node:20 image and install Chromium in the before_script:

# .gitlab-ci.yml
screenshot:
  image: node:20
  before_script:
    - apt-get update && apt-get install -y chromium-browser
    - npm install -g @openkova/cli
  script:
    - kova screenshot https://example.com --full-page --output screenshot.png
  artifacts:
    paths:
      - screenshot.png
    expire_in: 7 days
  variables:
    CHROMIUM_PATH: /usr/bin/chromium-browser

Key flags reference

FlagValuesWhat it does
--formatpng jpeg webp pdfOutput format (default: png)
--viewportmobile desktop wideViewport preset
--full-pageCapture full scrollable height
--outputpath/to/file.pngOutput file path
--depth1 2Crawl depth (for kova crawl)

Frequently asked questions

How do I take a screenshot in GitHub Actions?

Install chromium-browser with apt-get, install @openkova/cli with npm, then call kova screenshot <url>. Set CHROMIUM_PATH=/usr/bin/chromium-browser. Use actions/upload-artifact to save the PNG.

Does @openkova/cli require an API key in CI?

No. It is MIT-licensed and runs using your CI environment's Chrome binary. No accounts, no API keys, no external service calls.

Can I screenshot a locally running app in CI?

Yes. Start your app in the background (npm run start &), wait for it to be ready (npx wait-on http://localhost:3000), then call kova screenshot http://localhost:3000. Everything runs in the same runner.

Does this work in GitLab CI, CircleCI, or other systems?

Yes. @openkova/cli is a plain npm package. Install Chromium with your system package manager and call kova as any other CLI. The CHROMIUM_PATH env var lets you point it at any browser binary.

Compare screenshots between runs: Visual Regression Testing Without Percy — or add a screenshot tool to your AI client: Add a Screenshot Tool to Claude Desktop, Cursor, and Windsurf.