Docs/ Deployment/ Preview Environments

Preview Environments

Deploy each pull request of a Git app to its own temporary address, built from the pull request's branch and removed when the pull request is closed or merged.

How previews work

When your Git provider reports that a pull request was opened, reopened or received new commits, sh0 clones the pull request's branch, builds it like the app, starts a separate container and routes a dedicated address to it. Production is not touched. When the pull request is closed or merged, sh0 stops the container, removes the route and deletes the record.

sh0 does not write anything back to the pull request: no comment, no status check. The preview's address is visible in the app's Previews tab and with sh0 preview my-api list.

Requirements

  • An app deployed from a Git repository hosted on GitHub, GitLab or Bitbucket. Enabling previews on an app deployed from an archive or an image is refused: no pull request can reach it.
  • The app's webhook configured at the provider (see Git deployment), with pull request events added: “Pull requests” on GitHub, “Merge request events” on GitLab, pull request events on Bitbucket.
  • Previews enabled on the app. sh0 also accepts enabling them on an app that is not a Git app, but nothing will ever create a preview there.

Enabling previews

In the dashboard, open the app's Previews tab, click Enable Preview Environments and set the TTL (hours) (168 hours, seven days, by default). From the CLI:

Terminal
$ sh0 preview my-api enable --ttl 48
Preview environments enabled for app my-api.
TTL: 48 hours (2 days)

$ sh0 preview my-api list
No preview environments found.

$ sh0 preview my-api disable
Preview environments disabled for app my-api.

Disabling stops new previews from being created. Existing previews stay until they expire or are deleted, except that closing or merging their pull request still destroys them.

Preview addresses

The address is a single DNS label made of the pull request number and the app name, joined by dashes: pr-<number>-<app>, followed by the server's base domain. Pull request 12 of the app my-api on a server whose base domain is example.com gets pr-12-my-api.example.com.

On a server that uses the sh0.app address service, the server's identifier is appended with one more dash, still in the same label, so the address stays covered by the wildcard certificate. If the name is already taken, sh0 adds a four-character suffix; long names are shortened to stay within the 63-character limit of a DNS label. A preview keeps its address across new commits.

What a preview shares with production

Since 1.10.3: A preview runs the code of a pull request nobody has reviewed yet, so it does not receive your production variables. It receives only the variables you declare for previews, named PREVIEW_<NAME> on the app or the project: the container sees them without the prefix. PREVIEW_DATABASE_URL becomes DATABASE_URL in the preview, while production's DATABASE_URL never reaches it.

Danger
A PREVIEW_ variable is still handed to code from any pull request. Point it at a staging database and test keys, never at production, and enable previews only on repositories where everyone who can open a pull request is trusted. Since 1.10.3, a preview also runs on its own network: it cannot reach your production apps or your database servers, neither by address nor by name.internal. Give it a database it reaches by a public address.

Deploy hooks do not run for previews.

Lifetime and cleanup

  • Pull request closed or merged: the preview is destroyed right away.
  • TTL reached: every 5 minutes, sh0 destroys running previews whose lifetime has expired. The lifetime is counted from the preview's creation and is not extended by new commits; changing the TTL only affects previews created afterwards.
  • Failed preview: it is not cleaned up by the TTL; delete it from the Previews tab or with sh0 preview my-api delete <id>.
  • An app can have at most 50 previews; beyond that, a new pull request gets no preview until one is deleted.
Note
The behaviour of an actual pull request described on this page is taken from the sh0 source code; the commands shown were run on a test server.