Skip to content

Advanced Elixir cache configuration

Start with the Elixir cache setup guide. The options below are only needed when your project has additional compilation inputs, dependency exceptions or different checkout paths.

Select dependencies#

cache: true caches supported dependencies automatically. A keyword list has the same automatic selection, with optional overrides:

elixir
cache: [exclude: [:a_dependency_with_compile_time_side_effects]]

To limit caching to specific dependencies instead:

elixir
cache: [dependencies: [:nimble_options, :nimble_parsec]]

dependencies: :all explicitly selects all supported dependencies; dependencies: [] selects none. exclude: takes precedence over either selection. Omitting cache, or setting it to false or nil, disables caching.

Supported dependencies are fetched through Hex or Git and use the standard Mix compiler pipeline with pure Elixir sources. Native/custom compilers, custom tracers, source symlinks, umbrella roots and chains containing local dependencies compile normally. Tuist's own bootstrap dependencies also compile normally. Structural eligibility does not prove that arbitrary compilation code is hermetic.

Declare additional inputs#

Source trees, locked dependency identities, upstream sources and BEAMs, explicit effective application configuration, compiler options, Mix environment and target, and toolchain identity are included automatically. Dependency-specific env: and system_env: enter the key and configuration is evaluated under that environment. Application defaults enter through source inputs, not VM loading state.

For dependencies that read files outside their source tree or additional environment variables at compile time, declare those inputs:

elixir
cache: [
inputs: ["schemas", "config/compiler-input.json"],
env: ["YOUR_COMPILER_OPTION"]
]

Elixir macros and module bodies can read arbitrary files, environment, ambient code paths, network responses and time. Tuist does not automatically discover these reads. Changing undeclared inputs can produce stale outputs. Exclude dependencies with time/network-dependent compilation or side effects that must execute on every build; use dependencies: for an audited dependency allowlist when you need tighter control.

Standard compiler/runtime variables (ERL_COMPILER_OPTIONS, ELIXIR_ERL_OPTIONS, ERL_AFLAGS, ERL_FLAGS, ERL_ZFLAGS, SOURCE_DATE_EPOCH, ERL_LIBS, MIX_PATH and MIX_ARCHIVES) are captured automatically. Toolchain identity includes the Elixir build, full OTP version when available, ERTS, compiler bytecode, OS/version, architecture, word size and byte order. Inputs are captured before each lookup and checked again before publication, including changes from earlier compilations.

Reuse across checkout paths#

Checkout paths participate in keys by default. If your dependencies don't rely on producer paths, opt into reuse between different checkout locations:

elixir
cache: [portable: true]

Cached BEAMs retain producer debug/source paths and compile-time __DIR__, __ENV__.file and File.cwd!() values. Portable reuse does not rewrite them. Resource links point to the current source checkout. Upstream BEAM bytes also enter keys and may embed paths, so reuse can require restoring the upstream closure from the same cache rather than recompiling it independently.

Storage and access#

OptionDefaultPurpose
remotetrueUse the shared remote cache after a local miss.
uploadtruePublish new bundles remotely. false still allows reads and local snapshots.
pathTuist's cache directory, under mix/dependencies/v1Choose a different local store.
endpointDiscovered automaticallyOverride the account's HTTPS REAPI endpoint.

upload: false controls client behavior, not authorization. Project cache permissions are enforced by the server. HTTP is only allowed on loopback (localhost, 127.0.0.1, ::1) for development and sends credentials/artifacts unencrypted. Local snapshots and remote bundles are limited to 64 MiB; larger artifacts compile without being cached. The local store has no automatic eviction; remove its contents when needed. Remote artifacts use Kura's existing retention, eviction and project-cleanup rules.

Important

Compiler manifests and BEAMs can embed producer paths and compile-time values, including secrets. Project cache readers can access those artifacts. Cache writers are trusted code producers: digest checks detect corruption, not malicious executable BEAMs. Don't expose cache-write credentials to untrusted PR jobs or compile untrusted dependencies with those credentials available.

Troubleshoot missing hits#

  • Run mix tuist.setup after installing the package and commit the generated Mix configuration. Setup preserves other aliases and Tuist options; it refuses customized compile actions rather than silently removing them.
  • Keep the generated compile alias. Direct mix tuist.compile task discovery can compile dependencies before caching can inspect them.
  • Already-current outputs aren't cache hits and aren't published retrospectively. Clean just a supported dependency's build with mix deps.clean NAME --build after it has populated the cache, then compile again.
  • Match MIX_ENV, MIX_TARGET, compiler settings and declared inputs. Path changes also change keys unless portable reuse is enabled.
  • Unsupported dependencies appear as Not cached, not misses. They are excluded from hit rate. A build with no lookups has an unavailable rate, not 0%.
  • Missing credentials, unavailable endpoints and corrupt snapshots fall back to compilation. Compiler failures still fail the build.
  • CAS records can arrive after the build report. Refresh to see newly ingested transfers. Local hits and legacy builds have no remote CAS outputs.

Manual compile integration#

If setup cannot edit your project's configuration safely, integrate this alias manually, preserving any custom compile actions:

elixir
compile: [
"deps.compile tuist_ex --include-children",
"deps.loadpaths --no-compile --no-deps-check",
"tuist.compile"
]

These steps compile and load Tuist before other dependencies, including on cold parallel builds. The wrapper restores normal dependency checks afterward. The same alias works with Hex, Git and path installations; no script path is needed.

How it works#

Elixir caching uses the shared REAPI ActionCache/CAS pipeline, authentication and Kura-signed analytics used by Bazel, not a separate Mix storage service. A modeled input key references an ActionResult containing the raw-byte SHA-256 digest of a complete compilation bundle. Tuist verifies its files, application metadata, declared BEAMs and SCM manifest before staged installation. Corrupt entries are repaired after successful compilation.

Remote credentials/connections are resolved lazily; idle builds and local hits don't contact remote endpoints. One session is closed on every exit, including compiler failure. Client RPCs have a 30-second deadline. Mix remains responsible for lock checks, local dependency compilation and unsupported work.

Dashboard latency/bytes preserve local versus remote provenance and distinguish unavailable values from measured zero. Remote timing measures client CAS transfers, while local timing includes verification and installation; neither includes compilation. CAS Outputs uses successful Kura-signed transfers, not local copies or action-input keys. Throughput is bytes divided by measured time; compressed sizes are unavailable. Estimated compilation time avoided is the producer's original compile duration, not measured end-to-end build savings.