Preview environments

This guide gives every pull request its own environment with its own URLs, then removes it when the pull request closes. Previews are ordinary environments marked as ephemeral, so they use the same networks, volumes, health checks, and proxy as every other environment.

Prerequisites

Previews need four things before the first pull request:

  • A deploy node, registered with the deploy role. See Setting up nodes.
  • A wildcard DNS record for the preview base domain that points at that node, for example *.preview.example.com. Preview URLs are subdomains of the base domain, so one wildcard record covers every pull request.
  • The proxy obtains a certificate for each preview host through ACME, so no wildcard certificate is needed. The hosts must resolve and ports 80 and 443 must reach the node for the challenge to succeed. Keep in mind the certificate authority’s limit on new certificates per domain per week.
  • Control node credentials. Set UP_NODE_URL to the control node URL and UP_NODE_TOKEN to its auth token, as repository secrets. They apply to every environment. If pull request previews should use a different control node, set the same two names as environment secrets on the pr-<n> environment instead, because environment secrets override repository secrets. These environment variables override ~/.config/up/config.toml, which lets CI run without a config file on disk.

Declare the base domain

Add a preview block to up.yaml and name the base domain:

project: acme

preview:
  base_domain: preview.example.com

services:
  - name: web
    domains:
      - example.com
    source:
      type: git
      url: https://github.com/acme/web

The block is inert until an apply passes --preview, so normal applies keep using the domains you declared. See the preview configuration reference for the field.

How preview URLs are derived

A preview apply replaces each service’s first declared domain with <environment>-<service>.<base_domain>. Pull request 42 of the config above serves web at pr-42-web.preview.example.com, while production keeps example.com. Services without a declared domain stay private: they are reachable inside the environment network and have no public URL.

Environment names used with --preview must be lowercase alphanumeric with dashes, because the name becomes part of a hostname. pr-42 and release-1-2 are valid; PR_42 and release/1.2 are not.

Install the workflow

Copy templates/github/preview.yml from the up repository to .github/workflows/preview.yml in your repository. The workflow:

  • runs on opened, synchronize, reopened, and closed pull request events, and skips draft pull requests;
  • uses a concurrency group per pull request, so two quick pushes queue instead of deploying at once;
  • pins the build to the pull request head commit with --git-ref, so a push that lands during a deploy never changes what that deploy builds;
  • posts one comment per pull request with a table of service name, URL, status, and short commit, and updates that same comment on later pushes by looking for a hidden marker;
  • records a GitHub deployment for the preview, so the pull request shows a “View deployment” button that links to the preview URL;
  • removes the environment on closed, whether the pull request merged or was abandoned.

The environment is created with up environment create --if-not-exists, so every push after the first reuses the same environment instead of failing on the duplicate name.

The workflow calls the composite action karabohq/up/.github/actions/deploy, which installs up, creates the preview environment, applies the config, records the GitHub deployment, and maintains the pull request comment. When the pull request closes, the action sees the closed event, marks the deployment inactive, and removes the environment. The action takes environment plus five optional inputs, of which the template sets preview: true; file, comment-title, version, and go-version keep their defaults. Pin the action reference to a commit SHA or a release tag before relying on it in production; the repository has no release tags yet, so @main is the only moving option until the first release.

The GitHub environment named pr-<n> is created automatically the first time the job runs. It starts with no secrets and no protection rules, and only a repository administrator can configure it.

GitHub deployments

The workflow creates a deployment named pr-<n> for the pull request head commit and marks it transient, so GitHub treats it as an environment that will disappear. GitHub shows it on the pull request as a “View deployment” button and on the repository deployments page. When the pull request closes, the workflow posts an inactive status, which shows the deployment as destroyed.

The workflow needs the deployments: write permission for this; it is set at the top of the file. The GitHub environment object named pr-<n> stays behind after teardown, because deleting it needs repository admin rights. Repository administrators can remove old ones under Settings, Environments.

Run a preview by hand

The workflow composes ordinary commands, so you can do the same locally:

up environment create --if-not-exists --preview pr-42
up apply --pr 42 --preview --git-ref <commit-sha>
up service list --env pr-42

up environment create --preview marks the environment as ephemeral and auto-selects a deploy node; --if-not-exists makes a repeat call succeed without creating a second environment. up apply rewrites the domains, overrides the git ref on every git source, and converges the environment. up service list with --env shows only that environment’s services.

Remove the preview when you are done:

up environment remove pr-42 --force

Notes and limits

  • Preview applies ignore the environment block in up.yaml: --env (or --pr) names the target, and placement comes from the environment record. This keeps a pinned production node from pulling a preview off its own node.
  • Image sources are not rewritten. Previews build git sources at the pull request commit; bring your own per-pull-request image tags if you deploy pre-built images.
  • Pull requests from forks do not receive repository secrets, so the workflow only deploys previews for branches in the same repository.