Getting Started
1. Choose a deployment target
Upgrading from v5 and relying on always_on? See Migrating from v5 to v6 before using the workflow examples below.
PullPreview supports two deployment targets:
-
compose: deploy your app with Docker Compose on the preview instance -
helm: bootstrap k3s on the preview instance and deploy a Helm chart
Choose compose if your repo already runs with docker compose up.
Choose helm if your app is already packaged as a Helm chart.
See Deployment Targets for the full comparison.
2. Prepare your app
For compose
- Keep your app under
app_path(default.). - PullPreview looks for
docker-compose.ymlby default. - If your Compose files live elsewhere, set
app_pathand/orcompose_files.
For helm
- Set
deployment_target: helm. - Set
chart. - Set
proxy_tlsbecause Helm previews are always exposed through the PullPreview-managed Caddy gateway. - If you use Helm values files, keep them under
app_pathand pass them viachart_values.
3. Create the trigger label
Create a repository label named pullpreview, or use another label and set the label input.
4. Configure cloud credentials
Lightsail (default)
Add repository secrets:
AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY- optional
AWS_REGION(defaults tous-east-1)
For least-privilege setup, see Recommended AWS Configuration.
Hetzner
Add repository secrets:
HCLOUD_TOKENHETZNER_CA_KEY
HETZNER_CA_KEY is a private SSH CA key used by PullPreview for per-run SSH certificate authentication.
Generate one once and store only the private key in GitHub Secrets:
ssh-keygen -t rsa -b 3072 -m PEM -N "" -f hetzner_ca_keyOptional Hetzner action inputs:
provider: hetzner-
region: nbg1(default) -
image: ubuntu-24.04(default) -
instance_type: cpx21(default)
5. Add a workflow
Minimal compose workflow
name: PullPreview on: schedule: - cron: "30 */4 * * *" pull_request: types: [labeled, unlabeled, synchronize, closed, reopened, opened] 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-slim timeout-minutes: 30 steps: - uses: actions/checkout@v6 - uses: pullpreview/action@v6 with: app_path: . deployment_target: compose admins: "@collaborators/push" # optional HTTPS # proxy_tls: web:80 env: AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-1
schedule is optional and is only used for cleanup of dangling deployments. v6 preview deploys themselves are driven by pull request events.
Minimal helm workflow
name: PullPreview Helm on: pull_request: types: [labeled, unlabeled, synchronize, closed, reopened, opened] permissions: contents: read pull-requests: write jobs: deploy: if: github.event.label.name == 'pullpreview' || contains(github.event.pull_request.labels.*.name, 'pullpreview') runs-on: ubuntu-slim timeout-minutes: 45 steps: - uses: actions/checkout@v6 - uses: pullpreview/action@v6 with: provider: hetzner deployment_target: helm chart: ./charts/my-app chart_values: charts/my-app/values-preview.yaml proxy_tls: "{{ release_name }}-web:80" env: HCLOUD_TOKEN: ${{ secrets.HCLOUD_TOKEN }} HETZNER_CA_KEY: ${{ secrets.HETZNER_CA_KEY }}
6. Deploy
Open a pull request, add the trigger label, and let the workflow run.
On success you get:
- a live preview URL
- PR status comments updated by PullPreview
- a GitHub job summary with preview and SSH details
- action outputs:
live,url,host, andusername
Next: