Preview environments

Reviewing a change by reading it works until the change touches a migration, a queue or a third-party callback. A preview environment is that branch actually running: the project it belongs to, copied, deployed from the pull request's own commits, on a URL you can open.

Kaja previews the whole project, not one container. An app without the database it talks to and the configuration that connects them is not a preview of anything.

Turning them on

Previews need two things that are probably already true: the GitHub App installed on the repository, and at least one app in the project deployed from it.

Then open the project, go to Settings → Preview environments, and switch on Enable previews. The same panel holds the four answers that cannot be defaulted for you:

SettingDefaultWhat it does
Build pull requests from forksOffSee Pull requests from forks before turning this on.
Skip draft pull requestsOffLeaves a draft without a preview until it is marked ready.
Delete after (days without a push)7Removes a preview whose pull request has gone quiet. Zero means never.
Most previews at once5Refuses new previews past this many, so a busy repository cannot fill the cluster. Zero means no cap.

Below the table, the panel asks what each of the project's managed services becomes in a preview, which is Databases and other services below. Above it, the panel states what one preview will reserve on the cluster: the apps and services it copies, and the CPU and memory they ask for. Kaja does not autoscale your hardware, so the cost of the switch is on screen before you touch it.

What happens on a pull request

Opening a pull request duplicates the whole project onto its own URL. Merging or closing it takes the copy with it.

Opened. The project is duplicated into a new one named <project>-pr-<number>, carrying its apps, managed services, secrets, configuration, routes and tasks. Its apps build from the pull request's head, and each one that serves traffic gets a generated URL.

Its builds and deploys are filed under the preview itself, so a failed build is visible where you are looking rather than in the project it was copied from.

Kaja comments once. A single comment on the pull request lists each app with its URL and the state of its build, the commit being served, the services the preview was given, and a link into the console. If a build fails, the comment says outright that the URL is still serving the image the preview was created with, and links the run that failed.

More commits land. The preview rebuilds, and that same comment is edited in place rather than a new one being posted underneath.

Merged or closed. The preview and everything in it is deleted, and the comment says so. Deleting the base project deletes its previews with it.

Gone quiet. A preview with no pushes for the configured number of days is removed the same way. The clock runs from the last push, not from when the preview was created.

Databases and other services

Each managed service in the base project becomes one of four things in a preview:

  • Fresh — the preview gets its own instance, with its own credentials. This is the default, and the only one under which a preview is genuinely isolated: a half-finished migration in a pull request cannot reach real data.
  • Seeded from backup — the preview gets its own instance, with its own credentials, restored from the base project's most recent backup. Isolated exactly like fresh, and holding a copy of that project's data, which catches a larger class of bug in review than an empty schema does. It needs the base service to have a backup destination configured. Without one, or if the restore cannot start, the preview is created with an empty database and the reason is written into the pull request comment rather than the preview failing.
  • Shared — the preview's apps are pointed at the base project's instance instead. Cheaper, and a second writer on the base project's data. The Previews tab flags any service set this way, and the pull request comment names it, so it is visible rather than discovered.
  • Skipped — the preview gets nothing for that service. Useful for something the reviewed code does not touch and you would rather not pay for once per pull request.

A fresh database starts empty. Getting a schema into it is what an app's pre-deploy command is for: it runs with the incoming image before the new version serves, which in a preview means before anyone opens the URL.

A preview runs on the same cluster as the project it copies.

What you can do inside a preview

A preview is a copy, so the console gives it the verbs that make sense for one. Its apps, services, tasks, secrets, logs, metrics and terminal are all there, which is the reason to open one rather than just click the URL. You can also delete it early, without waiting for the pull request to close.

What it withholds are the project verbs. Its settings are read-only, because the spec is a copy of the base project's and editing it here drifts from the thing under review. It cannot be paused, saved as a template, or given previews of its own. Configure the project, and the preview follows on the next push.

Secrets in a preview

A preview holds a copy of the project's secrets, with their real values. That is deliberate, since a preview with blank credentials previews nothing, and it is the reason the fork setting below is off by default. It is worth knowing before you turn previews on for a repository whose pull requests come from people you would not give those credentials to.

Pull requests from forks

Off by default, and worth leaving off. A fork pull request is code written by someone outside your repository, and building a preview of it means running that code inside your cluster, in a project holding a copy of your secrets. Turn it on only for a repository where you would already have been comfortable with that.

What a preview is not

It is not a copy of your data unless you ask for one. A fresh service starts empty, exactly like a project spawned from a template. Seeding from a backup is the deliberate opt-in, and it is worth deciding per service rather than everywhere: it puts a copy of real data in an environment a pull request created automatically.

It is also not a staging environment. It lives as long as its pull request does, and everything in it goes when that closes.

Next steps