Concepts
Preview turns a docker-compose stack and a pull request into a live, isolated environment. This page covers the mental model; the reference has the exact commands, fields, and env vars.
What is a preview?
Section titled “What is a preview?”A preview is a running copy of your application, scoped to one pull request (or one ad-hoc creation), reachable at a stable URL. It consists of:
- Your app, run from your
docker-composefile inside an isolated pod. - A write-isolated Snap branch of your database, injected as a connection string.
- An overlay proxy in front of the app that injects the Destesi shell UI without touching your code.
The compose file in the pull request is the source of truth. Change a service in the PR and the preview changes with it — there is no separate environment definition to keep in sync.
The mental model: the PR is the environment
Section titled “The mental model: the PR is the environment”Most preview tools deploy a build artifact to shared staging, where data drifts and one PR’s changes bleed into another. Preview inverts that:
- One environment per pull request. Identity is
(workspace, pull request number). Pushing a new commit redeploys the same preview in place rather than stacking up new ones. - The database is forked, not shared. Each preview gets its own copy-on-write branch of your real data. Reviewers see realistic state; writes during review stay contained and vanish when the preview is torn down.
- The source is never written to. Snap’s branch is copy-on-write — your production database is read from to seed the branch and never mutated.
How a preview is built
Section titled “How a preview is built”When you create a preview (via the CLI, a GitHub Actions workflow, or the web dashboard), a controller picks up the work and runs this pipeline:
- Resolve the source. For a GitHub source, the controller fetches the repository archive using your workspace’s GitHub token from Connect. For a local source (
--from-dir), the CLI tars the directory and uploads it. - Fork the database branch. If the stack needs a database, the controller forks a Snap branch from the environment’s remote (or the one you named) and waits for it to be ready.
- Resolve environment templates. Declared env values like
${postgres.dsn}are resolved server-side against the freshly-forked branch. Unresolved references fail the preview fast. - Provision the pod. A pod runs your
docker compose up --build, fronted by the overlay proxy and exposed on a stable address. - Promote to healthy. The preview becomes
healthyonly once it is actually reachable over HTTP — not merely scheduled.
Dev mode
Section titled “Dev mode”Dev mode runs your preview from the development stack with hot reload, so source edits — your own, or ones the preview agent pushes — take effect in the running preview without a full image rebuild. Turn it on with the Dev mode checkbox in the create form, or --dev-mode on dst preview create / upsert.
It requires a docker-compose.dev.yml in the repository: dev mode tells the runner to prefer that file (the development stack, with its bind-mounted source and watch command) over the production compose. If the file isn’t present, the preview fails fast rather than silently falling back.
When to use it:
- Use it for iterative review — when you expect to push fixes through the preview agent or want the preview to track an actively-changing branch.
- Leave it off for a clean, build-once snapshot of the PR as it stands. The default build path runs
docker compose up --build, which produces a more production-like preview.
A preview running in dev mode is marked with a dev mode badge in the dashboard.
States
Section titled “States”A preview moves through a small state machine:
pending → building → starting → healthyThese are the exact status labels you’ll see on the preview’s status badge in the dashboard.
- pending (transient) — created and queued, waiting for the controller to claim it.
- building (transient) — the controller has claimed the work: fetching the source archive, forking the Snap branch, resolving env templates, and building images.
- starting (transient) — images are built and containers are coming up behind the overlay proxy; the preview is not yet answering over HTTP.
- healthy — reachable over HTTP. This is a real reachability check, not just “pod scheduled”.
- failed (terminal) — a build, run, or configuration error. The reason is on the preview’s
last_error. - deleted (terminal) — torn down; the pod and the forked branch are cleaned up.
A preview can return to pending when you redeploy or push a new commit — the same record is reused, so it cycles back through building and starting rather than creating a new preview.
Why the database is the hard part
Section titled “Why the database is the hard part”A preview is only as useful as its data. Booting an empty database next to the app produces a preview that “renders” but can’t be exercised. Preview leans on Snap for this:
- Copy-on-write branching means a fork is fast and cheap regardless of database size — you’re not dumping and restoring gigabytes per PR.
- Write isolation means a reviewer can create, edit, and delete freely without corrupting the source or colliding with another preview.
- Per-preview teardown discards the branch with the pod, so previews leave no residue.
This is why connecting a Snap remote is part of getting started — it’s the substrate the preview forks from.
The overlay: review without a checkout
Section titled “The overlay: review without a checkout”The overlay proxy fronts every preview and serves a shell UI under /.destesi/*. It exists so a reviewer can do a full review — read the change, click through the app, and ship a fix — from the preview URL alone:
- A change-summary modal orients the reviewer on first load.
- The preview agent can edit code and push back to the branch using the GitHub App installation token. Branch protection and CODEOWNERS still apply, so the agent can’t bypass your governance.
- OAuth passthrough lets Google, GitHub, Microsoft, and Slack logins work on ephemeral preview URLs against a single pre-registered callback, so you don’t register a redirect URI per PR.
The overlay is injected by the proxy; your application code is unchanged and unaware of it.
How it fits the wider suite
Section titled “How it fits the wider suite”Preview is a peer product in the Destesi suite — one login, one workspace, opened from the 9-dot launcher.
- Workspaces — the unit of collaboration. Every preview, key, and remote is workspace-scoped.
- Accounts & SSO — a single session follows you across products.
- Connect — third-party credentials (your GitHub connection) are added once per workspace and fetched by products at call time, never stored per-app.
- Snap — the database branching engine each preview forks from.