Browse the docs
CI/CD
Any CI with a service token
Give a pipeline exactly the environments it needs with a service token, install crunu with checksums, and keep production secrets out of CI altogether.
Last updated
On this page
A CI pipeline gets its variables from Crunu through a service token and the crunu command. This works on any CI system that can run a shell command on Linux or macOS. GitHub Actions, GitLab CI and Bitbucket Pipelines have their own pages with ready-made wrappers and OIDC binding.
Principles
- One service token per pipeline, with the smallest grant. A token
crunu_svc_…gets access only to the environments you choose, for exampleweb-app/staging. The key of a production environment is never issued to it, so even a leaked token can't decrypt production. - Production is fetched by the production machine. The agent runs on the production server and the pipeline only triggers the deploy. Production secrets never pass through CI.
- If CI really needs a production secret, for example to sync variables into a hosting platform, use a separate production token with a CI binding. It then works only in a step of the right repository, branch and deployment environment.
- Preview environments get short-lived tokens with a lease measured in hours.
Step 1. Create a service token
In the web app, open Service tokens, then New token. Give it a name such as github-actions-web-app, choose its environments and delivery, set the lease and, if you like, a usage limit and networks. See Service tokens for what each setting does.
The token is shown only once. The server knows only its public keys and can't recover it. Store it right away in your CI's protected variables as CRUNU_TOKEN.
The token page also shows a CI setup block with the values you need for installation: CRUNU_SERVER, CRUNU_VERSION, CRUNU_INSTALL_URL, CRUNU_INSTALL_SHA256 and CRUNU_BINARY_SHA256. They come from the signed release manifest. Store them as ordinary CI variables. Only CRUNU_TOKEN is a secret.
Step 2. Install crunu and run your command
Three commands install a pinned version of crunu without root and check both checksums:
curl -sSf "$CRUNU_INSTALL_URL" -o /tmp/crunu-install.sh
echo "$CRUNU_INSTALL_SHA256 /tmp/crunu-install.sh" | sha256sum -c -
sh /tmp/crunu-install.sh --version "$CRUNU_VERSION" --sha256 "$CRUNU_BINARY_SHA256" --user-install
exec crunu run --env web-app/staging -- npm testcrunu run signs in with CRUNU_TOKEN, checks signatures and certificates against the organization key whose fingerprint is built into the token, decrypts the variables in memory and starts the command with them. Nothing is written to disk.
The image needs sh and curl or wget. The node, python and alpine images have them. On a plain ubuntu or debian image, install curl and ca-certificates first.
The binary checksum in the CI setup block is for Linux amd64. For arm64 or macOS runners, give one checksum per platform from the release's manifest.json, for example amd64=… arm64=….
Keep the token away from your build
crunu run never passes CRUNU_TOKEN to the command it starts. But the shell that runs the step may still hold it:
- Set
CRUNU_TOKENonly on the step that runscrunu, not for the whole job. Job-level variables reach every step and every process. - Start
crunuwithexec. The step's shell is then replaced bycrunu, and no other process keeps the token in its environment.execmust be the last command of the step, because nothing after it runs.
This protects against accidental leaks and code without root. On runners where any process can become root, a malicious dependency could still read the token. The real protection is a small grant: only the environment the job needs, never production.
Deploying to a production server
-
On the production server, install the agent and enroll the machine once, from Machines, then New machine. Approve it with
payments-api / production. See Install the agent. -
Run the application through the agent so its variables come from the service's memory:
INI [Service] User=payments ExecStart=/usr/local/bin/crunu run --env payments-api/production --restart on-change -- /opt/payments/bin/serverThe
paymentsuser must be in thecrunugroup. -
The pipeline only ships code and restarts the service, for example over SSH with
sudo systemctl restart payments-api. It never sees the secrets.
Syncing into a platform's own variables
Some platforms keep environment variables themselves. Crunu has no built-in sync. You can push values from a pipeline with a production token bound to the main branch and the production deployment environment:
crunu run --env payments-api/production -- sh -c 'printf "%s" "$DATABASE_URL" | vercel env add DATABASE_URL production --force'Every run appears in the audit log: who, when, from which address and which version it received.
Revoking a token
Open Service tokens, the token, then Revoke…. The next sign-in with the token is refused. A pipeline step that already started finishes with what it already received. If the token may have leaked, revoke it, rotate the keys of the environments it had, and reissue the values inside at their providers. See Rotate environment keys.