Using Travis CI with Pulumi
Travis CI is a CI/CD service that builds and tests projects hosted in source control. You run Pulumi in a Travis build by installing the Pulumi CLI and invoking it from a .travis.yml file in your repository.
Because Travis runs the CLI directly, this works with a Pulumi program written in any supported language and with any cloud provider Pulumi supports.
This guide assumes Pulumi Cloud as your backend. Pulumi Cloud isn't required to run Pulumi in CI/CD — Pulumi also supports self-managed backends — but the access token, OIDC, and ESC features described here are specific to Pulumi Cloud.
Prerequisites
Before you begin, make sure you have:
- A Pulumi Cloud account and organization.
- A Travis CI account with your repository activated.
- A Git repository connected to Travis CI.
- A Pulumi program. If you don’t have one yet, follow a Get started guide.
Install and configure Pulumi
Travis runs the build steps defined in a .travis.yml file in the root of your repository. Install the Pulumi CLI in the before_install phase so later phases can call it:
# .travis.yml
language: node_js
node_js:
- lts/*
before_install:
- curl -fsSL https://get.pulumi.com | sh
- export PATH="$PATH:$HOME/.pulumi/bin"
script:
- pulumi version
As an alternative to installing the CLI on each build, you can run your build steps inside one of Pulumi’s official container images, which ship the Pulumi CLI and a language runtime preinstalled. Enable Travis’s Docker service and select the image for your language:
language: minimal
services:
- docker
script:
- docker run --rm -e PULUMI_ACCESS_TOKEN -v "$PWD:/work" -w /work/infra
pulumi/pulumi-nodejs bash -c "npm ci && pulumi preview --stack acme/website/staging"
Authenticate with Pulumi Cloud
When your pipeline uses Pulumi Cloud as its backend, it needs a single Pulumi access token to operate. Prefer an organization or team token over a personal one.
Provide the token to the build as the PULUMI_ACCESS_TOKEN environment variable. Set it as an encrypted variable so it isn’t exposed in build logs or committed to source control. The recommended way is the Travis repository settings — under Settings → Environment Variables add PULUMI_ACCESS_TOKEN and leave Display value in build log off. You can also encrypt it into .travis.yml with the Travis CLI:
travis encrypt PULUMI_ACCESS_TOKEN=pul-xxxxxxxx --add env.global
The Pulumi CLI reads PULUMI_ACCESS_TOKEN automatically and logs in non-interactively — no pulumi login step is required.
Pulumi ESC (Environments, Secrets, and Configuration) then supplies cloud credentials, secrets, and configuration to your Pulumi program. Because ESC delivers those values the same way whether the consumer is a pipeline or a developer’s machine, a single environment definition works in both places — you don’t store separate cloud provider keys in Travis.
The trunk-based development workflow
The most common way to run Pulumi in Travis CI follows a trunk-based development model, where work merges into a single main branch and deployments flow outward from there:
- Open a pull request. The build runs
pulumi previewand surfaces the proposed changes for review. - Merge to the main branch. The build runs
pulumi upto deploy the change to an environment that receives continuous delivery, such as a shared development or staging environment. - Promote to production. Pushing a git tag triggers a deployment to production, keeping production updates deliberate and traceable.
The .travis.yml below implements all three stages with conditional jobs. It assumes a Pulumi program in an infra/ directory and stacks named acme/website/staging and acme/website/production:
# .travis.yml
language: node_js
node_js:
- lts/*
before_install:
- curl -fsSL https://get.pulumi.com | sh
- export PATH="$PATH:$HOME/.pulumi/bin"
# Skip the default root-level `npm install`; the Pulumi program lives in infra/.
install: true
jobs:
include:
# Pull request: preview the changes the merge would make.
- stage: preview
if: type = pull_request
script:
- cd infra && npm ci
- pulumi preview --stack acme/website/staging
# Push to the main branch: deploy to the staging environment.
- stage: deploy to staging
if: branch = main AND type = push AND tag IS blank
script:
- cd infra && npm ci
- pulumi up --yes --stack acme/website/staging
# Tag push: promote to production.
- stage: deploy to production
if: tag IS present
script:
- cd infra && npm ci
- pulumi up --yes --stack acme/website/production
To promote a release, push a tag:
git tag release-2026-05-20
git push origin release-2026-05-20
Travis decides when to start a build — on pull requests, branch pushes, and tags — primarily through your repository’s Travis settings (for example, Build pushed branches and Build pushed pull requests). The .travis.yml file can further restrict which branches or tags trigger a build via the top-level branches: key, and the if: conditions above then select which job runs for each build.
For an optional ephemeral environment on each pull request, pair the preview job with a Review Stack, which provisions and tears down a per-PR environment automatically.
Report results on pull requests
Independently of the build you run in Travis, Pulumi offers native version control integrations for popular version control systems. A version control integration lets Pulumi Cloud post infrastructure-change summaries directly on pull requests as comments and status checks, and link each stack update back to the commit and pull request that produced it. See the Version Control page for the systems currently supported.
Speed up builds with caching
Travis workers don’t persist state across runs, so without caching Pulumi must fetch its provider plugins on every build. Use Travis’s caching to persist the Pulumi plugin directory — and your language dependencies — across builds:
cache:
directories:
- $HOME/.pulumi/plugins
- infra/node_modules
Pulumi plugin versions are tied to the provider package versions your program uses, so the cache stays correct as long as those packages are unchanged. For the most deterministic builds, bake the provider plugins into a custom builder image derived from an official pulumi/pulumi-* image and reference that image from your build. See the plugins documentation for details.
Concurrency
If commits merge to the main branch in quick succession, Travis can start overlapping builds that each run pulumi up on the same stack. Pulumi locks stack state during an update, so a concurrent update can’t corrupt your state — the second update fails fast rather than clashing with the first. To avoid those failed builds, set Limit concurrent jobs to 1 in your repository’s settings so deployment builds run one at a time. You can also enable auto-cancellation, which discards builds that are still waiting to run when a newer build arrives — note this won’t cancel a build that has already started.
Additional resources
- Continuous delivery — overview of running Pulumi in CI/CD.
- Pulumi ESC — deliver credentials, secrets, and configuration to pipelines and developers consistently.
- Review Stacks — ephemeral environments created automatically for each pull request.
- CI/CD troubleshooting guide — fixes for common failures when running Pulumi in CI/CD.
Thank you for your feedback!
If you have a question about how to use Pulumi, reach out in Community Slack.
Open an issue on GitHub to report a problem or suggest an improvement.