Repository navigation
Description
Preflight
- I searched existing issues and docs before opening this.
- This proposal is grounded in a real Roomote use case, not a broad generated feature list.
Problem
Successive tasks in the same project repeatedly download dependencies and rebuild code in separate Docker task containers. Cache files created by one task are not automatically available to the next task, even when both use the same Roomote environment.
Examples include Go's module and build caches, npm's package download cache, and equivalent caches for other language ecosystems. Losing these caches between tasks increases network traffic, build time, and the time users wait for implementation and verification.
tool_versions and repository commands automate setup, but they do not persist these caches across task containers. Task-specific workspace volumes preserve one task's files; they do not provide shared cache storage for successive tasks in an environment.
Current workaround
- Download dependencies and rebuild in each new task container.
- Reuse an existing task when appropriate, retaining its local caches, rather than starting an independent task.
- Customize Docker provisioning outside the environment editor to add mounts, requiring deployment-specific maintenance.
Proposed behavior
Allow administrators to configure shared volume mounts for each environment through the dashboard and its YAML definition.
Each environment should have its own declared volumes. When Roomote launches a Docker task using that environment, it should mount the same persistent volumes at the configured container paths. Subsequent tasks should reuse cached data without sharing their source workspaces or task state.
For example, an environment could declare separate volumes for:
- Go module downloads, mounted at a directory selected through
GOMODCACHE. - Go build artifacts, mounted at a directory selected through
GOCACHE. - npm package downloads, mounted at a directory selected through
npm_config_cache.
An illustrative YAML shape could be:
shared_volumes:
- name: go-modules
mount_path: /cache/go-modules
- name: go-build
mount_path: /cache/go-build
- name: npm
mount_path: /cache/npm
env:
GOMODCACHE: /cache/go-modules
GOCACHE: /cache/go-build
npm_config_cache: /cache/npmThe field names are suggestions, not a required schema. Volume names should be scoped to the environment identity: two environments declaring go-build should receive different storage by default.
Expected behavior:
- Create missing volumes automatically and attach them before setup commands or agent execution.
- Preserve cache contents when a task finishes or its container is removed, and mount them again for subsequent tasks and resumes.
- Keep source checkouts and task state isolated; share only explicitly configured directories.
- Make mounted directories writable by the worker user without requiring the agent to fix permissions on every task.
- Support overlapping tasks when the selected cache supports concurrent access; document access semantics and any limitations.
- Provide a way to inspect, clear, and delete environment caches, with clear behavior when mounts are removed or an environment is deleted.
Docker named volumes are one possible implementation. Other storage mechanisms providing equivalent environment-scoped reuse would also meet the need. Initial support could be limited to the Docker provider, with unsupported providers reporting the limitation explicitly.
Affected surfaces
Self-hosting or deployment
Self-hosting implications
Shared volumes consume persistent host storage and need ownership, lifecycle, and cleanup management separate from task-container cleanup. Names should be scoped by deployment and environment identity to avoid accidental sharing.
Tasks running on different Docker hosts would need placement rules or shared storage to reuse the same data; an initial single-host implementation should document this limitation. The basic local-volume workflow should not require another external service or inbound port.
Alternatives considered
- Prepared environment images: useful for preinstalled tools and dependencies, but do not preserve cache entries generated by later task builds. Image preparation and writable cache volumes address complementary needs.
- Task snapshots or retained containers: preserve one task's state, but do not provide a shared cache for independent tasks.
- Remote package proxies or build-cache services: can help, but require ecosystem-specific configuration and do not replace a general environment-level mount mechanism.
- One global cache for all environments: simpler, but does not give projects independent configuration and lifecycle control.
Documentation impact
Document the UI and YAML fields, supported providers, mount paths and permissions, cache-directory configuration for common ecosystems, concurrent-access limitations, multi-host behavior, and cache cleanup. Explain that these volumes persist across tasks independently of source-code checkout, worker-image updates, and task snapshots.
Activity
roomote-community commentedon Oct 7, 2026
I've analyzed this issue and here's my proposed implementation plan:
Add an optional
shared_volumesfield topackages/types/src/environment-config.tswith entries shaped as{ name, mount_path }. Validate a constrained logical name, an absolute cache path, unique names and mount paths, a small per-environment limit, and reject overlapping/reserved paths such as the task workspace. Keep this to Docker-managed named volumes rather than arbitrary host bind mounts. Add the field toapps/web/src/components/settings/environments/yaml-utils.tsso YAML round-trips preserve it, and expose the same list in the visual environment editor using the existing Docker-project list patterns.Add a shared Docker environment-volume helper under
packages/compute-providers/src/that derives an opaque deterministic volume name from the durable deployment identity, environment UUID, and logical volume name. Label every volume with Roomote-managed resource type, deployment, environment, and logical-name labels. This keeps two environments or deployments from accidentally sharing storage and makes volumes discoverable even after a declaration is renamed or removed.In
apps/controller/src/compute-providers/spawn-docker-worker.ts, create missing declared volumes before the worker container starts, attach them with Docker--mount type=volume, and initialize each mount root for the resolved worker UID/GID beforeinstall-worker.shand environment setup run. Leave these volumes out ofdestroyDockerInstance()inpackages/compute-providers/src/adapters/docker.ts; task workspace volumes remain task-scoped, while retained-container resumes naturally retain the original shared mounts.Add a distinct provider capability in
packages/types/src/compute-providers/capabilities.tsfor environment shared volumes, enabled only for the Docker provider initially. Add a common spawn-time guard so a task using an environment withshared_volumesfails early with an actionable unsupported-provider message instead of silently running without its caches.Add an admin-only environment-volume management queue, following the existing Docker validation queue pattern in
packages/sdk/src/server/lib/docker-environment-validation.tsandapps/bullmq/src/docker-validation-queue.ts. The BullMQ process already has restricted Docker volume API access in the supported Compose deployment. Use it to:Add tRPC commands and dashboard controls alongside the existing environment/snapshot controls in
apps/web/src/trpc/routers/_app.ts,apps/web/src/trpc/commands/environments/, andapps/web/src/components/settings/environments/Environments.tsx. Show each cache's logical name, mount path, existence/usage, and clear/delete actions, and update environment-deletion copy to state that cache cleanup is queued.Cover the contract with schema and YAML round-trip tests, Docker spawn tests for deterministic isolation, attachment, ownership, fresh-task reuse, and standby resume, teardown tests proving per-task cleanup does not remove environment volumes, queue tests for inspect/clear/delete and active-container conflicts, provider-capability tests, and environment UI/command tests. The main existing suites are
packages/types/src/environment-config.test.ts,apps/web/src/components/settings/environments/YamlEnvironmentEditor*.test.*,apps/controller/src/compute-providers/__tests__/spawn-docker-worker.test.ts,apps/controller/src/compute-providers/__tests__/docker-sandbox-security.test.ts, and environment command/component tests.Update
apps/docs/environments.mdx,apps/docs/environments/definition.mdx, andSELF_HOSTING.mdwith the YAML example, Docker-only initial support, writable-path rules, explicit cache cleanup, concurrent read/write semantics, cache-poisoning/trust considerations, and the single-host limitation. Concurrent tasks would mount the same volume read/write; Roomote would not serialize access, so the configured package/build cache must support concurrent writers.This approach keeps source checkouts and task state isolated, reuses only explicitly declared cache directories, preserves caches across ordinary task teardown, and gives administrators a bounded lifecycle instead of turning cache volumes into untracked Docker resources. It also avoids a database migration: the declaration stays in the existing environment config JSON, while Docker labels provide runtime discovery.
Please tag @roomote if you'd like me to implement this, or reply with feedback on the plan.