Images Agents Pricing Docs Blog View as Markdown (Agent) Get started
Human Agent
← Back to blog

Host Playwright & CI screenshots on a real CDN

A test failed in CI and left a screenshot. Now it's buried in a zipped artifact you have to download, unzip, and open locally. Here's how to turn it into a private, clickable URL that lands straight in the pull request.

Playwright captures a screenshot the moment a test fails — genuinely useful evidence. The problem is where it ends up. In GitHub Actions the standard move is actions/upload-artifact, which bundles everything into a zip. To actually see the failure you download the zip, unzip it, and hunt for the right PNG. Nobody does that at 2 AM during an incident, and you can't drop a zip into a PR comment.

This isn't a job for a full visual-testing platform, either. If you already diff with Playwright's built-in toHaveScreenshot(), you don't need to buy a second product to review the result — you just need the image, somewhere a human (or a bot) can click it. That's hosting, not a platform.

One GitHub Action

Drop the PixelVault screenshots action into your workflow behind if: failure(). It globs the screenshots, uploads them in a single grouped batch, and posts (or updates) one sticky comment on the pull request with a diff table — no comment spam, no artifact spelunking:

# .github/workflows/e2e.yml
jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write  # for the sticky PR comment
    steps:
      - run: npx playwright test

      - name: Host failure screenshots
        if: failure()
        uses: pixelvault-dev/screenshots-action@v1
        with:
          api-key: ${{ secrets.PIXELVAULT_API_KEY }}
          path: test-results
          pattern: "*-diff.png"
          visibility: private

Set PIXELVAULT_API_KEY once as a repository secret. Re-running the job updates the same comment in place (the collection is idempotent by run), so a flaky retry doesn't stack five comments.

Private by default

CI screenshots usually show your app mid-flow — staging data, internal UI, sometimes a logged-in account. You don't want those on a public, crawlable CDN. With visibility: private every screenshot is served behind a signed URL: the link in the PR works for reviewers, but strip the signature and the CDN returns 403. It's the review convenience of a public link without putting your UI on the open web.

Not on GitHub? One API call

The action is a thin wrapper over a plain endpoint, so GitLab CI, CircleCI, Jenkins, or a local run all work the same way — POST a run's screenshots as one batch and get back a signed URL per image:

# Any CI system: upload a run's screenshots in one call.
curl -s -X POST https://api.pixelvault.dev/v1/images/batch \
  -H "Authorization: Bearer $PIXELVAULT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"collection": {"type": "ci_build", "visibility": "private"},
      "images": [{"data": "<base64-png>", "filename": "diff.png"}]}'

# → each item returns a signed https://img.pixelvault.dev/… URL

Why a hosting API beats artifacts here

  • Clickable, not zipped. A URL renders inline in a PR, an issue, or a chat message. A zip renders nowhere.
  • Private when it matters. Signed URLs keep internal UI off the public web; public collections are there when you want a shareable link.
  • One sticky PR comment. The action keeps a single comment per PR and updates it — reviewers see the latest run, not a wall of duplicates.
  • Zero egress fees. Screenshots get viewed by teammates and bots alike; bandwidth doesn't bill you by surprise.
  • Same API everywhere. GitHub Actions, GitLab CI, CircleCI, or a local run — it's just an HTTP POST.

For agents running the tests

When a coding agent runs your E2E suite, it hits the same wall — a screenshot it can't easily surface to a human. Because upload is a plain API call (and there's an MCP tool for it), an agent can host the failure screenshot itself and hand you back a link in its summary. No browser, no artifact download.

Free to start

Screenshots add up fast. Compress images in CI before they are committed or uploaded, to keep both your repo and your storage small.

PixelVault's free tier includes 200 MB storage, 500 uploads/month, 1 GB bandwidth, and up to 100 private images — no credit card, no trial expiry. Paid plans (from $9/month) lift the private-image cap. Free-plan images left unused for a long time may be cleaned up (the rule); paid plans keep everything.

Read the API docs →