CI deployments
This guide deploys stable environments from CI. The branch name is the
environment name: pushing to production deploys the production environment,
and pushing to staging deploys staging. Deployments run behind a GitHub
environment, so production can require an approval and each environment can hold
its own control node credentials.
If you want a throwaway environment per pull request instead, see Preview environments.
Prerequisites
- A control node reachable from GitHub Actions, with a deploy node registered.
- An
up.yamldescribing the services of the project. - Control node credentials. Set
UP_NODE_URLandUP_NODE_TOKENas repository secrets when every environment uses the same control node. Set them as environment secrets onstagingandproductioninstead when the two point at different control nodes.
The staging and production environments do not need to be created ahead of
time. GitHub creates an environment automatically the first time a job
references it, but it starts with no secrets and no protection rules. Only a
repository administrator can configure it, so plan that step before the first
deploy.
Secrets
Environment secrets override repository secrets with the same name, and a job can only read an environment’s secrets after that environment’s protection rules pass. The practical setup is:
- Repository secrets
UP_NODE_URLandUP_NODE_TOKENwhen staging and production share a control node. Nothing else is needed. - Environment secrets on
stagingandproductionwhen they use different control nodes. Set the same two names, and the environment value wins.
Install the workflow
Copy templates/github/deploy.yml from the up repository to
.github/workflows/deploy.yml in your repository, and set branches: to the
branches you deploy from. The workflow:
- triggers on pushes to those branches, and the branch name is the environment
name, so pushing to
productiondeploys the environment namedproduction; - sets
environment:on the job, which applies that environment’s protection rules and secrets; - sets
deployment: false, which keeps the gate and secrets but leaves the deployment record to the action, so the action can attach a URL. This is incompatible with custom deployment protection rules; required reviewers, wait timers, and branch and tag policies still apply; - uses a per-environment concurrency group, so a second push waits for the first instead of deploying at the same time.
The job calls the composite action karabohq/up/.github/actions/deploy, which
installs up, creates the environment if it is missing, applies the config,
lists the services, and creates the GitHub deployment with a link to the first
public service URL. The action requires only environment; preview, file,
comment-title, version, and go-version all have defaults, with preview
defaulting to false. Pin the action reference to a commit SHA or to 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.
Approval gates
Protection rules live on the GitHub environment, not in the workflow file. On
the production environment, add required reviewers so a deploy waits for an
approval before it runs. The workflow itself does not change.
Availability depends on the plan. Environment secrets need GitHub Pro or higher for private repositories; GitHub Free only gets them on public repositories. Required reviewers and wait timers are free on public repositories but need GitHub Enterprise for private ones. Private repositories on Pro or Team can still restrict an environment to protected branches and tags, which stops feature branches from requesting a production deploy.
Public URLs for staging and uat
Production services keep the domains declared in up.yaml. A stable
non-production environment cannot use those same domains without colliding with
production, and there is no built-in staging domain yet. Two approaches work:
- Derived per-environment domains: declare a base domain per stable
environment, so up apply with
--env stagingyieldsweb.staging.example.comfrom the sameup.yaml. This is not implemented yet. - A config file per environment: keep
up.staging.yamlnext toup.yamlwith the staging domains, and set the deploy step’sfileinput toup.staging.yamlfor staging. This works today, but the two files must be kept in sync by hand.
Until one of these is adopted, services in staging have no public URL: they are reachable inside the environment network, and the deployment record has no link.
Deploying by hand
The template has no manual trigger. To add one, add a workflow_dispatch input
for the environment and use it in place of github.ref_name for the
environment name. The same environment gate applies, so a manual production
deploy still waits for approval when reviewers are configured.