Skip to content

CI/CD Preview Builds

Give every pull request a live preview URL using Localport tunnels in your CI/CD pipeline. GitHub Actions and GitLab CI examples included.

Give every pull request its own live preview URL. Instead of deploying to a staging server, spin up the app in your CI pipeline and tunnel it with Localport. Reviewers click a link and see the running app.

How it works

1. Your CI job builds the app and starts it locally (inside the CI runner)

2. Localport creates a tunnel to that local server

3. The public URL gets posted as a comment on the pull request

4. Reviewers open the link, test the feature, leave feedback

5. When the CI job ends, the tunnel closes

GitHub Actions example

name: Preview Build
on:
  pull_request:
    types: [opened, synchronize]

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

      - name: Install dependencies and build
        run: |
          npm ci
          npm run build

      - name: Start app in background
        run: npm run preview -- --port 3000 &

      - name: Install Localport
        run: curl -fsSL https://get.localport.dev | sh

      - name: Start tunnel
        run: |
          localport http 3000 --token ${{ secrets.LOCALPORT_TOKEN }} > tunnel.log 2>&1 &
          sleep 5
          TUNNEL_URL=$(grep -o 'https://[^ ]*' tunnel.log | head -1)
          echo "TUNNEL_URL=$TUNNEL_URL" >> $GITHUB_ENV

      - name: Comment preview URL on PR
        uses: actions/github-script@v7
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: '🔗 Preview: ' + process.env.TUNNEL_URL
            })

Token management

Store your Localport tunnel token as a GitHub Actions secret (LOCALPORT_TOKEN). Create a dedicated tunnel in the dashboard for CI/CD use.

GitLab CI example

preview:
  stage: test
  script:
    - npm ci && npm run build
    - npm run preview -- --port 3000 &
    - curl -fsSL https://get.localport.dev | sh
    - localport http 3000 --token $LOCALPORT_TOKEN > tunnel.log 2>&1 &
    - sleep 5
    - TUNNEL_URL=$(grep -o 'https://[^ ]*' tunnel.log | head -1)
    - 'echo "Preview URL: $TUNNEL_URL"'
  only:
    - merge_requests

Why not deploy to a staging server?

Localport previewStaging deploy
Setup timeMinutes (one CI step)Hours (infra, DNS, certs)
CostIncluded in your planHosting costs
SpeedReady in secondsDeploy pipeline
CleanupAutomatic (tunnel closes)Manual or scripted
IsolationEach PR gets its own URLShared environment

Best for review, not load testing

Preview tunnels are great for visual review and manual QA. For load testing or long-running environments, use a proper staging deploy.

Next steps