Getting started
Set up PullPreview in your repository in about ten minutes.
This guide walks you through adding PullPreview to an existing GitHub repository. By the end, opening a pull request and adding the pullpreview label will spin up a live preview environment automatically.
Prerequisites
Before you start:
- A Dockerized app. Your application must be launchable via
docker compose up. You need at least onedocker-compose.ymlin your repository. - A cloud account. Choose one:
- AWS Lightsail — create an IAM user with Lightsail full access. You will need
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY, and your preferredAWS_REGION. - Hetzner Cloud — create a project and generate an API token. You will need
HCLOUD_TOKENandHETZNER_CA_KEY. The latter is a PEM-encoded SSH CA private key PullPreview uses to sign short-lived SSH access certificates — it is always required for Hetzner (not just for TLS). Generate it once withssh-keygen -t rsa -b 3072 -m PEM -N "" -f hetzner_ca_keyand store the contents as the secret.
- AWS Lightsail — create an IAM user with Lightsail full access. You will need
- A GitHub repository with Actions enabled.
Step 1 — Add GitHub secrets
Go to your repository → Settings → Secrets and variables → Actions → New repository secret.
For AWS Lightsail, add:
| Secret name | Value |
|---|---|
AWS_ACCESS_KEY_ID | Your IAM access key ID |
AWS_SECRET_ACCESS_KEY | Your IAM secret access key |
For Hetzner, add:
| Secret name | Value |
|---|---|
HCLOUD_TOKEN | Your Hetzner Cloud API token |
HETZNER_CA_KEY | Your Hetzner SSH CA private key (PEM) |
The AWS_REGION value (e.g. us-east-1) is set directly in the workflow file, not as a secret.
For AWS, the example below uses static access keys for simplicity. For production we recommend short-lived credentials via GitHub OIDC and a least-privilege (lightsail:*) IAM policy — see Recommended AWS setup.
Step 2 — Create the workflow file
Create .github/workflows/pullpreview.yml in your repository. The minimal Lightsail workflow:
name: PullPreviewon: schedule: - cron: "30 */4 * * *" pull_request: types: [labeled, unlabeled, synchronize, reopened, opened, closed]
permissions: contents: read pull-requests: write
jobs: deploy: if: github.event_name == 'schedule' || github.event.label.name == 'pullpreview' || contains(github.event.pull_request.labels.*.name, 'pullpreview') runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v5 - uses: pullpreview/action@v6 with: admins: "@collaborators/push" provider: lightsail app_path: . instance_type: nano default_port: 80 compose_files: docker-compose.yml proxy_tls: web:80 ttl: 24h env: AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" AWS_REGION: "us-east-1"The if: guard lets the job run in exactly the cases PullPreview needs: on schedule (cleanup), when the pullpreview label is added or removed (github.event.label.name), and on any PR event while the label is present (contains(...)). That covers creation, redeploys, and teardown — removing the label or closing/merging the PR runs the job and destroys the instance. The schedule trigger is only for periodic cleanup of closed and TTL-expired previews; it does not keep instances alive.
PullPreview v6 is PR-driven — a direct
pushto a branch no longer deploys it. To keep a long-lived branch preview, see Migrating from v5 to v6.
See Workflow examples for the complete Hetzner workflow and additional patterns.
Step 3 — Create the label
In your repository, go to Issues → Labels → New label. Create a label named pullpreview (matching is case-insensitive, and you can choose a different name via the label input). The color and description are up to you.
Step 4 — Open a pull request and add the label
- Push a branch and open a pull request.
- Add the
pullpreviewlabel to the PR. - The workflow triggers. After a minute or two, the action posts a comment on the PR (and keeps it updated) with the preview URL. A job summary with the URL and SSH details is also written to the workflow run.
The environment redeploys on every push to the PR. Remove the pullpreview label (or close/merge the PR) to destroy the instance.
Next steps
- Understand the full lifecycle in How it works.
- Tune instance size, region, and ports in Configuration.
- Add HTTPS and a custom domain in HTTPS and custom domains.
- Harden AWS access with OIDC, and see more patterns in Workflow examples.