sprout · AGPL-3.0 · one shared instance
Every pull request gets its own preview
Sprout gives every pull request an isolated database and a live preview app — for teams that want to review running code, not just diffs. You host one shared instance; sprout handles the per-PR isolation and cleans up when the PR closes.
How it works
An operator deploys the shared instance plus the sprout gateway once. An adopting repo adds one config file and calls sprout from CI. Each pull request then lives through the same five steps:
Create
Opening a PR creates its own logical database on the shared instance.
Migrate
The preview app starts against that database and runs its own migrations.
Seed
Optional demo data loads once the app reports healthy.
Hand over
Reviewers open the preview URL. Pushes redeploy into the same database.
Drop
Closing the PR stops everything and drops the database. Nothing lingers.
Requirements → what you get
The resource argument is the whole pitch: one shared Postgres instance serves every pull request. Each PR gets one logical database on that instance — real isolation without provisioning a database server per PR, and without paying per-environment platform fees. When the PR closes, its database and containers go away.
| Requirement | What you get |
|---|---|
A repo with a Dockerfile that serves HTTP. | A live app URL for the PR, served through your own Traefik routing. |
| An operator-deployed gateway URL plus a deploy token for your repo. | Seeded data so reviewers click through a working product, not an empty shell. |
GitLab merge-request pipelines, or GitHub pull_request workflows. | Companion services — workers and secondary APIs that share the same preview database. |
One .sprout.yaml and one CI include. | Optional mail — previews send test mail into a shared inbox, addressed per PR so testers can tell them apart. |
| — | Nothing lingers. Teardown on PR close is the normal path; a periodic sweep recovers anything CI missed. |
How each of these is configured lives in the docs: previews for the lifecycle, operator deploy for the shared side.
Adopt in three files
Adoption is one config file (.sprout.yaml), one CI
include, and the Dockerfile your app already ships —
plus two CI variables your operator hands you.
- STEP 1
.sprout.yaml
The smallest config is two keys:
slugandpreview.hostname. - STEP 2
CI include
One include, no scripts — the GitLab component or the GitHub caller workflow.
- STEP 3
Two CI variables
SPROUT_URLandSPROUT_TOKEN, both masked in your CI settings.
slug: myapp
preview:
hostname: "pr-{pr_id}.myapp.preview.example.com"
Onboarding prompt
Agents skip the reading list: paste the block below into your coding harness and it wires the three files for you — the same single copy-paste block as the onboarding prompt page.
You are onboarding this repository to sprout (every pull request gets its own
preview database plus an optional preview app).
Step 1 — read the docs first, in this order.
1. https://simpros.github.io/sprout/llms.txt — the index of every doc page.
2. https://simpros.github.io/sprout/docs/adopting-a-repo.md — the
`.sprout.yaml` manifest reference.
3. https://simpros.github.io/sprout/docs/ci-integration.md — CI wiring for
this forge (GitLab component or GitHub reusable workflow).
Fetch each URL; do not rely on memory of this product.
Step 2 — inspect the repo before writing anything. Runtime and package
manager, the Dockerfile(s), how migrations run, whether fixtures/seed data
exist, which forge hosts it, and the existing CI workflow files. State what
you found in one short paragraph.
Step 3 — derive, then confirm. Propose `slug` and the preview hostname
template (must contain `{pr_id}`, bare host, no scheme/port/path) from the
repo name and its existing deploy conventions, and ask me to confirm both
before you write them. Never invent a domain or a hostname pattern.
Step 4 — write only these files.
- `.sprout.yaml` — the minimal working config: `slug`, `preview.hostname`,
plus `health` and `seed`/`db`/`services`/`mail` only where this repo
actually needs them. Match the manifest keys exactly
(`unknown key: <path>` rejects typos).
- `Dockerfile.seed` — only if preview data is wanted and no seed image
exists yet.
- The CI file for this forge: the GitLab one-include component
(`templates/preview.yml` shape, `sprout_version` pinned) or the GitHub
caller workflow (`examples/adopting-repo/.github/workflows/sprout.yml`
shape, `sprout_version` pinned, `contents: read` + `pull-requests: write`
+ `packages: write`).
Step 5 — tell me the CI variables to set myself (names, masked/secret,
where they go): `SPROUT_URL` (gateway URL), `SPROUT_TOKEN` (deploy token
scoped to this repo), optional `SPROUT_APP_ENV` / `SPROUT_SEED_ENV` dotenv
blobs and `GITLAB_TOKEN` for MR notes. Do not create, print, commit or
guess their values. If you cannot find a required value, ask me. Never
deploy against an unverified gateway URL without asking.
Step 6 — verify what you can without a running gateway: run the CLI's own
manifest loader over the file you wrote (`apps/cli/src/yaml.ts`
`parseSproutYaml`) and paste the result. Then name my next step
(`sprout doctor`, then a first `sprout ci preview` run from CI).
Do not:
- rewrite or refactor application code,
- touch files other than the ones in Step 4,
- invent operator-side settings (`SPROUT_PREVIEW_POSTGRES_URL`,
`SPROUT_TRAEFIK_*`, networks), tokens or endpoints,
- run a deploy or open a PR against a live instance without asking me first.
When you are done, summarise in five lines: files written, values you
confirmed with me, CI variables I must set, commands I should run next,
anything you could not determine.
Humans start at getting started, then adopting a repo and CI integration.
FAQ
Does sprout replace our hosting?
No. It adds per-PR previews next to whatever you already run. Your production setup stays untouched.
Do we need webhooks from our forge?
No. CI on PR open, push, and close drives the lifecycle; a sweep recovers anything CI missed.
What happens to data when the PR closes?
The preview database and its containers are removed. Pushes to an open PR keep the same database; only closing drops it.
Our repo has no database — is sprout overkill?
No. Light repos run SQLite-file or no-database previews: same preview URL and lifecycle, without a Postgres database.
Can two PRs see each other's data?
No. Each PR gets its own database (or its own file volume), so reviewing one PR never disturbs another.
Where do we start?
Read the docs index, or hand the whole job to an agent with the onboarding prompt.