CI/CD Preview Deploys
Give every pull request a live preview URL — without provisioning infrastructure.
How it works
In your CI pipeline, install NullBore, start your app, open a tunnel, and post the URL as a PR comment. Reviewers click the link and see a live version of the branch.
GitHub Actions example
name: Preview Deploy
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install NullBore
run: curl -fsSL nullbore.com/install.sh | sh
- name: Start app
run: |
npm ci
npm start &
sleep 5 # wait for server to boot
- name: Open tunnel
env:
NULLBORE_API_KEY: ${{ secrets.NULLBORE_API_KEY }}
run: |
export PATH="$PATH:$HOME/.local/bin"
nullbore open --port 3000 --name pr-${{ github.event.number }} --ttl 2h
echo "PREVIEW_URL=https://pr-${{ github.event.number }}.yourname.nullbore.com" >> $GITHUB_ENV
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `🔗 Preview: ${process.env.PREVIEW_URL}\n\n_Expires in 2 hours._`
})
Named tunnels (like
pr-123) require a Dev plan ($7/mo) or higher. On the free tier, use random slugs and capture the URL fromnullbore openoutput instead.
Why this over alternatives
- No infrastructure to manage — no Vercel/Netlify lock-in, no extra cloud resources
- Works with any stack — if it runs on localhost, NullBore can expose it
- Time-limited — preview URLs auto-expire, no orphaned deployments
- Self-hostable — run your own NullBore server for fully internal previews
Tips
- Use
pr-$NUMBERas the tunnel name for stable, predictable URLs. - Set TTL to match your review cycle (2-4 hours is typical).
- Add a cleanup step that runs
nullbore closeon PR merge/close.