PR Previews
Every pull request gets its own ephemeral PandaStack environment — built from the PR's code, served behind a stable URL, and posted as a comment on the PR. Opt-in per repo; auto-torn-down when the PR closes.
PR previews give every pull request its own live, isolated environment. Open a PR → PandaStack builds it from the PR's exact commit, deploys it to a Firecracker microVM, and comments the preview URL on the pull request. Push more commits → it rebuilds. Close or merge the PR → the environment is torn down automatically.
It's the same build pipeline as a regular App — the difference is the lifecycle is driven by the pull request, and the environment is ephemeral.
PR previews are opt-in per repository. Installing the GitHub App (for app auto-deploy) does not turn previews on — you enable them per repo from the dashboard, API, or SDK. A repo with previews off ignores pull-request events entirely.
Lifecycle
| PR event | What happens |
|---|---|
| opened / reopened | A preview environment is provisioned from the PR's head commit and deployed. The preview URL is posted as a comment on the PR. |
| push (new commits) | The preview rebuilds from the new commit. The same PR comment is edited in place (no comment spam). |
| closed / merged | The preview environment is torn down and its sandbox freed. |
@pandastack delete comment | Tears the preview down early, without closing the PR. The command also accepts the App's own handle — @<app-slug> delete. |
The PR comment is a single sticky comment that tracks state: ⏳ Building → ✅ Ready (with the URL) → ❌ Failed if a build breaks.
Enabling previews for a repo
Previews are off by default. Turn them on for a specific repo via any of:
In the Apps page, connect your GitHub account, then open the repo picker. Each repo has a Previews toggle — click it to enable (or disable) PR previews for that repo.
# Enable
curl -X POST https://api.pandastack.ai/v1/preview-repos \
-H "Authorization: Bearer $PANDASTACK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"github_repo_id": 123456789, "repo_full_name": "you/your-repo"}'
# List enabled repos
curl https://api.pandastack.ai/v1/preview-repos \
-H "Authorization: Bearer $PANDASTACK_API_KEY"
# Disable
curl -X DELETE https://api.pandastack.ai/v1/preview-repos/123456789 \
-H "Authorization: Bearer $PANDASTACK_API_KEY"from pandastack import Client
client = Client()
# Enable PR previews for a repo
client.apps.enable_preview_repo(123456789, repo_full_name="you/your-repo")
# List opted-in repos
client.apps.preview_repos()
# List live preview environments
client.apps.previews()
# Disable
client.apps.disable_preview_repo(123456789)import { Client } from "@pandastack/sdk";
const client = new Client();
// Enable PR previews for a repo
await client.apps.enablePreviewRepo(123456789, { repoFullName: "you/your-repo" });
// List opted-in repos
await client.apps.previewRepos();
// List live preview environments
await client.apps.previews();
// Disable
await client.apps.disablePreviewRepo(123456789);The github_repo_id is GitHub's numeric repository ID (not the owner/repo string). The dashboard toggle resolves it for you; for the API/SDK you can get it from the GitHub API or the connected-repos list.
GitHub App requirements
For previews to work, the PandaStack GitHub App installed on your repo needs:
- Webhook events:
Pull requestsandIssue comments. - Permissions:
Issues: Read & write(to post the preview-URL comment on the PR).
These are in addition to the repo-content read access the App already uses for cloning.
How a preview is built
A preview reuses the standard App deploy pipeline: shallow-clone the PR's commit → detect the framework → mise install the runtime → install + build → start and health-check → serve behind a stable URL. The preview runs on the base template (2 GiB / 2 vCPU).
Each preview gets a stable URL of the form https://<id>.<your-app-suffix>/ that survives rebuilds, so the link in the PR comment stays valid as you push commits.
Cleanup and limits
- Automatic teardown. Previews are torn down when the PR closes or merges. As a backstop, a reaper also removes previews that have had no push for 24 hours (each push resets the clock) — so an abandoned PR, or a missed close webhook, can never leak a running environment.
- Concurrency cap. Each workspace has a limit on concurrent live previews (default 10, configurable via
PANDASTACK_MAX_PREVIEWS_PER_WORKSPACE). When the cap is reached, a new PR gets a comment explaining the limit instead of an environment; closing another preview frees a slot.
Previews vs. apps
| App | PR preview | |
|---|---|---|
| Lifecycle | Long-lived; you manage it | Tied to a pull request |
| Created by | You (dashboard / API / SDK) | A PR event (once the repo is opted in) |
| URL | Stable, permanent | Stable for the life of the PR |
| Teardown | When you delete it | On PR close, @pandastack delete (or @<app-slug> delete), or the 24h reaper |
| Auto-deploy | On push to the tracked branch | On every push to the PR |
Previews and regular apps coexist — enabling previews on a repo doesn't affect any durable app you've deployed from the same repo.
Apps (git-driven hosting)
Connect a Git repo and PandaStack builds and serves it behind a stable URL — auto-detected frameworks, zero-downtime deploys, scale-to-zero, and push-to-deploy from GitHub.
Environment Variables & Secrets
Give your app config and secrets — injected at build and runtime, encrypted at rest, masked in the API, scoped to production or previews, and never leaked to logs or PR comments.