diff --git a/CLAUDE.md b/CLAUDE.md index 6ed73a3..11c1ffc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,6 +41,27 @@ links out to each service. Per-service detail (variables, ports, storage) belongs in that service's README, not the root one. Adding a service means adding its README and a row to the root table. +## Workspace services + +A service someone works *inside* — an editor, a coding agent, anything with a +shell — gets exactly two named volumes: one for the container user's home +directory, one mounted at `/workspace`. The home volume holds settings, +credentials and CLI logins; `/workspace` holds the code. Point whatever +variable selects the working directory at `/workspace`. + +`code-server`, `paseo` and `opencode-web` all follow this. +A service with no human inside it does not: `openhands` keeps only its state +volume, because each agent session gets a container of its own. + +The split is so that wiping one does not take the other. Reinstalling an editor +should not cost you a repository, and deleting a repository should not cost you +your extensions and logins. + +Check who owns `/workspace` on a fresh volume. Docker creates it `root:root` +unless the image ships the directory, and an image that drops to a non-root +user will not be able to write there. `code-server` needs an explicit `chown` +for this reason; `paseo` and `opencode-web` do not. + ## Environment variable order `environment:` entries are ordered by how badly the service needs them — not diff --git a/code-server/Dockerfile b/code-server/Dockerfile new file mode 100644 index 0000000..74aa411 --- /dev/null +++ b/code-server/Dockerfile @@ -0,0 +1,4 @@ +FROM lscr.io/linuxserver/code-server:latest + +# Workspace directory, owned by the container user. +RUN mkdir -p /workspace && chown 1000:1000 /workspace diff --git a/code-server/README.md b/code-server/README.md index a3afefc..54493fd 100644 --- a/code-server/README.md +++ b/code-server/README.md @@ -57,6 +57,36 @@ Coolify or Dokploy. See the [root README](../README.md) for why. ## Storage -Everything lives in the `code-server-config` named volume mounted at `/config`; -the default workspace is `/config/workspace`. Removing the volume wipes your -files, settings, and extensions. +| Volume | Mount | Holds | +| --- | --- | --- | +| `code-server-config` | `/config` | Home directory: settings, extensions, shell history, CLI logins | +| `code-server-workspace` | `/workspace` | Code you work on | + +Two volumes, the same split [paseo](../paseo/README.md) and +[opencode-web](../opencode-web/README.md) use: home in one, the workspace in +the other. Code survives a wipe of the editor's state, and the editor's state +survives a wipe of the code. + +`DEFAULT_WORKSPACE` points at `/workspace` to match. It only chooses the folder +code-server opens; it does not move anything. + +The `Dockerfile` exists only because of that move. The image hard-codes what it +hands to the `abc` user — `init-adduser` takes `/app`, `/config` and +`/defaults`, `init-code-server` takes `/config/workspace` by literal path — and +reads `DEFAULT_WORKSPACE` only to decide which folder to open. A named volume +on `/workspace` is therefore never chowned, comes up `root:root`, and the +editor cannot write a single file into it. + +Creating the directory in the image fixes it without any runtime step: Docker +seeds an empty named volume from the image directory, ownership included, so +`/workspace` arrives owned by `abc`. It is the same reason `paseo` needs no +fixup — its upstream image ships `/workspace` already owned. + +The alternative was a `chown` script in `/custom-cont-init.d`, the image's own +init hook. It was rejected because it needs a bind mount from the repository +into the container, and because the hook silently skips any script that has +lost its executable bit — a read-only workspace with nothing obvious to blame. +Baking `1000:1000` into the image costs the ability to change `PUID` at +runtime, which is free here: both services pin it to `1000`. + +Anything outside these two volumes is lost on redeploy. diff --git a/code-server/compose.yml b/code-server/compose.yml index 544f835..0ea781a 100644 --- a/code-server/compose.yml +++ b/code-server/compose.yml @@ -1,6 +1,6 @@ services: code-server: - image: 'lscr.io/linuxserver/code-server:latest' + build: . hostname: ${SERVICE_HOSTNAME} environment: - PUID=1000 @@ -11,7 +11,7 @@ services: - INSTALL_PACKAGES=gh|git|glab|unzip|zip - NODEJS_MOD_VERSION=24 - TZ=Asia/Ho_Chi_Minh - - DEFAULT_WORKSPACE=/config/workspace + - DEFAULT_WORKSPACE=/workspace - GIT_AUTHOR_NAME=${GIT_NAME} - GIT_AUTHOR_EMAIL=${GIT_EMAIL} - GIT_COMMITTER_NAME=${GIT_NAME} @@ -20,6 +20,8 @@ services: - PWA_APPNAME=code-server volumes: - 'code-server-config:/config' + - 'code-server-workspace:/workspace' - '/var/run/docker.sock:/var/run/docker.sock:ro' volumes: code-server-config: + code-server-workspace: