Skip to content

Diff: architecture/cargo-target-per-user-discipline

From c1035fd to c1035fd

+0 / −0 lines
BeforeAfter
--- ---
schema: foundry-doc-v1 schema: foundry-doc-v1
title: "Per-user build cache discipline — preventing cross-user Cargo races" title: "Per-user build cache discipline — preventing cross-user Cargo races"
slug: cargo-target-per-user-discipline slug: cargo-target-per-user-discipline
language: en language: en
category: architecture category: architecture
type: topic type: topic
status: active status: active
bcsc_class: public-disclosure-safe bcsc_class: public-disclosure-safe
last_edited: 2026-05-25 last_edited: 2026-05-25
editor: pointsav-engineering editor: pointsav-engineering
cites: [] cites: []
paired_with: cargo-target-per-user-discipline.es.md paired_with: cargo-target-per-user-discipline.es.md
--- ---
Cargo's incremental build cache lives under `target/`. By default that is a per-crate directory inside each crate. The workspace overrides this via the `CARGO_TARGET_DIR` environment variable pointing at a shared path, so the build cache is shared across all crates in the monorepo — saving disk space and rebuild time. Cargo's incremental build cache lives under `target/`. By default that is a per-crate directory inside each crate. The workspace overrides this via the `CARGO_TARGET_DIR` environment variable pointing at a shared path, so the build cache is shared across all crates in the monorepo — saving disk space and rebuild time.
This works for a single developer. With two developers in the workspace group it becomes a race. Cargo's `target/.cargo-lock` serialises full-tree builds, so two `cargo build` invocations on different crates block one another. Worse, mixed file ownership — build artefacts written by one user interleaved with another user's — causes sporadic `Permission denied` errors during incremental rebuild, even when the crates appear to be unrelated. This works for a single developer. With two developers in the workspace group it becomes a race. Cargo's `target/.cargo-lock` serialises full-tree builds, so two `cargo build` invocations on different crates block one another. Worse, mixed file ownership — build artefacts written by one user interleaved with another user's — causes sporadic `Permission denied` errors during incremental rebuild, even when the crates appear to be unrelated.
The fix: a system profile script exports `CARGO_TARGET_DIR` pointing at a per-user subdirectory (`<shared-root>/$USER/`). Each user gets a private subdirectory. The build cache stays shared within a user — the original optimisation is preserved — but no longer crosses users. The environment variable takes precedence over `~/.cargo/config.toml`'s `target-dir` setting, so the change takes effect on any new shell without per-user dotfile edits. The fix: a system profile script exports `CARGO_TARGET_DIR` pointing at a per-user subdirectory (`<shared-root>/$USER/`). Each user gets a private subdirectory. The build cache stays shared within a user — the original optimisation is preserved — but no longer crosses users. The environment variable takes precedence over `~/.cargo/config.toml`'s `target-dir` setting, so the change takes effect on any new shell without per-user dotfile edits.
The migration involved moving the existing 3.3 GiB cache into one user's subdirectory. That user's incremental state is preserved; the other developer starts with an empty cache that fills on first build. The migration involved moving the existing 3.3 GiB cache into one user's subdirectory. That user's incremental state is preserved; the other developer starts with an empty cache that fills on first build.
Service accounts in the workspace group (local-doorman, local-design, and similar) do not run Cargo and do not receive a per-user cache directory. The profile script only exports when the shared cache root exists, so non-workspace contexts are unaffected. Service accounts in the workspace group (local-doorman, local-design, and similar) do not run Cargo and do not receive a per-user cache directory. The profile script only exports when the shared cache root exists, so non-workspace contexts are unaffected.
The lesson generalises: any group-writable shared workspace path needs explicit design around which operations are safe to share (reads, content appends) and which require per-user partition (build state, lock files, temp). Cargo is the prominent example; the same logic applies to anything with mutable state under a shared root. The lesson generalises: any group-writable shared workspace path needs explicit design around which operations are safe to share (reads, content appends) and which require per-user partition (build state, lock files, temp). Cargo is the prominent example; the same logic applies to anything with mutable state under a shared root.
## See also ## See also
- [[foundry-services-slice-model]] — the cgroup partitioning that protects shared service resources in the same multi-developer environment - [[foundry-services-slice-model]] — the cgroup partitioning that protects shared service resources in the same multi-developer environment
- [[multi-engine-session-coordination]] — the session-lock protocol that prevents a related class of same-repo conflicts - [[multi-engine-session-coordination]] — the session-lock protocol that prevents a related class of same-repo conflicts
- [[totebox-session]] — the session model that the multi-operator workspace is built around - [[totebox-session]] — the session model that the multi-operator workspace is built around