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_URLto the control node URL andUP_NODE_TOKENto 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 thepr-<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, andclosedpull 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
environmentblock inup.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.