# How to Use mise in CI/CD Pipelines: Complete Configuration Guide

> Integrate mise into your CI/CD pipelines for deterministic tool version management and task execution. Learn how to configure mise for efficient builds and deployments with our comprehensive guide.

- Repository: [jdx/mise](https://github.com/jdx/mise)
- Tags: how-to-guide
- Published: 2026-08-09

---

**mise is a Rust-based CLI that unifies tool version management and task execution, making it ideal for CI/CD pipelines by providing deterministic, cached environments through commands like `mise install` and `mise exec`.**

mise (formerly rtx) is a fast, declarative tool version manager maintained in the **jdx/mise** repository. Using mise in CI/CD pipelines ensures that build, test, and deployment environments match your local development setup exactly, eliminating "works on my machine" errors through its hierarchical configuration system and lockfile support.

## Core Architecture for CI/CD

The mise source code is organized into specific modules that handle CI/CD requirements efficiently. Understanding these components helps you optimize pipeline performance and reliability.

### CLI Entry and Configuration Loading

In [`src/main.rs`](https://github.com/jdx/mise/blob/main/src/main.rs), the CLI bootstrap process parses commands and immediately detects CI environments. When the `CI`, `GITHUB_ACTIONS`, or `GITLAB_CI` environment variables are present, mise automatically switches to "trusted" mode, skipping interactive prompts and assuming configuration files are safe.

The configuration loader in [`src/config/mod.rs`](https://github.com/jdx/mise/blob/main/src/config/mod.rs) reads [`mise.toml`](https://github.com/jdx/mise/blob/main/mise.toml), `.tool-versions`, and environment files, merging them hierarchically. This guarantees that every CI runner receives the exact same tool set and environment variables regardless of the base image.

### Backend System and Toolset Management

The `Backend` trait defined in [`src/backend/mod.rs`](https://github.com/jdx/mise/blob/main/src/backend/mod.rs) abstracts version sources including Aqua and GitHub releases. In CI pipelines, the **Aqua backend** acts as a shared cache for version lists and metadata, dramatically reducing API calls and avoiding rate limits.

Tool resolution happens in [`src/toolset/mod.rs`](https://github.com/jdx/mise/blob/main/src/toolset/mod.rs), which handles version resolution, caching installations in `$HOME/.local/share/mise`, and creating shims. You can either install tools once with `mise install` or use on-demand execution—both paths leverage the same cache directory.

### CI Detection and Trust Model

Mise automatically treats environments with recognized CI variables as trusted. This trust model, documented in the CLI trust system, means:

- Lockfiles containing checksums are trusted without additional verification
- Interactive prompts are never displayed
- Shims are preferred for speed over permanent `PATH` modifications

## Setting Up mise in CI Workflows

### Installation and Bootstrapping

Bootstrap mise by downloading a pre-built release or including the binary in your container image. The standard installation script works across Linux, macOS, and Windows runners:

```bash
curl -fsSL https://mise.run | sh
echo "$HOME/.local/bin" >> $GITHUB_PATH  # For GitHub Actions

```

Alternatively, commit the `./bin/mise` binary directly to your repository for air-gapped or security-hardened environments.

### Caching Strategies

Cache the tool directory between pipeline runs to avoid re-downloading. The cache key should include your lockfile hash for optimal invalidation:

```yaml

# GitHub Actions example

- uses: actions/cache@v4
  with:
    path: ~/.local/share/mise
    key: ${{ runner.os }}-mise-${{ hashFiles('mise.lock') }}

```

For GitLab CI, set `MISE_DATA_DIR` to a project-relative path to enable built-in caching:

```yaml
variables:
  MISE_DATA_DIR: $CI_PROJECT_DIR/.mise/mise-data

cache:
  key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
  paths:
    - .mise/

```

### Handling Rate Limits and Authentication

Mise automatically reads the `GITHUB_TOKEN` environment variable (or tokens from [`github_tokens.toml`](https://github.com/jdx/mise/blob/main/github_tokens.toml)) to authenticate GitHub API requests. This prevents unauthenticated rate-limit errors when resolving tool versions in CI.

The **Aqua registry** further reduces API calls by providing a cached source of truth for version metadata, making pipelines faster and more reliable than direct GitHub API queries.

## Execution Patterns in CI

### Why mise exec Beats Shell Activation

In [`src/cli/exec.rs`](https://github.com/jdx/mise/blob/main/src/cli/exec.rs), the `mise exec` command implements the preferred CI execution pattern. Unlike shell activation (which modifies `PATH` permanently), `exec` sets up the environment, runs your command, then exits cleanly.

**Benefits for CI/CD:**
- **Non-interactive**: Works in containers without login shells
- **Performance**: No permanent `PATH` pollution; shims can be added once and reused
- **Isolation**: Each step gets a clean environment at [`src/cli/exec.rs`](https://github.com/jdx/mise/blob/main/src/cli/exec.rs) boundaries, preventing state bleed between tasks

Use this pattern for all build, test, and lint steps:

```bash
mise exec -- cargo test --verbose
mise exec -- python -m pytest
mise exec -- node build.js

```

### Task Execution with mise run

The task engine in [`src/task/mod.rs`](https://github.com/jdx/mise/blob/main/src/task/mod.rs) handles user-defined scripts with parallel execution support. Define tasks in [`mise.toml`](https://github.com/jdx/mise/blob/main/mise.toml) and trigger them in CI:

```toml
[tasks]
test = "cargo test --all"
lint = "cargo clippy -- -D warnings"

```

Run them sequentially or in parallel:

```bash
mise run test
mise run lint

```

## Complete CI Configuration Examples

### GitHub Actions

This workflow installs mise, caches tools using the lockfile, and runs tests through `mise exec`:

```yaml
name: CI
on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install mise
        run: |
          curl -fsSL https://mise.run | sh
          echo "$HOME/.local/bin" >> $GITHUB_PATH

      - name: Cache mise tools
        uses: actions/cache@v4
        with:
          path: ${{ runner.home }}/.local/share/mise
          key: ${{ runner.os }}-mise-${{ hashFiles('mise.lock') }}

      - name: Install project tools
        run: mise install

      - name: Run tests
        run: mise exec -- cargo test --verbose

```

### GitLab CI

For GitLab, configure the data directory to live within the project path for automatic caching:

```yaml
stages:
  - test

variables:
  MISE_DATA_DIR: $CI_PROJECT_DIR/.mise/mise-data

test:
  image: rust:latest
  stage: test
  script:
    - curl -fsSL https://mise.run | sh
    - export PATH=$HOME/.local/bin:$PATH
    - mise install
    - mise exec -- cargo test --all
  cache:
    key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
    paths:
      - .mise/

```

### Docker Integration

Build images that contain mise and a pre-populated tool cache for the fastest CI starts:

```dockerfile
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y curl
RUN curl -fsSL https://mise.run | sh && \
    echo "$HOME/.local/bin" >> /etc/profile.d/mise.sh

WORKDIR /src
COPY mise.toml mise.lock ./
RUN mise install  # Uses lockfile, caches in /root/.local/share/mise

COPY . .
RUN mise exec -- cargo build --release

```

## Lockfiles and Reproducibility

The `mise.lock` file stores exact versions and checksums for every tool. In CI environments, mise trusts the lockfile and skips verification when checksums and provenance signatures are present, eliminating network calls for already-resolved versions.

According to the mise-lock documentation, this behavior makes `mise install` idempotent and deterministic—you get the same binaries on every pipeline run without version drift. Commit `mise.lock` to version control and reference it in your cache keys for optimal CI performance.

## Summary

- **Bootstrap**: Install mise via `curl https://mise.run | sh` or commit the binary to your repo
- **Cache**: Persist `$HOME/.local/share/mise` or `MISE_DATA_DIR` between runs using lockfile-based cache keys
- **Execute**: Use `mise exec -- <command>` instead of shell activation for clean, isolated CI steps
- **Authenticate**: Export `GITHUB_TOKEN` to avoid API rate limits; leverage the Aqua backend for faster metadata resolution
- **Lock**: Commit `mise.lock` to ensure reproducible builds and skip redundant verification in CI

## Frequently Asked Questions

### How does mise detect CI environments?

Mise checks for standard CI environment variables like `CI`, `GITHUB_ACTIONS`, and `GITLAB_CI` during initialization in [`src/main.rs`](https://github.com/jdx/mise/blob/main/src/main.rs). When detected, it automatically enables trusted mode, skips interactive prompts, and assumes configuration files are safe according to the trust model documented in [`src/cli/trust.md`](https://github.com/jdx/mise/blob/main/src/cli/trust.md).

### Should I use mise install or mise exec in pipelines?

Use `mise install` once at the beginning of your job to populate the cache, then use `mise exec -- <command>` for individual steps. This pattern, implemented in [`src/cli/exec.rs`](https://github.com/jdx/mise/blob/main/src/cli/exec.rs), provides isolated environments for each command without permanently modifying the shell's `PATH`, making it ideal for container-based CI runners.

### How do I avoid GitHub API rate limits in CI?

Export a `GITHUB_TOKEN` environment variable with appropriate scopes—mise automatically uses this for GitHub-hosted tools as documented in [`docs/dev-tools/github-tokens.md`](https://github.com/jdx/mise/blob/main/docs/dev-tools/github-tokens.md). Additionally, the Aqua backend ([`src/backend/mod.rs`](https://github.com/jdx/mise/blob/main/src/backend/mod.rs)) caches version metadata, significantly reducing direct GitHub API calls compared to other version managers.

### Can I use mise with Docker-based CI runners?

Yes. You can either install mise inside the container at runtime or bake it into your Docker image along with a pre-populated tool cache. The latter approach, using `mise install` during the image build, eliminates download time from your pipeline execution and ensures consistent tooling across all environments.