Integrating preview environments into your CI/CD pipeline automates the deployment process and ensures every pull request gets a live preview URL. This article covers best practices and provides ready-to-use configurations for popular CI/CD platforms.
Why Automate Preview Deployments?
Manual preview deployments defeat the purpose of rapid iteration. By automating the process, you:
- Save time: No manual intervention required for each PR
- Ensure consistency: Every PR gets the same treatment
- Enable parallel workflows: Multiple PRs can have simultaneous previews
- Improve traceability: Preview URLs are automatically linked to PRs
GitHub Actions Integration
GitHub Actions is one of the most popular CI/CD platforms. Here's a complete workflow for automated preview deployments:
Basic Workflow
name: Preview Environment
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build project
run: npm run build
- name: Deploy to prev
id: deploy
run: |
curl -fsSL https://prev.sh/install.sh | sh
PREVIEW_URL=$(prev --subdomain pr-${{ github.event.number }} --output url)
echo "preview_url=$PREVIEW_URL" >> $GITHUB_OUTPUT
env:
PREV_TOKEN: ${{ secrets.PREV_TOKEN }}
- name: Comment 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 deployed: ${{ steps.deploy.outputs.preview_url }}'
})
Cleanup on PR Close
name: Cleanup Preview
on:
pull_request:
types: [closed]
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- name: Destroy preview
run: |
curl -fsSL https://prev.sh/install.sh | sh
prev destroy pr-${{ github.event.number }}
env:
PREV_TOKEN: ${{ secrets.PREV_TOKEN }}
GitLab CI Integration
For GitLab users, here's an equivalent configuration:
stages:
- deploy
- cleanup
deploy_preview:
stage: deploy
image: node:20
script:
- npm ci
- npm run build
- curl -fsSL https://prev.sh/install.sh | sh
- prev --subdomain mr-$CI_MERGE_REQUEST_IID
environment:
name: preview/$CI_MERGE_REQUEST_IID
url: https://mr-$CI_MERGE_REQUEST_IID.prev.sh
on_stop: stop_preview
rules:
- if: $CI_MERGE_REQUEST_IID
stop_preview:
stage: cleanup
image: node:20
script:
- curl -fsSL https://prev.sh/install.sh | sh
- prev destroy mr-$CI_MERGE_REQUEST_IID
environment:
name: preview/$CI_MERGE_REQUEST_IID
action: stop
rules:
- if: $CI_MERGE_REQUEST_IID
when: manual
CircleCI Integration
version: 2.1
jobs:
deploy-preview:
docker:
- image: cimg/node:20.0
steps:
- checkout
- run:
name: Install dependencies
command: npm ci
- run:
name: Build
command: npm run build
- run:
name: Deploy preview
command: |
curl -fsSL https://prev.sh/install.sh | sh
prev --subdomain pr-$CIRCLE_PR_NUMBER
workflows:
preview:
jobs:
- deploy-preview:
filters:
branches:
ignore: main
Best Practices
1. Use Semantic Subdomain Names
Include identifiable information in your subdomain:
prev --subdomain pr-123-feature-login
2. Set Appropriate TTLs
Match TTL to your review cycle:
prev --ttl 3d # Most PRs are reviewed within 3 days
3. Secure Your Tokens
Never commit API tokens. Use CI/CD secrets:
- GitHub: Repository Settings → Secrets
- GitLab: Settings → CI/CD → Variables
- CircleCI: Project Settings → Environment Variables
4. Handle Build Failures Gracefully
Add error handling to prevent silent failures:
- name: Deploy preview
run: |
prev --subdomain pr-${{ github.event.number }} || {
echo "Preview deployment failed"
exit 1
}
5. Add Status Checks
Require preview deployment as a status check before merging.
Monitoring and Debugging
Check Deployment Status
prev status pr-123
View Logs
prev logs pr-123
List All Active Previews
prev list
Conclusion
Automating preview environments with CI/CD is essential for modern development workflows. The configurations provided here should get you started quickly, regardless of which platform you use.
For more advanced configurations and troubleshooting, check out our documentation.