From 612fcb870521c726374b1b902afd07a5826c11fe Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Tue, 28 Apr 2026 02:08:06 +0000 Subject: [PATCH 1/3] chore: small repo cleanups - .dockerignore: drop misleading Dockerfile entry (BuildKit handles it), add editor/OS noise patterns and explicit .env exclusion - Dockerfile: bump NODE_VERSION 22.11.0 -> 22.20.0 (current Node 22 LTS) - README: align build-arg example with the new default Node version - opencode-user-config.json: lowercase the 'sentry' MCP key for consistency with 'context7' and 'github' --- .dockerignore | 22 ++++++++++++++++++---- Dockerfile | 2 +- README.md | 2 +- opencode-user-config.json | 2 +- 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.dockerignore b/.dockerignore index ba1598a..04b61ed 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,10 +1,24 @@ +# VCS .git .gitignore -.dockerignore -Dockerfile + +# Docs / meta (not needed in the image) README.md -node_modules -**/.DS_Store +LICENSE + +# Local-only env files (never bake into image) +.env **/.env **/.env.* + +# Editor / OS noise +.idea +.vscode +*.swp +*.swo +**/.DS_Store +Thumbs.db + +# Build artifacts / logs +node_modules *.log diff --git a/Dockerfile b/Dockerfile index 06d1b30..5a1a81a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,7 +55,7 @@ RUN install -d -m 0755 /out/etc/apt/keyrings \ FROM debian:bookworm-slim AS runtime ARG NVM_VERSION=v0.40.3 -ARG NODE_VERSION=22.11.0 +ARG NODE_VERSION=22.20.0 ARG USER_UID=1000 ARG USER_GID=1000 diff --git a/README.md b/README.md index bc731e3..bc8068e 100644 --- a/README.md +++ b/README.md @@ -46,5 +46,5 @@ Open . ## Notes -- Override Node at build time: `docker build --build-arg NODE_VERSION=20.18.0 -t my-opencode .` +- Override Node at build time: `docker build --build-arg NODE_VERSION=22.20.0 -t my-opencode .` - Python isn't installed. If an npm package needs `node-gyp`, install on the fly inside an OpenCode bash session: `sudo apt-get install -y python3`. diff --git a/opencode-user-config.json b/opencode-user-config.json index 3af3705..9beeba3 100644 --- a/opencode-user-config.json +++ b/opencode-user-config.json @@ -15,7 +15,7 @@ "Authorization": "Bearer {env:GITHUB_MCP_TOKEN}" } }, - "Sentry": { + "sentry": { "type": "remote", "url": "https://mcp.sentry.dev/mcp", "headers": {} From 0d28431ccc4101f2e4b7cfd2bdac69c2bbbb5b81 Mon Sep 17 00:00:00 2001 From: Aditya Mathur Date: Tue, 28 Apr 2026 02:11:34 +0000 Subject: [PATCH 2/3] feat: start OpenCode in ~/dev instead of /workspace Move the default working directory from /workspace to ~/dev so the shell prompt and OpenCode worktree feel like a normal dev environment. The Railway Volume still mounts at /workspace and OpenCode session/auth data still persists via the ~/.local/share/opencode -> /workspace/.opencode symlink, but files written under ~/dev are intentionally ephemeral. - Dockerfile: create /home/developer/dev, set WORKDIR to it, comment the ephemerality caveat. - docker-entrypoint.sh: git init ~/dev (so OpenCode anchors its worktree there) and cd into it; keep the /workspace ownership fix and /workspace/.opencode mkdir untouched. - README: document the new start dir and the persistence trade-off. --- Dockerfile | 7 ++++++- README.md | 4 ++-- docker-entrypoint.sh | 24 ++++++++++++++---------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5a1a81a..52305d7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -111,6 +111,7 @@ RUN groupadd --gid ${USER_GID} developer \ && chmod 0440 /etc/sudoers.d/developer \ && install -d -m 0755 -o developer -g developer \ /workspace \ + /home/developer/dev \ /home/developer/.opencode \ /home/developer/.opencode/bin \ /home/developer/.local \ @@ -154,7 +155,11 @@ COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh # No VOLUME directive — Railway rejects them. Attach a Railway Volume at # /workspace via the dashboard for persistence. EXPOSE 4096 -WORKDIR /workspace +# Start OpenCode in ~/dev. Note: this directory is NOT persisted by the +# Railway Volume (which is mounted at /workspace) — anything written here +# is lost on redeploy. Session/auth data still persists via the +# /workspace/.opencode symlink. +WORKDIR /home/developer/dev # PORT lets PaaS platforms (Railway/Fly/Render) assign a port; falls back # to 4096 locally. diff --git a/README.md b/README.md index bc8068e..2476745 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ Self-hosted [OpenCode](https://opencode.ai) web UI in a Docker image, ready to d - [Context7](https://context7.com) → `CONTEXT7_API_KEY` - [GitHub MCP](https://github.com/github/github-mcp-server) → `GITHUB_MCP_TOKEN` - [Sentry MCP](https://mcp.sentry.dev) → no env var -- Non-root `developer` user. Mount a single persistent volume at `/workspace` to keep your projects **and** OpenCode session/auth data across redeploys — `~/.local/share/opencode` is symlinked into `/workspace/.opencode`. +- Non-root `developer` user. OpenCode starts in `~/dev` (ephemeral) for a familiar working dir; mount a persistent volume at `/workspace` to keep OpenCode session/auth data across redeploys — `~/.local/share/opencode` is symlinked into `/workspace/.opencode`. **Files in `~/dev` are not persisted** — clone projects under `/workspace` if you need them to survive redeploys. ## Deploy on Railway 1. Push this repo to GitHub. 2. Railway: **New Project → Deploy from GitHub repo**. 3. **Variables** tab: set `OPENCODE_SERVER_PASSWORD` and at least one LLM provider key. -4. (Optional) Add a **Volume** mounted at `/workspace` so projects and OpenCode session history survive redeploys (sessions live at `/workspace/.opencode` via a symlink — one volume covers both). +4. (Optional) Add a **Volume** mounted at `/workspace` so OpenCode session history survives redeploys (sessions live at `/workspace/.opencode` via a symlink). Note that the default start dir `~/dev` is **ephemeral** — clone any projects you want to keep under `/workspace`. 5. **Settings → Networking → Generate Domain**, open it, sign in as `opencode` with the password from step 3. ## Environment variables diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 887e723..0e69e31 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -21,17 +21,21 @@ mkdir -p /workspace/.opencode # OpenCode picks the "worktree" (project root) by walking up from cwd # looking for a .git directory. With no .git ancestor it falls back to / # and refuses to write there ("the default working directory (/) doesn't -# allow writing"). Init /workspace as a git repo so opencode anchors on -# /workspace as the worktree. Skipped if a previous deploy already set -# this up. -if [ ! -d /workspace/.git ]; then - git init -q /workspace - git -C /workspace config user.email "developer@my-opencode.local" - git -C /workspace config user.name "Developer" +# allow writing"). Init ~/dev as a git repo so opencode anchors there as +# the worktree. Skipped if a previous deploy already set this up. +# +# NOTE: ~/dev is on the image's home filesystem, NOT the Railway Volume, +# so anything written here is ephemeral. The volume stays at /workspace. +DEV_DIR="${HOME:-/home/developer}/dev" +mkdir -p "$DEV_DIR" +if [ ! -d "$DEV_DIR/.git" ]; then + git init -q "$DEV_DIR" + git -C "$DEV_DIR" config user.email "developer@my-opencode.local" + git -C "$DEV_DIR" config user.name "Developer" fi -# Pin cwd to /workspace too — Railway can start the container from / -# regardless of the Dockerfile's WORKDIR. -cd /workspace +# Pin cwd to ~/dev — Railway can start the container from / regardless +# of the Dockerfile's WORKDIR. +cd "$DEV_DIR" exec "$@" From 6ffd9ab61ff481932d5f33421291025b2b180d4f Mon Sep 17 00:00:00 2001 From: mathuraditya724 Date: Mon, 27 Apr 2026 19:24:57 -0700 Subject: [PATCH 3/3] Move persistence volume from /workspace to ~/dev OpenCode now starts in ~/dev (this PR's earlier change), so the natural place to attach a Railway Volume is the same directory the agent actually works in. This drops the /workspace indirection entirely and collapses everything onto a single mount path. Changes: - Dockerfile: drop /workspace from `install -d`. Retarget the ~/.local/share/opencode symlink to /home/developer/dev/.opencode. Update the WORKDIR/EXPOSE comment block to describe the new single-volume model. - docker-entrypoint.sh: chown / mkdir / git-init now all act on $DEV_DIR (~/dev) instead of /workspace. The chown still claims a fresh root-owned volume on first boot via passwordless sudo. - README: "What's inside" and the Railway deploy step both updated to point at /home/developer/dev as the single Volume mount path. No more "ephemeral cwd vs persistent /workspace" trade-off; clones in ~/dev now persist by default if a Volume is attached. Verified locally with a forced root-owned named volume mounted at /home/developer/dev: - entrypoint chowns to 1000:1000 - /path API reports worktree=/home/developer/dev, directory=/home/developer/dev - /project/current reports vcs: git - /global/health returns 200 over basic auth - ~/.local/share/opencode -> /home/developer/dev/.opencode symlink intact --- Dockerfile | 16 ++++++---------- README.md | 4 ++-- docker-entrypoint.sh | 25 +++++++++++-------------- 3 files changed, 19 insertions(+), 26 deletions(-) diff --git a/Dockerfile b/Dockerfile index 52305d7..0e3fbc0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -110,7 +110,6 @@ RUN groupadd --gid ${USER_GID} developer \ && echo 'developer ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/developer \ && chmod 0440 /etc/sudoers.d/developer \ && install -d -m 0755 -o developer -g developer \ - /workspace \ /home/developer/dev \ /home/developer/.opencode \ /home/developer/.opencode/bin \ @@ -118,7 +117,7 @@ RUN groupadd --gid ${USER_GID} developer \ /home/developer/.local/share \ /home/developer/.config \ /home/developer/.config/opencode \ - && ln -s /workspace/.opencode /home/developer/.local/share/opencode \ + && ln -s /home/developer/dev/.opencode /home/developer/.local/share/opencode \ && chown -h developer:developer /home/developer/.local/share/opencode COPY --from=downloader --chown=developer:developer \ @@ -147,18 +146,15 @@ COPY --chown=developer:developer \ opencode-user-config.json \ /home/developer/.config/opencode/opencode.json -# Tiny entrypoint that mkdir's /workspace/.opencode at runtime so a single -# Railway Volume mounted at /workspace persists both projects and OpenCode -# session/auth data (~/.local/share/opencode is symlinked into it). +# Tiny entrypoint that mkdir's ~/dev/.opencode at runtime so a single +# Railway Volume mounted at ~/dev persists projects + OpenCode session/auth +# data together (~/.local/share/opencode is symlinked into it). COPY --chmod=0755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh # No VOLUME directive — Railway rejects them. Attach a Railway Volume at -# /workspace via the dashboard for persistence. +# /home/developer/dev (~/dev) via the dashboard for persistence; both +# projects you clone there and OpenCode session/auth data live in it. EXPOSE 4096 -# Start OpenCode in ~/dev. Note: this directory is NOT persisted by the -# Railway Volume (which is mounted at /workspace) — anything written here -# is lost on redeploy. Session/auth data still persists via the -# /workspace/.opencode symlink. WORKDIR /home/developer/dev # PORT lets PaaS platforms (Railway/Fly/Render) assign a port; falls back diff --git a/README.md b/README.md index 2476745..d6af7ee 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ Self-hosted [OpenCode](https://opencode.ai) web UI in a Docker image, ready to d - [Context7](https://context7.com) → `CONTEXT7_API_KEY` - [GitHub MCP](https://github.com/github/github-mcp-server) → `GITHUB_MCP_TOKEN` - [Sentry MCP](https://mcp.sentry.dev) → no env var -- Non-root `developer` user. OpenCode starts in `~/dev` (ephemeral) for a familiar working dir; mount a persistent volume at `/workspace` to keep OpenCode session/auth data across redeploys — `~/.local/share/opencode` is symlinked into `/workspace/.opencode`. **Files in `~/dev` are not persisted** — clone projects under `/workspace` if you need them to survive redeploys. +- Non-root `developer` user. OpenCode starts in `~/dev`. Mount a single persistent volume at `~/dev` (= `/home/developer/dev`) to keep your projects **and** OpenCode session/auth data across redeploys — `~/.local/share/opencode` is symlinked into `~/dev/.opencode`. ## Deploy on Railway 1. Push this repo to GitHub. 2. Railway: **New Project → Deploy from GitHub repo**. 3. **Variables** tab: set `OPENCODE_SERVER_PASSWORD` and at least one LLM provider key. -4. (Optional) Add a **Volume** mounted at `/workspace` so OpenCode session history survives redeploys (sessions live at `/workspace/.opencode` via a symlink). Note that the default start dir `~/dev` is **ephemeral** — clone any projects you want to keep under `/workspace`. +4. (Optional) Add a **Volume** mounted at `/home/developer/dev` so projects you clone and OpenCode session history both survive redeploys (sessions live at `~/dev/.opencode` via a symlink, so one volume covers both). 5. **Settings → Networking → Generate Domain**, open it, sign in as `opencode` with the password from step 3. ## Environment variables diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 0e69e31..784676b 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -1,33 +1,30 @@ #!/bin/sh set -e -# A fresh Railway Volume mounted at /workspace lands owned by root, so -# fix it up to belong to the running user before we try to write to it. +DEV_DIR="${HOME:-/home/developer}/dev" + +# A fresh Railway Volume mounted at ~/dev lands owned by root, so fix it +# up to belong to the running user before we try to write to it. # Passwordless sudo is configured for the developer user specifically # so this single chown can succeed without further setup. -if [ ! -w /workspace ]; then - sudo chown "$(id -u):$(id -g)" /workspace || { - echo "ERROR: /workspace is not writable and chown failed." >&2 +if [ ! -w "$DEV_DIR" ]; then + sudo chown "$(id -u):$(id -g)" "$DEV_DIR" || { + echo "ERROR: $DEV_DIR is not writable and chown failed." >&2 echo "Check the volume's mount permissions in your platform's dashboard." >&2 exit 1 } fi -# Ensure OpenCode's session/auth dir exists in the workspace. The image -# symlinks ~/.local/share/opencode -> /workspace/.opencode so a single -# Railway Volume on /workspace persists projects + session history. -mkdir -p /workspace/.opencode +# Ensure OpenCode's session/auth dir exists in the dev volume. The image +# symlinks ~/.local/share/opencode -> ~/dev/.opencode so a single Railway +# Volume mounted at ~/dev persists projects + session history together. +mkdir -p "$DEV_DIR/.opencode" # OpenCode picks the "worktree" (project root) by walking up from cwd # looking for a .git directory. With no .git ancestor it falls back to / # and refuses to write there ("the default working directory (/) doesn't # allow writing"). Init ~/dev as a git repo so opencode anchors there as # the worktree. Skipped if a previous deploy already set this up. -# -# NOTE: ~/dev is on the image's home filesystem, NOT the Railway Volume, -# so anything written here is ephemeral. The volume stays at /workspace. -DEV_DIR="${HOME:-/home/developer}/dev" -mkdir -p "$DEV_DIR" if [ ! -d "$DEV_DIR/.git" ]; then git init -q "$DEV_DIR" git -C "$DEV_DIR" config user.email "developer@my-opencode.local"