cargo-nextest Advanced Configuration: Filters, Partitioning, and CI Integration
cargo-nextest runs Rust tests faster and with better output than cargo test. Beyond the basics, it has a configuration system that lets you filter which tests run, partition tests across parallel CI jobs, retry flaky tests automatically, and produce JUnit XML for CI dashboards — all without changing test code.
Key Takeaways
- .config/nextest.toml is the config file — commit it to version control
- Test filters use Rust-based expressions: binary, test name, tags
- --partition hash:1/4 splits tests across 4 parallel CI shards
- Profiles let you define different behavior for local vs CI runs
- --retries auto-retries flaky tests without failing the build
Configuration file
All nextest configuration lives in .config/nextest.toml at the workspace root. Create it once and commit it:
[profile.default]
# How many tests to run in parallel
test-threads = "num-cpus"
# Retry failed tests up to 2 times before marking as failed
retries = 0
# Fail fast: stop after first test failure
fail-fast = false
# Time limit per test
slow-timeout = { period = "60s", terminate-after = 3 }Profiles
Profiles let you define different settings for different contexts:
[profile.default]
test-threads = "num-cpus"
retries = 0
[profile.ci]
# More aggressive parallelism on CI (typically more cores available)
test-threads = 8
# Retry flaky tests automatically on CI
retries = 2
# Collect JUnit XML output
[profile.ci.junit]
path = "target/nextest/ci/junit.xml"
[profile.slow]
# Profile for running the full suite including slow integration tests
test-threads = 2
slow-timeout = { period = "300s", terminate-after = 2 }Run a specific profile:
cargo nextest run --profile ci
cargo nextest run --profile slowTest filters
nextest's filter expressions are more powerful than substring matching:
# Run tests by name (substring)
cargo nextest run test_auth
<span class="hljs-comment"># Run tests in a specific binary
cargo nextest run --<span class="hljs-built_in">test integration_tests
<span class="hljs-comment"># Run tests matching a regex
cargo nextest run -E <span class="hljs-string">'test(~"auth")'
<span class="hljs-comment"># Run tests NOT matching a pattern
cargo nextest run -E <span class="hljs-string">'not test(~"slow")'
<span class="hljs-comment"># Run tests in a specific package
cargo nextest run -p my-crate
<span class="hljs-comment"># Combine: tests in my-crate that contain "user"
cargo nextest run -E <span class="hljs-string">'package(my-crate) and test(~"user")'The -E flag takes a filter expression. Available predicates:
| Predicate | Meaning |
|---|---|
test(name) |
Test name matches (substring) |
test(~"regex") |
Test name matches regex |
binary(name) |
Test binary matches |
package(name) |
Package matches |
kind(lib) |
Test kind: lib, bin, test, bench, example |
not X |
Negation |
X and Y |
Both must match |
X or Y |
Either matches |
Partitioning for parallel CI
Partition splits your test suite into N equal shards, each running independently on a separate CI job:
# Shard 1 of 4
cargo nextest run --partition <span class="hljs-built_in">hash:1/4
<span class="hljs-comment"># Shard 2 of 4
cargo nextest run --partition <span class="hljs-built_in">hash:2/4
<span class="hljs-comment"># Shard 3 of 4
cargo nextest run --partition <span class="hljs-built_in">hash:3/4
<span class="hljs-comment"># Shard 4 of 4
cargo nextest run --partition <span class="hljs-built_in">hash:4/4The hash strategy distributes tests by name hash — reproducible across runs, no coordination required. Each CI job gets a different shard index.
GitHub Actions matrix example:
jobs:
test:
strategy:
matrix:
shard: [1, 2, 3, 4]
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-nextest
- run: cargo nextest run --profile ci --partition hash:${{ matrix.shard }}/4Four jobs run in parallel, cutting total CI time by up to 75%.
Retry configuration for flaky tests
Configure retries globally or per-test in the config file:
[profile.ci]
# Retry all failing tests up to 2 times
retries = 2For per-test overrides (some tests are known-flaky):
[[profile.ci.overrides]]
filter = 'test(~"integration")'
retries = 3
slow-timeout = { period = "120s", terminate-after = 2 }
[[profile.ci.overrides]]
filter = 'test(~"snapshot")'
retries = 0 # snapshot tests should never be flakyOverrides stack from most specific to least specific. The first matching override wins.
Thread limits for resource-intensive tests
Some tests (database, network) can't run fully in parallel:
[[profile.default.overrides]]
# Limit database tests to 4 parallel executions
filter = 'test(~"db_")'
test-threads = 4
[[profile.default.overrides]]
# Run end-to-end tests serially
filter = 'test(~"e2e_")'
test-threads = 1Timeout configuration
nextest distinguishes between slow (warn) and termination timeout:
[profile.ci]
# Warn after 60 seconds, kill after 3 warnings (180 seconds total)
slow-timeout = { period = "60s", terminate-after = 3 }
[[profile.ci.overrides]]
# Integration tests get more time
filter = 'test(~"integration")'
slow-timeout = { period = "120s", terminate-after = 2 }When a test exceeds the period, nextest marks it as "slow" in output. When terminate-after is exceeded, it kills the test process.
JUnit XML output
CI systems (GitHub Actions, GitLab, CircleCI) can display test results from JUnit XML:
[profile.ci.junit]
path = "target/nextest/ci/junit.xml"
# Report each retry as a separate test case
report-skipped = trueGitHub Actions example using the XML for annotations:
- run: cargo nextest run --profile ci
- name: Upload test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: target/nextest/ci/junit.xmlListing tests without running them
Useful for debugging filter expressions:
# List all tests
cargo nextest list
<span class="hljs-comment"># List tests matching a filter
cargo nextest list -E <span class="hljs-string">'test(~"auth")'
<span class="hljs-comment"># List tests in JSON format (for tooling)
cargo nextest list --message-format jsonComplete CI configuration example
A .config/nextest.toml for a typical Rust web service:
[profile.default]
test-threads = "num-cpus"
retries = 0
fail-fast = false
slow-timeout = { period = "30s", terminate-after = 2 }
[profile.ci]
test-threads = 8
retries = 2
fail-fast = false
slow-timeout = { period = "60s", terminate-after = 2 }
[profile.ci.junit]
path = "target/nextest/ci/junit.xml"
report-skipped = true
# Database tests run serially to avoid connection pool exhaustion
[[profile.ci.overrides]]
filter = 'test(~"db_") or test(~"_database") or test(~"migration")'
test-threads = 1
retries = 1
# Integration tests get more time
[[profile.ci.overrides]]
filter = 'kind(test) and not test(~"unit_")'
slow-timeout = { period = "120s", terminate-after = 3 }Archive and reuse test binaries
nextest can archive compiled test binaries to run them later (e.g., after cross-compilation):
# Compile and archive
cargo nextest archive --archive-file tests.tar.zst
<span class="hljs-comment"># Run from archive (on the same or another machine)
cargo nextest run --archive-file tests.tar.zstThis is useful in Docker-based CI where you want to compile once and run tests in a different environment.
Installation
# Via cargo-binstall (fast)
cargo binstall cargo-nextest
<span class="hljs-comment"># Via cargo (slower compile)
cargo install cargo-nextest
<span class="hljs-comment"># In CI with taiki-e/install-action
- uses: taiki-e/install-action@cargo-nextestSummary
cargo-nextest's configuration system covers the common friction points in Rust CI: flaky test retries, parallel shard distribution, per-test thread limits, and JUnit XML output. All of this is declared in .config/nextest.toml without touching test code. Start with the basic profile setup, add partitioning when your suite grows past a few minutes, and layer in per-test overrides as you encounter specific timing or resource constraints.