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

# CI/CD Preview Builds

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

```bash
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
            })
```

> [!TIP] 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

```bash
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 preview | Staging deploy |
|---|---|---|
| Setup time | Minutes (one CI step) | Hours (infra, DNS, certs) |
| Cost | Included in your plan | Hosting costs |
| Speed | Ready in seconds | Deploy pipeline |
| Cleanup | Automatic (tunnel closes) | Manual or scripted |
| Isolation | Each PR gets its own URL | Shared environment |

> [!INFO] 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

- [HTTP Tunnels](/docs/http-tunnels) — How HTTP tunneling works
- [Installation](/docs/installation) — Install Localport on your CI runners