diff --git a/common/src/common/config.py b/common/src/common/config.py index 73309ef..8dbb5cf 100644 --- a/common/src/common/config.py +++ b/common/src/common/config.py @@ -86,7 +86,7 @@ class Settings(BaseSettings): # ── local FS roots ──────────────────────────────────────────── workspace_root: str = Field( - default="/workspace/workspaces", + default="/app/workspaces", description=( "Schedule subprocess cwd; staging area for notebook_runner. " "Backend no longer writes here — workspace files live in " diff --git a/docker-compose.yml b/docker-compose.yml index 6f46f1e..9ab739f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -88,7 +88,7 @@ services: environment: DATABASE_URL: ${DATABASE_URL:?DATABASE_URL is required} SERVICE_NAME: runtime-manager - WORKSPACES_ROOT: /workspace/workspaces + WORKSPACES_ROOT: /app/workspaces PUBLIC_BASE_URL: http://runtime REMOTE_BUCKET: rustfs:${RUSTFS_WORKSPACE_BUCKET:-workspaces} RCLONE_CONFIG_RUSTFS_TYPE: s3 @@ -105,7 +105,7 @@ services: healthcheck: test: - CMD-SHELL - - grep -q ' /workspace/workspaces .* - fuse.rclone ' /proc/self/mountinfo && curl -fsS http://127.0.0.1:8000/api/v1/health >/dev/null + - grep -q ' /app/workspaces .* - fuse.rclone ' /proc/self/mountinfo && curl -fsS http://127.0.0.1:8000/api/v1/health >/dev/null interval: 10s timeout: 5s retries: 18 diff --git a/runtime/src/runtime/mount.py b/runtime/src/runtime/mount.py index 0cc1e12..97cfbff 100644 --- a/runtime/src/runtime/mount.py +++ b/runtime/src/runtime/mount.py @@ -60,7 +60,7 @@ def start_rclone_mount() -> None: "rclone", "mount", REMOTE_BUCKET, - str(WORKSPACES_ROOT), + WORKSPACES_ROOT.as_posix(), "--allow-other", "--vfs-cache-mode", "full", "--vfs-cache-max-size", "20G", diff --git a/runtime/src/runtime/process.py b/runtime/src/runtime/process.py index 04198e4..3337c3d 100644 --- a/runtime/src/runtime/process.py +++ b/runtime/src/runtime/process.py @@ -24,7 +24,6 @@ from common.utils import get_free_port, start_process from runtime.mount import WORKSPACES_ROOT PUBLIC_BASE_URL = settings.public_base_url -JUPYTER_PROCESS_CWD = Path("/app") class JupyterProcessRecord(TypedDict): @@ -108,7 +107,7 @@ async def start_workspace(ws_id: str) -> dict: "--allow-root", f"--ServerApp.token={token}", f"--ServerApp.base_url={base_path}", - f"--ServerApp.root_dir={workspace_path}", + "--notebook-dir=.", "--ServerApp.terminals_enabled=False", "--NotebookApp.terminals_enabled=False", "--ServerApp.allow_origin=*", @@ -118,11 +117,7 @@ async def start_workspace(ws_id: str) -> dict: ] try: - # Keep the server process cwd off the rclone/FUSE mount. Remote - # S3 directory refreshes can replace a virtual directory inode, - # leaving a long-lived cwd marked "(deleted)" and making - # os.getcwd() fail while Jupyter starts kernels or nbconvert. - process, log_file = start_process(cmd, JUPYTER_PROCESS_CWD) + process, log_file = start_process(cmd, workspace_path) except Exception as e: logger.error(f"Failed to start Jupyter for {ws_id}: {e}") raise HTTPException(