Operate
Container Attachments
How container attachments work, why isolated_paths are repo-relative, and when attached cache paths preserve value across ephemeral runs.
Recommended next
Why this feature exists
Attachments let a container context keep the source tree bind-mounted from the host while moving selected dependency or artifact paths onto Ota-managed storage.
This is the right boundary for platform-sensitive or high-churn paths such as node_modules, .next, .venv, .m2, or .npm when those paths should survive container recreation without polluting the host tree.
- source still comes from the host bind mount at
/workspace - only the declared attachment paths are overlaid
- for
attachments.isolated_paths, ota backs each path with an engine-managed named volume - ota injects
OTA_WORKSPACEautomatically for task execution, so advanced overrides do not need to hardcode/workspace - that means the attached path can survive ephemeral container recreation when the same context shape is reused
- active execution ownership follows that same storage truth, so separate container contexts can coexist on the same logical path name when Ota can prove they resolve to different managed isolation namespaces
The most important rule
attachments.isolated_paths entries are repo-relative paths, not absolute container paths.
Ota maps each entry to /workspace/<path> inside the container.
Correct: declare the repo-relative path. Ota maps .m2 to /workspace/.m2 inside the container.
execution: contexts: app: backend: container attachments: isolated_paths: - .m2Invalid: absolute container paths are rejected. isolated_paths must stay relative to the repo root.
execution: contexts: app: backend: container attachments: isolated_paths: - /workspace/.m2How the mapping works
node_modules and .next are common examples, not special cases baked into ota.
The real rule is simpler: declare any repo-relative path that should be lifted out of disposable container-local state and kept on durable Ota-managed storage instead.
node_modulesbecomes/workspace/node_modules.nextbecomes/workspace/.next.m2becomes/workspace/.m2.npmbecomes/workspace/.npm
These repo-relative paths already line up with where frontend tools usually write, so no extra tool reconfiguration is needed.
execution: contexts: app: backend: container attachments: isolated_paths: - node_modules - .nextWhy these frontend paths are common examples
Frontend repos often use node_modules and .next because those paths already live under the repo root and the tools already write there naturally.
That makes them good examples of attachment-backed paths that need no extra env wiring.
- without an attachment, those paths stay in the container filesystem
- persistent containers may reuse that state because the same container survives
- ephemeral containers usually lose that state because each run starts fresh
- with an attachment, ota lifts that repo path onto durable storage so later runs can reuse it
- the same pattern works for any repo-relative path, not just Node or Next.js paths
When tool reconfiguration is required
An attachment only makes a path durable. It does not automatically force a tool to use that path.
If the tool already writes into a repo-relative path under /workspace, the attachment is enough. If the tool writes somewhere else by default, ota can now derive the common cache redirects for well-known pairs such as Maven plus .m2, npm or npx plus .npm, pnpm plus .pnpm-store, Gradle plus .gradle, pip plus .pip-cache, and Poetry plus .pypoetry-cache.
Use explicit env only when you want to override ota's fallback wiring or point the tool somewhere else on purpose.
Maven usually writes under the container home directory, not under /workspace, so ota derives the redirect automatically when the context isolates .m2.
execution: contexts: application: backend: container attachments: isolated_paths: - .m2 tasks: build: context: application command: exe: mvn args: - packageWhen the repo does not have a node_modules tree but still fetches packages through npm or npx, ota can derive the cache path automatically from .npm too.
execution: contexts: tooling: backend: container attachments: isolated_paths: - .npm tasks: api:automation: context: tooling script: ./scripts/api/run-api-tests.shThe same fallback model also covers pnpm, Gradle, pip, and Poetry when the context isolates their conventional cache paths.
execution: contexts: tooling: backend: container attachments: isolated_paths: - .pnpm-store - .gradle - .pip-cache - .pypoetry-cacheIf you want to override ota's fallback, use OTA_WORKSPACE explicitly instead of hardcoding /workspace.
tasks: build: env: MAVEN_OPTS: -Dmaven.repo.local=${OTA_WORKSPACE}/custom-m2/repository run: mvn packagePersistent versus ephemeral
Persistent containers naturally preserve container-local state because the container itself is reused.
Ephemeral containers do not preserve container-local state by themselves, but attached isolated paths can still preserve value because the state lives in the Ota-managed volume, not in the disposable container filesystem.
- persistent container + no attachment: container-local cache can survive because the container survives
- ephemeral container + no attachment: container-local cache usually does not survive
- ephemeral container + attachment-backed cache path: the cache can survive because the path is volume-backed
- tasks still run as separate tasks; attachments preserve the path, not one shared shell session
When to use it
- use it for repo-relative dependency trees like
node_modules,.next, or.venvthat should stay container-owned - use it for tool caches like
.m2or.npmwhen you also point the tool at/workspace/<path> - use it when ephemeral runs should still get durable cache value without pretending the whole container is persistent
What not to do
- do not put absolute paths like
/workspace/.m2inisolated_paths - do not assume the attachment alone retargets a tool to that path
- do not use
node_modulesas a generic Node answer when the repo does not actually have a repo-local dependency tree - do not treat attachments as one shared session; they only preserve the declared path