From 2faa08d7ff92557dc0b37793dac8986717baa71a Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Thu, 14 May 2026 12:29:50 -0400 Subject: [PATCH 01/27] Attempting to run vllm locally instide of a vwb app. --- src/vllm/.devcontainer.json | 34 ++++++++++++++++++++++++ src/vllm/Dockerfile | 26 ++++++++++++++++++ src/vllm/devcontainer-template.json | 30 +++++++++++++++++++++ src/vllm/docker-compose.yaml | 41 +++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 src/vllm/.devcontainer.json create mode 100644 src/vllm/Dockerfile create mode 100644 src/vllm/devcontainer-template.json create mode 100644 src/vllm/docker-compose.yaml diff --git a/src/vllm/.devcontainer.json b/src/vllm/.devcontainer.json new file mode 100644 index 000000000..e8ec1e24b --- /dev/null +++ b/src/vllm/.devcontainer.json @@ -0,0 +1,34 @@ +{ + "name": "vLLM Llama 3", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "shutdownAction": "none", + "workspaceFolder": "/workspace", + "postCreateCommand": [ + "./startupscript/post-startup.sh", + "vllm", + "/home/vllm", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "postStartCommand": [ + "./startupscript/remount-on-restart.sh", + "vllm", + "/home/vllm", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "17" + }, + "ghcr.io/devcontainers/features/aws-cli:1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1": {}, + "./.devcontainer/features/workbench-tools": { + "cloud": "${templateOption:cloud}", + "username": "vllm", + "userHomeDir": "/home/vllm" + } + }, + "remoteUser": "root" +} \ No newline at end of file diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile new file mode 100644 index 000000000..d43acb669 --- /dev/null +++ b/src/vllm/Dockerfile @@ -0,0 +1,26 @@ +FROM vllm/vllm-openai:latest AS base + +ARG NB_USER=vllm +ARG NB_UID=1010 +ARG NB_GID=1000 +ARG WORKDIR=/home/${NB_USER} + +USER root + +RUN apt-get update \ + && apt-get install -yq sudo curl \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd --gid ${NB_GID} ${NB_USER} || true \ + && useradd --uid ${NB_UID} --gid ${NB_GID} --create-home --home-dir ${WORKDIR} --shell /bin/bash ${NB_USER} \ + && mkdir -p /workspace /models \ + && chown -R ${NB_UID}:${NB_GID} ${WORKDIR} /workspace /models /tmp + +# Grant passwordless sudo +RUN echo "${NB_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${NB_USER} \ + && chmod 0440 /etc/sudoers.d/${NB_USER} + +ENV HOME=${WORKDIR} +WORKDIR ${WORKDIR} + +USER ${NB_USER} diff --git a/src/vllm/devcontainer-template.json b/src/vllm/devcontainer-template.json new file mode 100644 index 000000000..3d8982ea7 --- /dev/null +++ b/src/vllm/devcontainer-template.json @@ -0,0 +1,30 @@ +{ + "id": "vllm", + "version": "0.0.1", + "name": "vLLM", + "description": "vLLM inference server with Llama 3 on GPU", + "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/vllm", + "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", + "options": { + "cloud": { + "type": "string", + "description": "VM cloud environment", + "proposals": [ + "gcp" + ], + "default": "gcp" + }, + "login": { + "type": "string", + "description": "Whether to log in to workbench CLI", + "proposals": [ + "true", + "false" + ], + "default": "false" + } + }, + "platforms": [ + "Any" + ] +} \ No newline at end of file diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml new file mode 100644 index 000000000..ebb55ea64 --- /dev/null +++ b/src/vllm/docker-compose.yaml @@ -0,0 +1,41 @@ +services: + app: + container_name: "application-server" + build: + context: . + dockerfile: Dockerfile + user: vllm + restart: always + volumes: + - .:/workspace:cached + - models:/models + ports: + - "8000:8000" + environment: + - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-} + networks: + - app-network + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse + security_opt: + - apparmor:unconfined + # Serve Llama 3 8B — adjust model/quantization as needed + command: > + python -m vllm.entrypoints.openai.api_server --model meta-llama/Meta-Llama-3-8B-Instruct --host 0.0.0.0 --port 8000 --max-model-len 4096 --dtype half --gpu-memory-utilization 0.90 + # GPU runtime is auto-injected by Workbench when nvidia-smi is detected + # (see startupscript/butane/050-parse-devcontainer.sh + nvidia-runtime.yaml) + # For local testing, uncomment: + # deploy: + # resources: + # reservations: + # devices: + # - driver: nvidia + # count: all + # capabilities: [gpu] +networks: + app-network: + external: true +volumes: + models: From a7b6589c364ce928ac75a11889ed319d02095823 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Thu, 14 May 2026 15:18:44 -0400 Subject: [PATCH 02/27] Updated to multi stage docker --- src/vllm/.devcontainer.json | 38 +++++++++++++++++++------ src/vllm/Dockerfile | 32 ++++++++------------- src/vllm/devcontainer-template.json | 4 +-- src/vllm/docker-compose.yaml | 43 ++++++++++++++++++----------- 4 files changed, 70 insertions(+), 47 deletions(-) diff --git a/src/vllm/.devcontainer.json b/src/vllm/.devcontainer.json index e8ec1e24b..5aaa80013 100644 --- a/src/vllm/.devcontainer.json +++ b/src/vllm/.devcontainer.json @@ -1,20 +1,21 @@ { - "name": "vLLM Llama 3", + "name": "vLLM Llama 3.2", "dockerComposeFile": "docker-compose.yaml", "service": "app", + "runServices": ["app", "vllm"], "shutdownAction": "none", "workspaceFolder": "/workspace", "postCreateCommand": [ "./startupscript/post-startup.sh", - "vllm", - "/home/vllm", + "abc", + "/config", "${templateOption:cloud}", "${templateOption:login}" ], "postStartCommand": [ "./startupscript/remount-on-restart.sh", - "vllm", - "/home/vllm", + "abc", + "/config", "${templateOption:cloud}", "${templateOption:login}" ], @@ -26,9 +27,28 @@ "ghcr.io/dhoeric/features/google-cloud-cli:1": {}, "./.devcontainer/features/workbench-tools": { "cloud": "${templateOption:cloud}", - "username": "vllm", - "userHomeDir": "/home/vllm" + "username": "abc", + "userHomeDir": "/config" } }, - "remoteUser": "root" -} \ No newline at end of file + "remoteUser": "root", + "customizations": { + "workbench": { + "opens": { + "extensions": [ + ".py", + ".ipynb", + ".sh", + ".md", + ".html", + ".csv", + ".json", + ".jsonc", + ".yaml", + ".yml" + ], + "fileUrlSuffix": "?payload=[[\"openFile\",\"vscode-remote:///config/{path}\"]]" + } + } + } +} diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index d43acb669..981b73ed6 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -1,26 +1,18 @@ -FROM vllm/vllm-openai:latest AS base - -ARG NB_USER=vllm -ARG NB_UID=1010 -ARG NB_GID=1000 -ARG WORKDIR=/home/${NB_USER} - -USER root +FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 AS code-server RUN apt-get update \ - && apt-get install -yq sudo curl \ - && rm -rf /var/lib/apt/lists/* + && apt-get install -y --no-install-recommends jq python3 python3-pip \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /config/extensions \ + && chown abc:abc /config /config/extensions -RUN groupadd --gid ${NB_GID} ${NB_USER} || true \ - && useradd --uid ${NB_UID} --gid ${NB_GID} --create-home --home-dir ${WORKDIR} --shell /bin/bash ${NB_USER} \ - && mkdir -p /workspace /models \ - && chown -R ${NB_UID}:${NB_GID} ${WORKDIR} /workspace /models /tmp +USER abc +ENV HOME=/config -# Grant passwordless sudo -RUN echo "${NB_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/${NB_USER} \ - && chmod 0440 /etc/sudoers.d/${NB_USER} +RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python + +USER root +WORKDIR /config -ENV HOME=${WORKDIR} -WORKDIR ${WORKDIR} -USER ${NB_USER} +FROM vllm/vllm-openai:latest AS vllm diff --git a/src/vllm/devcontainer-template.json b/src/vllm/devcontainer-template.json index 3d8982ea7..871d8e379 100644 --- a/src/vllm/devcontainer-template.json +++ b/src/vllm/devcontainer-template.json @@ -2,7 +2,7 @@ "id": "vllm", "version": "0.0.1", "name": "vLLM", - "description": "vLLM inference server with Llama 3 on GPU", + "description": "Code-server IDE with vLLM inference server (Llama 3.2 3B). Supports CPU and GPU.", "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/vllm", "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", "options": { @@ -27,4 +27,4 @@ "platforms": [ "Any" ] -} \ No newline at end of file +} diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml index ebb55ea64..a3ae8236c 100644 --- a/src/vllm/docker-compose.yaml +++ b/src/vllm/docker-compose.yaml @@ -4,15 +4,16 @@ services: build: context: . dockerfile: Dockerfile - user: vllm + target: code-server restart: always volumes: - .:/workspace:cached - - models:/models + - work:/config:cached ports: - - "8000:8000" + - "8443:8443" environment: - - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-} + USER: "abc" + DEFAULT_WORKSPACE: "/config" networks: - app-network cap_add: @@ -21,21 +22,31 @@ services: - /dev/fuse security_opt: - apparmor:unconfined - # Serve Llama 3 8B — adjust model/quantization as needed + + vllm: + build: + context: . + dockerfile: Dockerfile + target: vllm + restart: always + volumes: + - models:/root/.cache/huggingface + environment: + - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-} + networks: + - app-network command: > - python -m vllm.entrypoints.openai.api_server --model meta-llama/Meta-Llama-3-8B-Instruct --host 0.0.0.0 --port 8000 --max-model-len 4096 --dtype half --gpu-memory-utilization 0.90 - # GPU runtime is auto-injected by Workbench when nvidia-smi is detected - # (see startupscript/butane/050-parse-devcontainer.sh + nvidia-runtime.yaml) - # For local testing, uncomment: - # deploy: - # resources: - # reservations: - # devices: - # - driver: nvidia - # count: all - # capabilities: [gpu] + python -m vllm.entrypoints.openai.api_server + --model meta-llama/Llama-3.2-3B-Instruct + --host 0.0.0.0 + --port 8000 + --max-model-len 2048 + --device cpu + --dtype bfloat16 + networks: app-network: external: true volumes: + work: models: From da032fcf827fefc01c33aba5ab95d565a8953783 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Thu, 14 May 2026 17:16:47 -0400 Subject: [PATCH 03/27] updated --- src/vllm/.devcontainer.json | 10 ++-------- src/vllm/Dockerfile | 9 ++++----- src/vllm/docker-compose.yaml | 25 ++----------------------- src/vllm/start-vllm.sh | 32 ++++++++++++++++++++++++++++++++ src/vllm/sudo-passwordless.sh | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 36 deletions(-) create mode 100755 src/vllm/start-vllm.sh create mode 100755 src/vllm/sudo-passwordless.sh diff --git a/src/vllm/.devcontainer.json b/src/vllm/.devcontainer.json index 5aaa80013..440ef073c 100644 --- a/src/vllm/.devcontainer.json +++ b/src/vllm/.devcontainer.json @@ -2,16 +2,10 @@ "name": "vLLM Llama 3.2", "dockerComposeFile": "docker-compose.yaml", "service": "app", - "runServices": ["app", "vllm"], "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": [ - "./startupscript/post-startup.sh", - "abc", - "/config", - "${templateOption:cloud}", - "${templateOption:login}" - ], + "postCreateCommand": + "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc; ./start-vllm.sh", "postStartCommand": [ "./startupscript/remount-on-restart.sh", "abc", diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index 981b73ed6..e6d36e3a9 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -1,11 +1,13 @@ -FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 AS code-server +FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 RUN apt-get update \ - && apt-get install -y --no-install-recommends jq python3 python3-pip \ + && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /config/extensions \ && chown abc:abc /config /config/extensions +RUN pip install --break-system-packages vllm openai + USER abc ENV HOME=/config @@ -13,6 +15,3 @@ RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --insta USER root WORKDIR /config - - -FROM vllm/vllm-openai:latest AS vllm diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml index a3ae8236c..0856faf59 100644 --- a/src/vllm/docker-compose.yaml +++ b/src/vllm/docker-compose.yaml @@ -4,7 +4,6 @@ services: build: context: . dockerfile: Dockerfile - target: code-server restart: always volumes: - .:/workspace:cached @@ -14,6 +13,8 @@ services: environment: USER: "abc" DEFAULT_WORKSPACE: "/config" + SUDO_PASSWORD: "pwd" + HUGGING_FACE_HUB_TOKEN: "${HF_TOKEN:-}" networks: - app-network cap_add: @@ -23,30 +24,8 @@ services: security_opt: - apparmor:unconfined - vllm: - build: - context: . - dockerfile: Dockerfile - target: vllm - restart: always - volumes: - - models:/root/.cache/huggingface - environment: - - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN:-} - networks: - - app-network - command: > - python -m vllm.entrypoints.openai.api_server - --model meta-llama/Llama-3.2-3B-Instruct - --host 0.0.0.0 - --port 8000 - --max-model-len 2048 - --device cpu - --dtype bfloat16 - networks: app-network: external: true volumes: work: - models: diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh new file mode 100755 index 000000000..ddf02bcb2 --- /dev/null +++ b/src/vllm/start-vllm.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +VLLM_LOG="/config/vllm-server.log" + +echo "Starting vLLM server on port 8000..." +if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then + echo "GPU detected — using GPU backend" + nohup python -m vllm.entrypoints.openai.api_server \ + --model meta-llama/Llama-3.2-3B-Instruct \ + --host 0.0.0.0 \ + --port 8000 \ + --max-model-len 4096 \ + --dtype half \ + --gpu-memory-utilization 0.90 \ + > "${VLLM_LOG}" 2>&1 & +else + echo "No GPU detected — using CPU backend (this will be slow)" + nohup python -m vllm.entrypoints.openai.api_server \ + --model meta-llama/Llama-3.2-3B-Instruct \ + --host 0.0.0.0 \ + --port 8000 \ + --max-model-len 2048 \ + --device cpu \ + --dtype bfloat16 \ + > "${VLLM_LOG}" 2>&1 & +fi + +echo "vLLM PID: $! — logs at ${VLLM_LOG}" diff --git a/src/vllm/sudo-passwordless.sh b/src/vllm/sudo-passwordless.sh new file mode 100755 index 000000000..d14bd0a17 --- /dev/null +++ b/src/vllm/sudo-passwordless.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script is used to set up passwordless sudo for the core user on the VM. +# It requires to be run with root priviledges and USER_NAME to be set in the environment. +# It is typically called from post-startup.sh. + +USER_NAME="${1}" + +if [[ -z "${USER_NAME}" ]]; then + echo "Usage: $0 " + exit 1 +fi + +sudoers_file="/etc/sudoers" +sudoers_d_file="/etc/sudoers.d/${USER_NAME}" + +# Make sure user exists +if ! id "${USER_NAME}" &>/dev/null; then + echo "User ${USER_NAME} does not exist." + exit 1 +fi + +# Check if there's an old rule in the main sudoers file that requires a password +if grep -q "^${USER_NAME} ALL=(ALL:ALL) ALL" "${sudoers_file}"; then + echo "Found password-requiring rule for ${USER_NAME} in /etc/sudoers. Commenting it out." + + # Comment out the old rule in /etc/sudoers + sed -i "s/^${USER_NAME} ALL=(ALL:ALL) ALL/# ${USER_NAME} ALL=(ALL:ALL) ALL/" "${sudoers_file}" +fi + +echo "${USER_NAME} ALL=(ALL) NOPASSWD:ALL" > "${sudoers_d_file}" +chmod 440 "${sudoers_d_file}" + +echo "User ${USER_NAME} has been given passwordless sudo access." From 804f0352142234f8963db8ae3a85b9b7188deae2 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 09:45:01 -0400 Subject: [PATCH 04/27] updated --- src/vllm/start-vllm.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh index ddf02bcb2..3a68403fd 100755 --- a/src/vllm/start-vllm.sh +++ b/src/vllm/start-vllm.sh @@ -9,7 +9,7 @@ VLLM_LOG="/config/vllm-server.log" echo "Starting vLLM server on port 8000..." if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then echo "GPU detected — using GPU backend" - nohup python -m vllm.entrypoints.openai.api_server \ + nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-3.2-3B-Instruct \ --host 0.0.0.0 \ --port 8000 \ @@ -19,12 +19,12 @@ if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then > "${VLLM_LOG}" 2>&1 & else echo "No GPU detected — using CPU backend (this will be slow)" - nohup python -m vllm.entrypoints.openai.api_server \ + VLLM_TARGET_DEVICE=cpu \ + nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ --model meta-llama/Llama-3.2-3B-Instruct \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 2048 \ - --device cpu \ --dtype bfloat16 \ > "${VLLM_LOG}" 2>&1 & fi From fd45c7fced4e322066da2655f8869897c8cb2624 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 10:42:27 -0400 Subject: [PATCH 05/27] update --- src/vllm/.devcontainer.json | 2 +- src/vllm/Dockerfile | 21 ++++++++++++++------- src/vllm/devcontainer-template.json | 2 +- src/vllm/docker-compose.yaml | 2 +- src/vllm/start-vllm.sh | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/vllm/.devcontainer.json b/src/vllm/.devcontainer.json index 440ef073c..21f0c3f6a 100644 --- a/src/vllm/.devcontainer.json +++ b/src/vllm/.devcontainer.json @@ -1,5 +1,5 @@ { - "name": "vLLM Llama 3.2", + "name": "vLLM Gemma 4", "dockerComposeFile": "docker-compose.yaml", "service": "app", "shutdownAction": "none", diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index e6d36e3a9..e9eb51d49 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -1,17 +1,24 @@ -FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 +FROM vllm/vllm-openai:latest + +USER root RUN apt-get update \ - && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ - && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /config/extensions \ - && chown abc:abc /config /config/extensions + && apt-get install -y --no-install-recommends jq sudo curl \ + && curl -fsSL https://code-server.dev/install.sh | sh \ + && pip install openai \ + && rm -rf /var/lib/apt/lists/* + +RUN mkdir -p /config/extensions /workspace \ + && useradd -u 1000 -s /bin/bash -d /config abc || true \ + && chown -R abc:abc /config /workspace /tmp -RUN pip install --break-system-packages vllm openai +RUN echo "abc ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/abc \ + && chmod 0440 /etc/sudoers.d/abc USER abc ENV HOME=/config -RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python +RUN code-server --install-extension ms-python.python USER root WORKDIR /config diff --git a/src/vllm/devcontainer-template.json b/src/vllm/devcontainer-template.json index 871d8e379..7dadd887c 100644 --- a/src/vllm/devcontainer-template.json +++ b/src/vllm/devcontainer-template.json @@ -2,7 +2,7 @@ "id": "vllm", "version": "0.0.1", "name": "vLLM", - "description": "Code-server IDE with vLLM inference server (Llama 3.2 3B). Supports CPU and GPU.", + "description": "Code-server IDE with vLLM inference server (Gemma 4 E4B). Supports CPU and GPU.", "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/vllm", "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", "options": { diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml index 0856faf59..267dd9c81 100644 --- a/src/vllm/docker-compose.yaml +++ b/src/vllm/docker-compose.yaml @@ -14,7 +14,6 @@ services: USER: "abc" DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" - HUGGING_FACE_HUB_TOKEN: "${HF_TOKEN:-}" networks: - app-network cap_add: @@ -23,6 +22,7 @@ services: - /dev/fuse security_opt: - apparmor:unconfined + command: code-server --bind-addr 0.0.0.0:8443 --auth none --disable-telemetry /config networks: app-network: diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh index 3a68403fd..f1f891c35 100755 --- a/src/vllm/start-vllm.sh +++ b/src/vllm/start-vllm.sh @@ -10,7 +10,7 @@ echo "Starting vLLM server on port 8000..." if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then echo "GPU detected — using GPU backend" nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ - --model meta-llama/Llama-3.2-3B-Instruct \ + --model google/gemma-4-E4B-it \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 4096 \ @@ -21,7 +21,7 @@ else echo "No GPU detected — using CPU backend (this will be slow)" VLLM_TARGET_DEVICE=cpu \ nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ - --model meta-llama/Llama-3.2-3B-Instruct \ + --model google/gemma-4-E4B-it \ --host 0.0.0.0 \ --port 8000 \ --max-model-len 2048 \ From e744e4e0a55ca9c59be5f326c841998a111105e9 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 12:18:24 -0400 Subject: [PATCH 06/27] Added Readme --- src/vllm/README.md | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/vllm/README.md diff --git a/src/vllm/README.md b/src/vllm/README.md new file mode 100644 index 000000000..79a12e885 --- /dev/null +++ b/src/vllm/README.md @@ -0,0 +1,47 @@ +# vLLM + +Code-server IDE with a vLLM inference server running Gemma 4 E4B. Opens a browser-based VS Code editor on port 8443 with vLLM serving an OpenAI-compatible API on port 8000 inside the container. + +## Recommended VM Configuration + +| Model | GPU | Machine type | CPUs | RAM | Notes | +|---|---|---|---|---|---| +| Gemma 4 E2B | T4 (16 GB) | n1-standard-2 | 2 | 7.5 GB | Smallest viable setup | +| **Gemma 4 E4B** | **T4 (16 GB)** | **n1-standard-4** | **4** | **15 GB** | **Default config, best value** | +| Gemma 4 E4B | L4 (24 GB) | n1-standard-4 | 4 | 15 GB | ~2x faster inference | +| Gemma 4 26B MoE | A100 (40 GB) | n1-standard-8 | 8 | 30 GB | Requires `--tensor-parallel-size` changes | + +## Changing the Model + +Update the `--model` flag in `start-vllm.sh` and the `model=` parameter in your Python scripts. + +## Usage + +Once the app is ready, open the code-server IDE and run: + +```python +from openai import OpenAI + +client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused") + +response = client.chat.completions.create( + model="google/gemma-4-E4B-it", + messages=[{"role": "user", "content": "Hello!"}], + max_tokens=100, +) +print(response.choices[0].message.content) +``` + +## Debugging + +Check vLLM server logs: + +```bash +tail -f ~/vllm-server.log +``` + +Verify the server is running: + +```bash +curl http://localhost:8000/v1/models +``` From 6e55adb57ab45b67964ce3432e34f980932928ba Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 12:56:53 -0400 Subject: [PATCH 07/27] using vscode base --- src/vllm/Dockerfile | 21 +++++++-------------- src/vllm/docker-compose.yaml | 1 - 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index e9eb51d49..e6d36e3a9 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -1,24 +1,17 @@ -FROM vllm/vllm-openai:latest - -USER root +FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 RUN apt-get update \ - && apt-get install -y --no-install-recommends jq sudo curl \ - && curl -fsSL https://code-server.dev/install.sh | sh \ - && pip install openai \ - && rm -rf /var/lib/apt/lists/* - -RUN mkdir -p /config/extensions /workspace \ - && useradd -u 1000 -s /bin/bash -d /config abc || true \ - && chown -R abc:abc /config /workspace /tmp + && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /config/extensions \ + && chown abc:abc /config /config/extensions -RUN echo "abc ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/abc \ - && chmod 0440 /etc/sudoers.d/abc +RUN pip install --break-system-packages vllm openai USER abc ENV HOME=/config -RUN code-server --install-extension ms-python.python +RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python USER root WORKDIR /config diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml index 267dd9c81..732f4ba71 100644 --- a/src/vllm/docker-compose.yaml +++ b/src/vllm/docker-compose.yaml @@ -22,7 +22,6 @@ services: - /dev/fuse security_opt: - apparmor:unconfined - command: code-server --bind-addr 0.0.0.0:8443 --auth none --disable-telemetry /config networks: app-network: From 7225cf488b9e3910af1fc6d928695ddc6c5b1000 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 13:53:08 -0400 Subject: [PATCH 08/27] This installs PyTorch built for CUDA 12.8 first, then vLLM will use that compatible version --- src/vllm/Dockerfile | 7 ++++--- src/vllm/README.md | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index e6d36e3a9..c6e4fb7a5 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -3,10 +3,11 @@ FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f RUN apt-get update \ && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /config/extensions \ - && chown abc:abc /config /config/extensions + && mkdir -p /config/extensions /config/.cache/huggingface \ + && chown -R abc:abc /config -RUN pip install --break-system-packages vllm openai +RUN pip install --break-system-packages torch --index-url https://download.pytorch.org/whl/cu128 \ + && pip install --break-system-packages vllm openai USER abc ENV HOME=/config diff --git a/src/vllm/README.md b/src/vllm/README.md index 79a12e885..e32f4e0e2 100644 --- a/src/vllm/README.md +++ b/src/vllm/README.md @@ -32,6 +32,34 @@ response = client.chat.completions.create( print(response.choices[0].message.content) ``` +### Using `UV` + + +1. install uv + + ```sh + pip install uv + ``` + +2. initialize the project + + ```sh + uv init local-vllm-gema4-test + ``` + +3. Add open ai package to the registry + + ```sh + cd local-vllm-gema4-test/ + uv add opena + ``` + +4. Run the application + + ```sh + uv run main.py + ``` + ## Debugging Check vLLM server logs: From dd8bff192ba5f9f0e48969a9be1d1ae367e272c6 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 14:41:11 -0400 Subject: [PATCH 09/27] Update CUDA driver missmatch. This uses --extra-index-url so both vLLM and PyTorch pull their CUDA 12.8 compatible wheels together, ensuring the compiled vLLM extensions match the CUDA runtime --- src/vllm/Dockerfile | 3 +-- src/vllm/README.md | 28 +++++++++++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index c6e4fb7a5..434e2aa2c 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -6,8 +6,7 @@ RUN apt-get update \ && mkdir -p /config/extensions /config/.cache/huggingface \ && chown -R abc:abc /config -RUN pip install --break-system-packages torch --index-url https://download.pytorch.org/whl/cu128 \ - && pip install --break-system-packages vllm openai +RUN pip install --break-system-packages vllm openai --extra-index-url https://download.pytorch.org/whl/cu128 USER abc ENV HOME=/config diff --git a/src/vllm/README.md b/src/vllm/README.md index e32f4e0e2..c0d699caa 100644 --- a/src/vllm/README.md +++ b/src/vllm/README.md @@ -20,16 +20,20 @@ Update the `--model` flag in `start-vllm.sh` and the `model=` parameter in your Once the app is ready, open the code-server IDE and run: ```python -from openai import OpenAI +def main(): + from openai import OpenAI -client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused") + client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused") -response = client.chat.completions.create( - model="google/gemma-4-E4B-it", - messages=[{"role": "user", "content": "Hello!"}], - max_tokens=100, -) -print(response.choices[0].message.content) + response = client.chat.completions.create( + model="google/gemma-4-E4B-it", + messages=[{"role": "user", "content": "Hello!"}], + max_tokens=100, + ) + print(response.choices[0].message.content) + +if __name__ == "__main__": + main() ``` ### Using `UV` @@ -51,7 +55,7 @@ print(response.choices[0].message.content) ```sh cd local-vllm-gema4-test/ - uv add opena + uv add openai ``` 4. Run the application @@ -73,3 +77,9 @@ Verify the server is running: ```bash curl http://localhost:8000/v1/models ``` + +Manually running vllm model + +```bash +/usr/bin/python3 -m vllm.entrypoints.openai.api_server --model google/gemma-4-E4B-it --host 0.0.0.0 --port 8000 --max-model-len 4096 --dtype half --gpu-memory-utilization 0.90 +``` From fae282728d1da9d8f2f7a05b9ed5904a4c987d23 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 15:30:40 -0400 Subject: [PATCH 10/27] Switch from vLLM to Ollama to resolve CUDA driver compatibility issues --- src/vllm/.devcontainer.json | 3 +- src/vllm/Dockerfile | 5 ++- src/vllm/README.md | 57 +++++++++++++---------------- src/vllm/devcontainer-template.json | 2 +- src/vllm/start-vllm.sh | 31 ++++------------ 5 files changed, 39 insertions(+), 59 deletions(-) diff --git a/src/vllm/.devcontainer.json b/src/vllm/.devcontainer.json index 21f0c3f6a..a3fac8486 100644 --- a/src/vllm/.devcontainer.json +++ b/src/vllm/.devcontainer.json @@ -1,5 +1,5 @@ { - "name": "vLLM Gemma 4", + "name": "Ollama Gemma 4", "dockerComposeFile": "docker-compose.yaml", "service": "app", "shutdownAction": "none", @@ -19,6 +19,7 @@ }, "ghcr.io/devcontainers/features/aws-cli:1": {}, "ghcr.io/dhoeric/features/google-cloud-cli:1": {}, + "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, "./.devcontainer/features/workbench-tools": { "cloud": "${templateOption:cloud}", "username": "abc", diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index 434e2aa2c..da98d25aa 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -2,11 +2,12 @@ FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f RUN apt-get update \ && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ + && curl -fsSL https://ollama.com/install.sh | sh \ && rm -rf /var/lib/apt/lists/* \ - && mkdir -p /config/extensions /config/.cache/huggingface \ + && mkdir -p /config/extensions /config/.cache \ && chown -R abc:abc /config -RUN pip install --break-system-packages vllm openai --extra-index-url https://download.pytorch.org/whl/cu128 +RUN pip install --break-system-packages openai USER abc ENV HOME=/config diff --git a/src/vllm/README.md b/src/vllm/README.md index c0d699caa..66f820773 100644 --- a/src/vllm/README.md +++ b/src/vllm/README.md @@ -1,6 +1,6 @@ -# vLLM +# Ollama + Gemma 4 -Code-server IDE with a vLLM inference server running Gemma 4 E4B. Opens a browser-based VS Code editor on port 8443 with vLLM serving an OpenAI-compatible API on port 8000 inside the container. +Code-server IDE with an Ollama inference server running Gemma 4 E4B. Opens a browser-based VS Code editor on port 8443 with Ollama serving an OpenAI-compatible API on port 11434 inside the container. ## Recommended VM Configuration @@ -9,77 +9,72 @@ Code-server IDE with a vLLM inference server running Gemma 4 E4B. Opens a browse | Gemma 4 E2B | T4 (16 GB) | n1-standard-2 | 2 | 7.5 GB | Smallest viable setup | | **Gemma 4 E4B** | **T4 (16 GB)** | **n1-standard-4** | **4** | **15 GB** | **Default config, best value** | | Gemma 4 E4B | L4 (24 GB) | n1-standard-4 | 4 | 15 GB | ~2x faster inference | -| Gemma 4 26B MoE | A100 (40 GB) | n1-standard-8 | 8 | 30 GB | Requires `--tensor-parallel-size` changes | +| Gemma 4 26B MoE | A100 (40 GB) | n1-standard-8 | 8 | 30 GB | Larger model, needs more VRAM | ## Changing the Model -Update the `--model` flag in `start-vllm.sh` and the `model=` parameter in your Python scripts. +Update the `ollama pull` command in `start-vllm.sh` and the `model=` parameter in your Python scripts. See available models at https://ollama.com/library. ## Usage Once the app is ready, open the code-server IDE and run: ```python -def main(): - from openai import OpenAI +from openai import OpenAI - client = OpenAI(base_url="http://localhost:8000/v1", api_key="unused") +client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused") - response = client.chat.completions.create( - model="google/gemma-4-E4B-it", - messages=[{"role": "user", "content": "Hello!"}], - max_tokens=100, - ) - print(response.choices[0].message.content) - -if __name__ == "__main__": - main() +response = client.chat.completions.create( + model="gemma4:4b", + messages=[{"role": "user", "content": "Hello!"}], + max_tokens=100, +) +print(response.choices[0].message.content) ``` -### Using `UV` - +### Using `uv` -1. install uv +1. Install uv - ```sh + ```sh pip install uv ``` -2. initialize the project +2. Initialize the project - ```sh + ```sh uv init local-vllm-gema4-test ``` -3. Add open ai package to the registry +3. Add openai package - ```sh + ```sh cd local-vllm-gema4-test/ uv add openai ``` 4. Run the application - ```sh + ```sh uv run main.py ``` ## Debugging -Check vLLM server logs: +Check Ollama server logs: ```bash -tail -f ~/vllm-server.log +tail -f ~/ollama-server.log ``` Verify the server is running: ```bash -curl http://localhost:8000/v1/models +curl http://localhost:11434/v1/models ``` -Manually running vllm model +List downloaded models: -```bash -/usr/bin/python3 -m vllm.entrypoints.openai.api_server --model google/gemma-4-E4B-it --host 0.0.0.0 --port 8000 --max-model-len 4096 --dtype half --gpu-memory-utilization 0.90 +```bash +ollama list ``` diff --git a/src/vllm/devcontainer-template.json b/src/vllm/devcontainer-template.json index 7dadd887c..a8bf68b3a 100644 --- a/src/vllm/devcontainer-template.json +++ b/src/vllm/devcontainer-template.json @@ -2,7 +2,7 @@ "id": "vllm", "version": "0.0.1", "name": "vLLM", - "description": "Code-server IDE with vLLM inference server (Gemma 4 E4B). Supports CPU and GPU.", + "description": "Code-server IDE with Ollama inference server (Gemma 4 E4B). Supports CPU and GPU.", "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/vllm", "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", "options": { diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh index f1f891c35..d79c46f8b 100755 --- a/src/vllm/start-vllm.sh +++ b/src/vllm/start-vllm.sh @@ -4,29 +4,12 @@ set -o errexit set -o nounset set -o pipefail -VLLM_LOG="/config/vllm-server.log" +OLLAMA_LOG="/config/ollama-server.log" -echo "Starting vLLM server on port 8000..." -if command -v nvidia-smi &>/dev/null && nvidia-smi &>/dev/null; then - echo "GPU detected — using GPU backend" - nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ - --model google/gemma-4-E4B-it \ - --host 0.0.0.0 \ - --port 8000 \ - --max-model-len 4096 \ - --dtype half \ - --gpu-memory-utilization 0.90 \ - > "${VLLM_LOG}" 2>&1 & -else - echo "No GPU detected — using CPU backend (this will be slow)" - VLLM_TARGET_DEVICE=cpu \ - nohup /usr/bin/python3 -m vllm.entrypoints.openai.api_server \ - --model google/gemma-4-E4B-it \ - --host 0.0.0.0 \ - --port 8000 \ - --max-model-len 2048 \ - --dtype bfloat16 \ - > "${VLLM_LOG}" 2>&1 & -fi +nohup ollama serve > "${OLLAMA_LOG}" 2>&1 & +sleep 2 -echo "vLLM PID: $! — logs at ${VLLM_LOG}" +echo "Pulling google/gemma-4-E4B-it model (this may take a few minutes)..." +ollama pull gemma4:4b >> "${OLLAMA_LOG}" 2>&1 + +echo "Ollama ready — logs at ${OLLAMA_LOG}" From 840d1e639039739f1384951306feda2af020bf4c Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 15:53:21 -0400 Subject: [PATCH 11/27] replaced curl with sh --- src/vllm/Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index da98d25aa..017fd71a8 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -2,11 +2,13 @@ FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f RUN apt-get update \ && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ - && curl -fsSL https://ollama.com/install.sh | sh \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /config/extensions /config/.cache \ && chown -R abc:abc /config +RUN curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/local/bin/ollama \ + && chmod +x /usr/local/bin/ollama + RUN pip install --break-system-packages openai USER abc From 20674b3766a637f9e5a3d91c9cfa6855d00e07f1 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 16:54:26 -0400 Subject: [PATCH 12/27] Fix Ollama install: use tar.zst download and correct gemma4:e4b model tag --- src/vllm/Dockerfile | 6 ++++-- src/vllm/README.md | 2 +- src/vllm/start-vllm.sh | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index 017fd71a8..417a94c7e 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -6,8 +6,10 @@ RUN apt-get update \ && mkdir -p /config/extensions /config/.cache \ && chown -R abc:abc /config -RUN curl -L https://ollama.com/download/ollama-linux-amd64 -o /usr/local/bin/ollama \ - && chmod +x /usr/local/bin/ollama +RUN apt-get update && apt-get install -y --no-install-recommends zstd \ + && curl -fsSL https://ollama.com/download/ollama-linux-amd64.tar.zst | tar --zstd -x -C /usr \ + && rm -rf /var/lib/apt/lists/* \ + && ollama --version RUN pip install --break-system-packages openai diff --git a/src/vllm/README.md b/src/vllm/README.md index 66f820773..477b79cae 100644 --- a/src/vllm/README.md +++ b/src/vllm/README.md @@ -25,7 +25,7 @@ from openai import OpenAI client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused") response = client.chat.completions.create( - model="gemma4:4b", + model="gemma4:e4b", messages=[{"role": "user", "content": "Hello!"}], max_tokens=100, ) diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh index d79c46f8b..df2cf6de1 100755 --- a/src/vllm/start-vllm.sh +++ b/src/vllm/start-vllm.sh @@ -10,6 +10,6 @@ nohup ollama serve > "${OLLAMA_LOG}" 2>&1 & sleep 2 echo "Pulling google/gemma-4-E4B-it model (this may take a few minutes)..." -ollama pull gemma4:4b >> "${OLLAMA_LOG}" 2>&1 +ollama pull gemma4:e4b >> "${OLLAMA_LOG}" 2>&1 echo "Ollama ready — logs at ${OLLAMA_LOG}" From 1774245beff59078cf4f4be7e2a5b2ed98f144c4 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 17:03:52 -0400 Subject: [PATCH 13/27] removed the keep warm issue --- src/vllm/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vllm/docker-compose.yaml b/src/vllm/docker-compose.yaml index 732f4ba71..bc09abbab 100644 --- a/src/vllm/docker-compose.yaml +++ b/src/vllm/docker-compose.yaml @@ -14,6 +14,7 @@ services: USER: "abc" DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" + OLLAMA_KEEP_ALIVE: "-1" networks: - app-network cap_add: From bffdbffd7da3cdf1285546339adda2399e198123 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 15 May 2026 17:09:41 -0400 Subject: [PATCH 14/27] added dark theme to vscode --- src/vllm/Dockerfile | 4 +++- src/vllm/start-vllm.sh | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vllm/Dockerfile b/src/vllm/Dockerfile index 417a94c7e..5ada347b5 100644 --- a/src/vllm/Dockerfile +++ b/src/vllm/Dockerfile @@ -16,7 +16,9 @@ RUN pip install --break-system-packages openai USER abc ENV HOME=/config -RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python +RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python \ + && mkdir -p /config/data/User \ + && echo '{"workbench.colorTheme":"Default Dark Modern"}' > /config/data/User/settings.json USER root WORKDIR /config diff --git a/src/vllm/start-vllm.sh b/src/vllm/start-vllm.sh index df2cf6de1..d5c6f8616 100755 --- a/src/vllm/start-vllm.sh +++ b/src/vllm/start-vllm.sh @@ -9,7 +9,10 @@ OLLAMA_LOG="/config/ollama-server.log" nohup ollama serve > "${OLLAMA_LOG}" 2>&1 & sleep 2 -echo "Pulling google/gemma-4-E4B-it model (this may take a few minutes)..." +echo "Pulling gemma4:e4b model (this may take a few minutes)..." ollama pull gemma4:e4b >> "${OLLAMA_LOG}" 2>&1 -echo "Ollama ready — logs at ${OLLAMA_LOG}" +echo "Preloading model into GPU memory..." +curl -s http://localhost:11434/api/generate -d '{"model":"gemma4:e4b","prompt":"warmup","options":{"num_predict":1}}' > /dev/null 2>&1 + +echo "Ollama ready — model loaded — logs at ${OLLAMA_LOG}" From 4d7e892f980b36728061aebf0b0298b08ab503e9 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 10:50:32 -0400 Subject: [PATCH 15/27] Add ollama_gemma-4-26B-A4B app template for Gemma 4 26B MoE on A100 Based on the vllm/ollama E4B template, configured for the larger Gemma 4 26B MoE model (4B active params, 18GB) targeting A100 GPUs. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/.devcontainer.json | 49 ++++++++++++ src/ollama_gemma-4-26B-A4B/Dockerfile | 24 ++++++ src/ollama_gemma-4-26B-A4B/README.md | 78 +++++++++++++++++++ .../devcontainer-template.json | 30 +++++++ .../docker-compose.yaml | 31 ++++++++ src/ollama_gemma-4-26B-A4B/main.py | 10 +++ src/ollama_gemma-4-26B-A4B/start-ollama.sh | 18 +++++ .../sudo-passwordless.sh | 34 ++++++++ 8 files changed, 274 insertions(+) create mode 100644 src/ollama_gemma-4-26B-A4B/.devcontainer.json create mode 100644 src/ollama_gemma-4-26B-A4B/Dockerfile create mode 100644 src/ollama_gemma-4-26B-A4B/README.md create mode 100644 src/ollama_gemma-4-26B-A4B/devcontainer-template.json create mode 100644 src/ollama_gemma-4-26B-A4B/docker-compose.yaml create mode 100644 src/ollama_gemma-4-26B-A4B/main.py create mode 100755 src/ollama_gemma-4-26B-A4B/start-ollama.sh create mode 100755 src/ollama_gemma-4-26B-A4B/sudo-passwordless.sh diff --git a/src/ollama_gemma-4-26B-A4B/.devcontainer.json b/src/ollama_gemma-4-26B-A4B/.devcontainer.json new file mode 100644 index 000000000..52ceafefb --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/.devcontainer.json @@ -0,0 +1,49 @@ +{ + "name": "Ollama Gemma 4 26B-A4B", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "shutdownAction": "none", + "workspaceFolder": "/workspace", + "postCreateCommand": + "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc; ./start-ollama.sh", + "postStartCommand": [ + "./startupscript/remount-on-restart.sh", + "abc", + "/config", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "features": { + "ghcr.io/devcontainers/features/java:1": { + "version": "17" + }, + "ghcr.io/devcontainers/features/aws-cli:1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1": {}, + "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, + "./.devcontainer/features/workbench-tools": { + "cloud": "${templateOption:cloud}", + "username": "abc", + "userHomeDir": "/config" + } + }, + "remoteUser": "root", + "customizations": { + "workbench": { + "opens": { + "extensions": [ + ".py", + ".ipynb", + ".sh", + ".md", + ".html", + ".csv", + ".json", + ".jsonc", + ".yaml", + ".yml" + ], + "fileUrlSuffix": "?payload=[[\"openFile\",\"vscode-remote:///config/{path}\"]]" + } + } + } +} diff --git a/src/ollama_gemma-4-26B-A4B/Dockerfile b/src/ollama_gemma-4-26B-A4B/Dockerfile new file mode 100644 index 000000000..5ada347b5 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/Dockerfile @@ -0,0 +1,24 @@ +FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /config/extensions /config/.cache \ + && chown -R abc:abc /config + +RUN apt-get update && apt-get install -y --no-install-recommends zstd \ + && curl -fsSL https://ollama.com/download/ollama-linux-amd64.tar.zst | tar --zstd -x -C /usr \ + && rm -rf /var/lib/apt/lists/* \ + && ollama --version + +RUN pip install --break-system-packages openai + +USER abc +ENV HOME=/config + +RUN /app/code-server/bin/code-server --extensions-dir /config/extensions --install-extension ms-python.python \ + && mkdir -p /config/data/User \ + && echo '{"workbench.colorTheme":"Default Dark Modern"}' > /config/data/User/settings.json + +USER root +WORKDIR /config diff --git a/src/ollama_gemma-4-26B-A4B/README.md b/src/ollama_gemma-4-26B-A4B/README.md new file mode 100644 index 000000000..7126a49d2 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/README.md @@ -0,0 +1,78 @@ +# Ollama + Gemma 4 26B-A4B + +Code-server IDE with an Ollama inference server running Gemma 4 26B MoE (4B active parameters). Opens a browser-based VS Code editor on port 8443 with Ollama serving an OpenAI-compatible API on port 11434 inside the container. + +## Recommended VM Configuration + +| Model | GPU | Machine type | CPUs | RAM | Notes | +|---|---|---|---|---|---| +| **Gemma 4 26B MoE** | **A100 (40 GB)** | **n1-standard-8** | **8** | **30 GB** | **Default config for this app** | +| Gemma 4 26B MoE | A100 (80 GB) | n1-standard-8 | 8 | 30 GB | More VRAM headroom for longer contexts | + +## Changing the Model + +Update the `ollama pull` command in `start-ollama.sh` and the `model=` parameter in your Python scripts. See available models at https://ollama.com/library. + +## Usage + +Once the app is ready, open the code-server IDE and run: + +```python +from openai import OpenAI + +client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused") + +response = client.chat.completions.create( + model="gemma4:26b", + messages=[{"role": "user", "content": "Hello!"}], + max_tokens=100, +) +print(response.choices[0].message.content) +``` + +### Using `uv` + +1. Install uv + + ```sh + pip install uv + ``` + +2. Initialize the project + + ```sh + uv init local-ollama-gemma4-26b + ``` + +3. Add openai package + + ```sh + cd local-ollama-gemma4-26b/ + uv add openai + ``` + +4. Run the application + + ```sh + uv run main.py + ``` + +## Debugging + +Check Ollama server logs: + +```bash +tail -f ~/ollama-server.log +``` + +Verify the server is running: + +```bash +curl http://localhost:11434/v1/models +``` + +List downloaded models: + +```bash +ollama list +``` diff --git a/src/ollama_gemma-4-26B-A4B/devcontainer-template.json b/src/ollama_gemma-4-26B-A4B/devcontainer-template.json new file mode 100644 index 000000000..47edeabd1 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/devcontainer-template.json @@ -0,0 +1,30 @@ +{ + "id": "ollama_gemma-4-26B-A4B", + "version": "0.0.1", + "name": "Ollama Gemma 4 26B-A4B", + "description": "Code-server IDE with Ollama inference server (Gemma 4 26B MoE). Requires A100 GPU.", + "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/ollama_gemma-4-26B-A4B", + "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", + "options": { + "cloud": { + "type": "string", + "description": "VM cloud environment", + "proposals": [ + "gcp" + ], + "default": "gcp" + }, + "login": { + "type": "string", + "description": "Whether to log in to workbench CLI", + "proposals": [ + "true", + "false" + ], + "default": "false" + } + }, + "platforms": [ + "Any" + ] +} diff --git a/src/ollama_gemma-4-26B-A4B/docker-compose.yaml b/src/ollama_gemma-4-26B-A4B/docker-compose.yaml new file mode 100644 index 000000000..bc09abbab --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/docker-compose.yaml @@ -0,0 +1,31 @@ +services: + app: + container_name: "application-server" + build: + context: . + dockerfile: Dockerfile + restart: always + volumes: + - .:/workspace:cached + - work:/config:cached + ports: + - "8443:8443" + environment: + USER: "abc" + DEFAULT_WORKSPACE: "/config" + SUDO_PASSWORD: "pwd" + OLLAMA_KEEP_ALIVE: "-1" + networks: + - app-network + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse + security_opt: + - apparmor:unconfined + +networks: + app-network: + external: true +volumes: + work: diff --git a/src/ollama_gemma-4-26B-A4B/main.py b/src/ollama_gemma-4-26B-A4B/main.py new file mode 100644 index 000000000..b953dc296 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/main.py @@ -0,0 +1,10 @@ +from openai import OpenAI + +client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused") + +response = client.chat.completions.create( + model="gemma4:26b", + messages=[{"role": "user", "content": "Hello, who are you?"}], + max_tokens=100, +) +print(response.choices[0].message.content) diff --git a/src/ollama_gemma-4-26B-A4B/start-ollama.sh b/src/ollama_gemma-4-26B-A4B/start-ollama.sh new file mode 100755 index 000000000..170260662 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/start-ollama.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +OLLAMA_LOG="/config/ollama-server.log" + +nohup ollama serve > "${OLLAMA_LOG}" 2>&1 & +sleep 2 + +echo "Pulling gemma4:26b model (this may take a few minutes)..." +ollama pull gemma4:26b >> "${OLLAMA_LOG}" 2>&1 + +echo "Preloading model into GPU memory..." +curl -s http://localhost:11434/api/generate -d '{"model":"gemma4:26b","prompt":"warmup","options":{"num_predict":1}}' > /dev/null 2>&1 + +echo "Ollama ready — model loaded — logs at ${OLLAMA_LOG}" diff --git a/src/ollama_gemma-4-26B-A4B/sudo-passwordless.sh b/src/ollama_gemma-4-26B-A4B/sudo-passwordless.sh new file mode 100755 index 000000000..d14bd0a17 --- /dev/null +++ b/src/ollama_gemma-4-26B-A4B/sudo-passwordless.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script is used to set up passwordless sudo for the core user on the VM. +# It requires to be run with root priviledges and USER_NAME to be set in the environment. +# It is typically called from post-startup.sh. + +USER_NAME="${1}" + +if [[ -z "${USER_NAME}" ]]; then + echo "Usage: $0 " + exit 1 +fi + +sudoers_file="/etc/sudoers" +sudoers_d_file="/etc/sudoers.d/${USER_NAME}" + +# Make sure user exists +if ! id "${USER_NAME}" &>/dev/null; then + echo "User ${USER_NAME} does not exist." + exit 1 +fi + +# Check if there's an old rule in the main sudoers file that requires a password +if grep -q "^${USER_NAME} ALL=(ALL:ALL) ALL" "${sudoers_file}"; then + echo "Found password-requiring rule for ${USER_NAME} in /etc/sudoers. Commenting it out." + + # Comment out the old rule in /etc/sudoers + sed -i "s/^${USER_NAME} ALL=(ALL:ALL) ALL/# ${USER_NAME} ALL=(ALL:ALL) ALL/" "${sudoers_file}" +fi + +echo "${USER_NAME} ALL=(ALL) NOPASSWD:ALL" > "${sudoers_d_file}" +chmod 440 "${sudoers_d_file}" + +echo "User ${USER_NAME} has been given passwordless sudo access." From f1f8b7e35a69cb2b441bcb9b7b16cc52b5e6178a Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 10:54:41 -0400 Subject: [PATCH 16/27] Remove main.py from ollama_gemma-4-26B-A4B template Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/main.py | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 src/ollama_gemma-4-26B-A4B/main.py diff --git a/src/ollama_gemma-4-26B-A4B/main.py b/src/ollama_gemma-4-26B-A4B/main.py deleted file mode 100644 index b953dc296..000000000 --- a/src/ollama_gemma-4-26B-A4B/main.py +++ /dev/null @@ -1,10 +0,0 @@ -from openai import OpenAI - -client = OpenAI(base_url="http://localhost:11434/v1", api_key="unused") - -response = client.chat.completions.create( - model="gemma4:26b", - messages=[{"role": "user", "content": "Hello, who are you?"}], - max_tokens=100, -) -print(response.choices[0].message.content) From 4b3239c1f850cd1017d8b157743610a8a7dc23d2 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 11:38:11 -0400 Subject: [PATCH 17/27] Start Ollama on every VM boot, not just first creation Moved start-ollama.sh into postStartCommand so the model auto-starts after VM stop/start cycles. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/.devcontainer.json | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ollama_gemma-4-26B-A4B/.devcontainer.json b/src/ollama_gemma-4-26B-A4B/.devcontainer.json index 52ceafefb..d81311e07 100644 --- a/src/ollama_gemma-4-26B-A4B/.devcontainer.json +++ b/src/ollama_gemma-4-26B-A4B/.devcontainer.json @@ -6,13 +6,8 @@ "workspaceFolder": "/workspace", "postCreateCommand": "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc; ./start-ollama.sh", - "postStartCommand": [ - "./startupscript/remount-on-restart.sh", - "abc", - "/config", - "${templateOption:cloud}", - "${templateOption:login}" - ], + "postStartCommand": + "./startupscript/remount-on-restart.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./start-ollama.sh", "features": { "ghcr.io/devcontainers/features/java:1": { "version": "17" From 75549d4708818c07c5f517929dc467f9dd592c3a Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 11:40:07 -0400 Subject: [PATCH 18/27] Enable flash attention for more efficient inference Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ollama_gemma-4-26B-A4B/docker-compose.yaml b/src/ollama_gemma-4-26B-A4B/docker-compose.yaml index bc09abbab..d34c906f2 100644 --- a/src/ollama_gemma-4-26B-A4B/docker-compose.yaml +++ b/src/ollama_gemma-4-26B-A4B/docker-compose.yaml @@ -15,6 +15,7 @@ services: DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" OLLAMA_KEEP_ALIVE: "-1" + OLLAMA_FLASH_ATTENTION: "true" networks: - app-network cap_add: From 39e072ff522744301b13d2b87b05ea46853e8c48 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 11:42:30 -0400 Subject: [PATCH 19/27] Add tesseract-ocr to Dockerfile Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ollama_gemma-4-26B-A4B/Dockerfile b/src/ollama_gemma-4-26B-A4B/Dockerfile index 5ada347b5..998f4ff22 100644 --- a/src/ollama_gemma-4-26B-A4B/Dockerfile +++ b/src/ollama_gemma-4-26B-A4B/Dockerfile @@ -1,7 +1,7 @@ FROM lscr.io/linuxserver/code-server@sha256:7bd334657f13505abc1e20afeeee5670ad8f818e68853c810889184e597f3051 RUN apt-get update \ - && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv \ + && apt-get install -y --no-install-recommends jq python3 python3-pip python3-venv tesseract-ocr \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /config/extensions /config/.cache \ && chown -R abc:abc /config From 6c481a5ab6ff79c5db2f4b736d028cc34eb1e539 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Mon, 18 May 2026 20:14:12 -0400 Subject: [PATCH 20/27] Add uv Python package manager to Dockerfile Co-Authored-By: Claude Opus 4.6 (1M context) --- src/ollama_gemma-4-26B-A4B/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ollama_gemma-4-26B-A4B/Dockerfile b/src/ollama_gemma-4-26B-A4B/Dockerfile index 998f4ff22..a8ddc67d1 100644 --- a/src/ollama_gemma-4-26B-A4B/Dockerfile +++ b/src/ollama_gemma-4-26B-A4B/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends zstd \ && rm -rf /var/lib/apt/lists/* \ && ollama --version -RUN pip install --break-system-packages openai +RUN pip install --break-system-packages openai uv USER abc ENV HOME=/config From cea74d1a4c4eec2bfabd92646f919bcc7bf917f8 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Wed, 10 Jun 2026 13:50:01 -0400 Subject: [PATCH 21/27] Add pep-ppp-prod app template with dark mode default Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/.devcontainer.json | 71 +++++++++++++++++++++ src/pep-ppp-prod/README.md | 17 +++++ src/pep-ppp-prod/devcontainer-template.json | 23 +++++++ src/pep-ppp-prod/docker-compose.yaml | 28 ++++++++ src/pep-ppp-prod/sudo-passwordless.sh | 34 ++++++++++ 5 files changed, 173 insertions(+) create mode 100644 src/pep-ppp-prod/.devcontainer.json create mode 100644 src/pep-ppp-prod/README.md create mode 100644 src/pep-ppp-prod/devcontainer-template.json create mode 100644 src/pep-ppp-prod/docker-compose.yaml create mode 100644 src/pep-ppp-prod/sudo-passwordless.sh diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json new file mode 100644 index 000000000..7f1992cf6 --- /dev/null +++ b/src/pep-ppp-prod/.devcontainer.json @@ -0,0 +1,71 @@ +{ + "name": "pepcli-prod app", + "dockerComposeFile": "docker-compose.yaml", + "service": "app", + "shutdownAction": "none", + "workspaceFolder": "/workspace", + "postCreateCommand": "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc", + "postStartCommand": [ + "./startupscript/remount-on-restart.sh", + "abc", + "/config", + "${templateOption:cloud}", + "${templateOption:login}" + ], + "features": { + "ghcr.io/devcontainers/features/java@sha256:9663ce0219ff85786e87901ce5f0a59f488edd5f99b46015192cda48468b233a": { + "version": "17" + }, + "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": { + "version": "24.11.0" + }, + "ghcr.io/devcontainers/features/aws-cli@sha256:1f93c8315b7a6d76982ebb2269f8b0d50413fc0f965c032edf4aee0caceb73ef": {}, + "ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {}, + "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, + "./.devcontainer/features/gemini-cli": { + "username": "abc" + }, + "./.devcontainer/features/workbench-tools": { + "cloud": "${templateOption:cloud}", + "username": "abc", + "userHomeDir": "/config" + }, + "./.devcontainer/features/postgres-client": { + "version": "16" + } + }, + "remoteUser": "root", + "customizations": { + "vscode": { + "settings": { + "workbench.colorTheme": "Default Dark Modern" + } + }, + "workbench": { + "opens": { + "extensions": [ + ".c", + ".cjs", + ".cpp", + ".go", + ".java", + ".js", + ".mjs", + ".php", + ".scala", + ".sh", + ".ts", + ".md", + ".html", + ".csv", + ".json", + ".jsonc", + ".tsv", + ".xml", + ".yml" + ], + "fileUrlSuffix": "?payload=[[\"openFile\",\"vscode-remote:///config/{path}\"]]" + } + } + } +} \ No newline at end of file diff --git a/src/pep-ppp-prod/README.md b/src/pep-ppp-prod/README.md new file mode 100644 index 000000000..9909fd726 --- /dev/null +++ b/src/pep-ppp-prod/README.md @@ -0,0 +1,17 @@ + +# PEP CLI Prod (pep-ppp-prod) + +A Template to run PEP CLI prod on workbench. + +## Options + +| Options Id | Description | Type | Default Value | +|-----|-----|-----|-----| +| cloud | VM cloud environment | string | gcp | +| login | Whether to log in to workbench CLI | string | false | + + + +--- + +_Note: This file was auto-generated from the [devcontainer-template.json](https://github.com/verily-src/workbench-app-devcontainers/blob/main/src/pep-ppp-prod/devcontainer-template.json). Add additional notes to a `NOTES.md`._ \ No newline at end of file diff --git a/src/pep-ppp-prod/devcontainer-template.json b/src/pep-ppp-prod/devcontainer-template.json new file mode 100644 index 000000000..a0e81e5e1 --- /dev/null +++ b/src/pep-ppp-prod/devcontainer-template.json @@ -0,0 +1,23 @@ +{ + "id": "pepcli-prod", + "version": "0.0.1", + "name": "PEP CLI Prod", + "description": "A Template to run PEP CLI prod on workbench", + "documentationURL": "https://github.com/verily-src/workbench-app-devcontainers/tree/master/src/pepcli-prod", + "licenseURL": "https://github.com/verily-src/workbench-app-devcontainers/blob/master/LICENSE", + "options": { + "cloud": { + "type": "string", + "description": "VM cloud environment", + "proposals": ["gcp", "aws"], + "default": "gcp" + }, + "login": { + "type": "string", + "description": "Whether to log in to workbench CLI", + "proposals": ["true", "false"], + "default": "false" + } + }, + "platforms": ["Any"] +} diff --git a/src/pep-ppp-prod/docker-compose.yaml b/src/pep-ppp-prod/docker-compose.yaml new file mode 100644 index 000000000..314ccd48a --- /dev/null +++ b/src/pep-ppp-prod/docker-compose.yaml @@ -0,0 +1,28 @@ +version: "2.4" +services: + app: + container_name: "application-server" + image: "gitlabregistry.pep.cs.ru.nl/pep-public/core/ppp-prod" + restart: always + volumes: + - .:/workspace:cached + - work:/home/vscode:cached + ports: + - "8443:8443" + environment: + USER: "abc" + DEFAULT_WORKSPACE: "/config" + SUDO_PASSWORD: "pwd" + networks: + - app-network + cap_add: + - SYS_ADMIN + devices: + - /dev/fuse + security_opt: + - apparmor:unconfined +networks: + app-network: + external: true +volumes: + work: diff --git a/src/pep-ppp-prod/sudo-passwordless.sh b/src/pep-ppp-prod/sudo-passwordless.sh new file mode 100644 index 000000000..d14bd0a17 --- /dev/null +++ b/src/pep-ppp-prod/sudo-passwordless.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +# This script is used to set up passwordless sudo for the core user on the VM. +# It requires to be run with root priviledges and USER_NAME to be set in the environment. +# It is typically called from post-startup.sh. + +USER_NAME="${1}" + +if [[ -z "${USER_NAME}" ]]; then + echo "Usage: $0 " + exit 1 +fi + +sudoers_file="/etc/sudoers" +sudoers_d_file="/etc/sudoers.d/${USER_NAME}" + +# Make sure user exists +if ! id "${USER_NAME}" &>/dev/null; then + echo "User ${USER_NAME} does not exist." + exit 1 +fi + +# Check if there's an old rule in the main sudoers file that requires a password +if grep -q "^${USER_NAME} ALL=(ALL:ALL) ALL" "${sudoers_file}"; then + echo "Found password-requiring rule for ${USER_NAME} in /etc/sudoers. Commenting it out." + + # Comment out the old rule in /etc/sudoers + sed -i "s/^${USER_NAME} ALL=(ALL:ALL) ALL/# ${USER_NAME} ALL=(ALL:ALL) ALL/" "${sudoers_file}" +fi + +echo "${USER_NAME} ALL=(ALL) NOPASSWD:ALL" > "${sudoers_d_file}" +chmod 440 "${sudoers_d_file}" + +echo "User ${USER_NAME} has been given passwordless sudo access." From 45c544e2062d59e1327a6e84daedfa0cf25bec8d Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Wed, 10 Jun 2026 14:07:33 -0400 Subject: [PATCH 22/27] Align pep-ppp-prod with pep-cli-acc config (root user, code-server on 8080, login default true) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/.devcontainer.json | 30 +++++++++------------ src/pep-ppp-prod/devcontainer-template.json | 2 +- src/pep-ppp-prod/docker-compose.yaml | 6 ++--- 3 files changed, 17 insertions(+), 21 deletions(-) diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json index 7f1992cf6..075a93a7a 100644 --- a/src/pep-ppp-prod/.devcontainer.json +++ b/src/pep-ppp-prod/.devcontainer.json @@ -4,34 +4,30 @@ "service": "app", "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc", + "postCreateCommand": "./startupscript/post-startup.sh root /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh root", "postStartCommand": [ "./startupscript/remount-on-restart.sh", - "abc", + "root", "/config", "${templateOption:cloud}", "${templateOption:login}" ], "features": { - "ghcr.io/devcontainers/features/java@sha256:9663ce0219ff85786e87901ce5f0a59f488edd5f99b46015192cda48468b233a": { + "ghcr.io/devcontainers/features/java:1.6.3": { "version": "17" }, - "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": { - "version": "24.11.0" + "ghcr.io/devcontainers/features/aws-cli:1.1.1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1.0.1": {}, + "ghcr.io/coder/devcontainer-features/code-server:1": { + "port": 8080, + "host": "0.0.0.0", + "auth": "none" }, - "ghcr.io/devcontainers/features/aws-cli@sha256:1f93c8315b7a6d76982ebb2269f8b0d50413fc0f965c032edf4aee0caceb73ef": {}, - "ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {}, - "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, - "./.devcontainer/features/gemini-cli": { - "username": "abc" + "ghcr.io/devcontainers/features/go:1": { + "version": "1.22" }, - "./.devcontainer/features/workbench-tools": { - "cloud": "${templateOption:cloud}", - "username": "abc", - "userHomeDir": "/config" - }, - "./.devcontainer/features/postgres-client": { - "version": "16" + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12" } }, "remoteUser": "root", diff --git a/src/pep-ppp-prod/devcontainer-template.json b/src/pep-ppp-prod/devcontainer-template.json index a0e81e5e1..57ad3f700 100644 --- a/src/pep-ppp-prod/devcontainer-template.json +++ b/src/pep-ppp-prod/devcontainer-template.json @@ -16,7 +16,7 @@ "type": "string", "description": "Whether to log in to workbench CLI", "proposals": ["true", "false"], - "default": "false" + "default": "true" } }, "platforms": ["Any"] diff --git a/src/pep-ppp-prod/docker-compose.yaml b/src/pep-ppp-prod/docker-compose.yaml index 314ccd48a..206dfe008 100644 --- a/src/pep-ppp-prod/docker-compose.yaml +++ b/src/pep-ppp-prod/docker-compose.yaml @@ -6,11 +6,11 @@ services: restart: always volumes: - .:/workspace:cached - - work:/home/vscode:cached + - work:/home/ubuntu:cached ports: - - "8443:8443" + - "8080:8080" environment: - USER: "abc" + USER: "root" DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" networks: From af63abf808555c6d654e841f5997ce449bfcdc7d Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Wed, 10 Jun 2026 14:29:13 -0400 Subject: [PATCH 23/27] Add Claude Code setup with Vertex config, GCP auth, and dark mode Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/.devcontainer.json | 5 ++- src/pep-ppp-prod/setup-claude-code.sh | 61 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) create mode 100755 src/pep-ppp-prod/setup-claude-code.sh diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json index 075a93a7a..ad9f8e83c 100644 --- a/src/pep-ppp-prod/.devcontainer.json +++ b/src/pep-ppp-prod/.devcontainer.json @@ -4,7 +4,7 @@ "service": "app", "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": "./startupscript/post-startup.sh root /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh root", + "postCreateCommand": "./startupscript/post-startup.sh root /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh root; ./setup-claude-code.sh", "postStartCommand": [ "./startupscript/remount-on-restart.sh", "root", @@ -28,7 +28,8 @@ }, "ghcr.io/devcontainers/features/python:1": { "version": "3.12" - } + }, + "ghcr.io/anthropics/devcontainer-features/claude-code:1": {} }, "remoteUser": "root", "customizations": { diff --git a/src/pep-ppp-prod/setup-claude-code.sh b/src/pep-ppp-prod/setup-claude-code.sh new file mode 100755 index 000000000..06c01fd55 --- /dev/null +++ b/src/pep-ppp-prod/setup-claude-code.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +mkdir -p ~/.claude && cat < ~/.claude/settings.json +{ + "env": { + "CLAUDE_CODE_USE_VERTEX": "1", + "CLOUD_ML_REGION": "us-east5", + "VERTEX_REGION_CLAUDE_4_7_OPUS": "us", + "VERTEX_REGION_CLAUDE_4_8_OPUS": "us", + "ANTHROPIC_VERTEX_PROJECT_ID": "wb-agile-aubergine-8187", + "CLAUDE_CODE_ENABLE_TELEMETRY": "1", + "DISABLE_TELEMETRY": "1", + "OTEL_LOG_USER_PROMPTS": "0", + "OTEL_LOG_TOOL_DETAILS": "1", + "OTEL_METRICS_EXPORTER": "otlp", + "OTEL_LOGS_EXPORTER": "otlp", + "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf", + "OTEL_EXPORTER_OTLP_ENDPOINT": "https://claude-otel-collector-usage-events-64mw6qm9.uc.gateway.dev", + "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT": "https://claude-otel-collector-usage-events-64mw6qm9.uc.gateway.dev/v1/traces", + "OTEL_EXPORTER_OTLP_METRICS_ENDPOINT": "https://claude-otel-collector-usage-events-64mw6qm9.uc.gateway.dev/v1/metrics", + "OTEL_EXPORTER_OTLP_HEADERS": "x-api-key=AIzaSyAY8kCGXpV3XHEDc3J5CoMk7XeCkb6HVuQ", + "OTEL_RESOURCE_ATTRIBUTES": "environment=workbench,username=\${WORKBENCH_USER_EMAIL%@*}" + }, + "permissions": { + "disableBypassPermissionsMode": "disable", + "deny": [ + "Read(.env)", + "Read(.env.*)" + ] + }, + "allowedMcpServers": [ + {"serverUrl": "https://mcp.atlassian.com/v1/mcp"}, + {"serverUrl": "https://mcp.figma.com/mcp"}, + {"serverUrl": "https://mcp.cypress.io/mcp"}, + {"serverUrl": "https://mcp.heymarvin.com"}, + {"serverCommand": ["xcrun", "mcpbridge"]}, + { + "serverCommand": [ + "npx", + "-y", + "@playwright/mcp@latest", + "--allowed-origins", + "https://dev.*.verily.com;https://test.*.verily.com;https://dev.*.verilyme.com;https://test.*.verilyme.com;https://localhost:*", + "--save-session", + "--extension" + ] + } + ], + "sandbox": { + "enabled": true, + "autoAllowBashIfSandboxed": true, + "excludedCommands": ["git"], + "allowUnsandboxedCommands": false + } +} +EOF + +mkdir -p /config/data/User +echo '{"workbench.colorTheme":"Default Dark Modern"}' > /config/data/User/settings.json + +gcloud auth login --update-adc From 5598e0e8ccdd52d63996005dc2daf13616327789 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Wed, 10 Jun 2026 17:21:42 -0400 Subject: [PATCH 24/27] Remove interactive gcloud auth from startup script to prevent hang Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/setup-claude-code.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/pep-ppp-prod/setup-claude-code.sh b/src/pep-ppp-prod/setup-claude-code.sh index 06c01fd55..a8bf39a77 100755 --- a/src/pep-ppp-prod/setup-claude-code.sh +++ b/src/pep-ppp-prod/setup-claude-code.sh @@ -57,5 +57,3 @@ EOF mkdir -p /config/data/User echo '{"workbench.colorTheme":"Default Dark Modern"}' > /config/data/User/settings.json - -gcloud auth login --update-adc From be34f4754a0ffa52032e06b46d09a922ebc6ccd4 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Thu, 11 Jun 2026 09:36:59 -0400 Subject: [PATCH 25/27] Revert to original working config (abc user, port 8443, linuxserver features) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/.devcontainer.json | 31 +++++++++++++++------------- src/pep-ppp-prod/docker-compose.yaml | 6 +++--- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json index ad9f8e83c..a478ac1a2 100644 --- a/src/pep-ppp-prod/.devcontainer.json +++ b/src/pep-ppp-prod/.devcontainer.json @@ -4,32 +4,35 @@ "service": "app", "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": "./startupscript/post-startup.sh root /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh root; ./setup-claude-code.sh", + "postCreateCommand": "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc; ./setup-claude-code.sh", "postStartCommand": [ "./startupscript/remount-on-restart.sh", - "root", + "abc", "/config", "${templateOption:cloud}", "${templateOption:login}" ], "features": { - "ghcr.io/devcontainers/features/java:1.6.3": { + "ghcr.io/devcontainers/features/java@sha256:9663ce0219ff85786e87901ce5f0a59f488edd5f99b46015192cda48468b233a": { "version": "17" }, - "ghcr.io/devcontainers/features/aws-cli:1.1.1": {}, - "ghcr.io/dhoeric/features/google-cloud-cli:1.0.1": {}, - "ghcr.io/coder/devcontainer-features/code-server:1": { - "port": 8080, - "host": "0.0.0.0", - "auth": "none" + "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": { + "version": "24.11.0" }, - "ghcr.io/devcontainers/features/go:1": { - "version": "1.22" + "ghcr.io/devcontainers/features/aws-cli@sha256:1f93c8315b7a6d76982ebb2269f8b0d50413fc0f965c032edf4aee0caceb73ef": {}, + "ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {}, + "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, + "./.devcontainer/features/gemini-cli": { + "username": "abc" }, - "ghcr.io/devcontainers/features/python:1": { - "version": "3.12" + "./.devcontainer/features/workbench-tools": { + "cloud": "${templateOption:cloud}", + "username": "abc", + "userHomeDir": "/config" }, - "ghcr.io/anthropics/devcontainer-features/claude-code:1": {} + "./.devcontainer/features/postgres-client": { + "version": "16" + } }, "remoteUser": "root", "customizations": { diff --git a/src/pep-ppp-prod/docker-compose.yaml b/src/pep-ppp-prod/docker-compose.yaml index 206dfe008..314ccd48a 100644 --- a/src/pep-ppp-prod/docker-compose.yaml +++ b/src/pep-ppp-prod/docker-compose.yaml @@ -6,11 +6,11 @@ services: restart: always volumes: - .:/workspace:cached - - work:/home/ubuntu:cached + - work:/home/vscode:cached ports: - - "8080:8080" + - "8443:8443" environment: - USER: "root" + USER: "abc" DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" networks: From a2ec2aa77e5673d83ed34be04c25c9f6af67d781 Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Thu, 11 Jun 2026 10:10:43 -0400 Subject: [PATCH 26/27] Restore pep-ppp-prod to last known working state (5598e0e) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/pep-ppp-prod/.devcontainer.json | 31 +++++++++++++--------------- src/pep-ppp-prod/docker-compose.yaml | 6 +++--- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json index a478ac1a2..ad9f8e83c 100644 --- a/src/pep-ppp-prod/.devcontainer.json +++ b/src/pep-ppp-prod/.devcontainer.json @@ -4,35 +4,32 @@ "service": "app", "shutdownAction": "none", "workspaceFolder": "/workspace", - "postCreateCommand": "./startupscript/post-startup.sh abc /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh abc; ./setup-claude-code.sh", + "postCreateCommand": "./startupscript/post-startup.sh root /config \"${templateOption:cloud}\" \"${templateOption:login}\"; ./sudo-passwordless.sh root; ./setup-claude-code.sh", "postStartCommand": [ "./startupscript/remount-on-restart.sh", - "abc", + "root", "/config", "${templateOption:cloud}", "${templateOption:login}" ], "features": { - "ghcr.io/devcontainers/features/java@sha256:9663ce0219ff85786e87901ce5f0a59f488edd5f99b46015192cda48468b233a": { + "ghcr.io/devcontainers/features/java:1.6.3": { "version": "17" }, - "ghcr.io/devcontainers/features/node@sha256:8c0de46939b61958041700ee89e3493f3b2e4131a06dc46b4d9423427d06e5f6": { - "version": "24.11.0" + "ghcr.io/devcontainers/features/aws-cli:1.1.1": {}, + "ghcr.io/dhoeric/features/google-cloud-cli:1.0.1": {}, + "ghcr.io/coder/devcontainer-features/code-server:1": { + "port": 8080, + "host": "0.0.0.0", + "auth": "none" }, - "ghcr.io/devcontainers/features/aws-cli@sha256:1f93c8315b7a6d76982ebb2269f8b0d50413fc0f965c032edf4aee0caceb73ef": {}, - "ghcr.io/dhoeric/features/google-cloud-cli@sha256:fa5d894718825c5ad8009ac8f2c9f0cea3d1661eb108a9d465cba9f3fc48965f": {}, - "ghcr.io/anthropics/devcontainer-features/claude-code@sha256:cfc2e7d3e9fd3b9b01f8d5cb158508a884c8c0ede2e23ed10f32dea5d4ffe69a": {}, - "./.devcontainer/features/gemini-cli": { - "username": "abc" + "ghcr.io/devcontainers/features/go:1": { + "version": "1.22" }, - "./.devcontainer/features/workbench-tools": { - "cloud": "${templateOption:cloud}", - "username": "abc", - "userHomeDir": "/config" + "ghcr.io/devcontainers/features/python:1": { + "version": "3.12" }, - "./.devcontainer/features/postgres-client": { - "version": "16" - } + "ghcr.io/anthropics/devcontainer-features/claude-code:1": {} }, "remoteUser": "root", "customizations": { diff --git a/src/pep-ppp-prod/docker-compose.yaml b/src/pep-ppp-prod/docker-compose.yaml index 314ccd48a..206dfe008 100644 --- a/src/pep-ppp-prod/docker-compose.yaml +++ b/src/pep-ppp-prod/docker-compose.yaml @@ -6,11 +6,11 @@ services: restart: always volumes: - .:/workspace:cached - - work:/home/vscode:cached + - work:/home/ubuntu:cached ports: - - "8443:8443" + - "8080:8080" environment: - USER: "abc" + USER: "root" DEFAULT_WORKSPACE: "/config" SUDO_PASSWORD: "pwd" networks: From 1f8a04ebc69469837543ad5f9d6a59fec9eb8a4a Mon Sep 17 00:00:00 2001 From: Drew Applegath Date: Fri, 28 Aug 2026 09:38:19 -0400 Subject: [PATCH 27/27] Fix pep-ppp-prod Java feature: switch JDK distro from ms to tem The Java (via SDKMAN!) feature defaulted to the Microsoft Build of OpenJDK (jdkDistro "ms"). SDKMAN no longer resolves a bare version "17" for the "ms" distro (Microsoft has wound down that build), so the feature install failed with "Version 17 not found", aborting the devcontainer build on every prod instance. Pin jdkDistro to Temurin ("tem"), the maintained Eclipse Adoptium LTS build, which resolves version 17 reliably. Co-Authored-By: Claude Opus 4.8 --- src/pep-ppp-prod/.devcontainer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pep-ppp-prod/.devcontainer.json b/src/pep-ppp-prod/.devcontainer.json index ad9f8e83c..15f3c2131 100644 --- a/src/pep-ppp-prod/.devcontainer.json +++ b/src/pep-ppp-prod/.devcontainer.json @@ -14,7 +14,8 @@ ], "features": { "ghcr.io/devcontainers/features/java:1.6.3": { - "version": "17" + "version": "17", + "jdkDistro": "tem" }, "ghcr.io/devcontainers/features/aws-cli:1.1.1": {}, "ghcr.io/dhoeric/features/google-cloud-cli:1.0.1": {},