GPU-accelerated VS Code in a browser — CUDA 12.4 / 12.6, PyTorch, uv, and Claude Code preinstalled, ready to deploy on Dokploy.
cuda-code-server gives you a browser-based VS Code IDE that can actually see your GPU. Pull the image, run it with --gpus all, and torch.cuda.is_available() returns True on the first try. The image bundles CUDA 12.6.1 (or 12.4.1), cuDNN, PyTorch + torchvision + torchaudio, the Claude Code CLI, uv, Node.js 20, and the Docker CLI — so a fresh container is a complete ML development environment, not a shell you have to spend an afternoon outfitting.
Primary deploy target is Dokploy. Plain docker run, Docker Compose, and Kubernetes are all first-class too.
- Quick start
- Image variants
- Deploy on Dokploy (primary target)
- Deploy with Docker Compose
- Deploy on Kubernetes
- Connecting and using
- What's inside
- Configuration
- Building from source
- Verifying GPU access
- FAQ
- Contributing
- Related projects
- License
Prerequisites: Docker 23+ and the NVIDIA Container Toolkit on the host. A quick host-side sanity check:
docker run --rm --gpus all nvidia/cuda:12.6.1-base-ubuntu22.04 nvidia-smiIf that prints the GPU table, the runtime is wired up. Now run cuda-code-server:
PASSWORD=$(openssl rand -base64 24)
echo "Save this password: $PASSWORD"
docker run -d \
--name cuda-code-server \
--gpus all \
-e PASSWORD="$PASSWORD" \
-p 8080:8080 \
-v cuda-code-server-home:/home/coder \
-v cuda-code-server-project:/home/coder/project \
ghcr.io/harshpatel333/cuda-code-server:latestOpen http://localhost:8080, enter the password. In the integrated terminal (Ctrl+`):
nvidia-smi
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count())"Both should succeed. You now have a GPU-enabled VS Code.
Every variant is published to both registries (GHCR and Docker Hub) on every push to main and every v* tag.
| CUDA | Python | PyTorch wheel index | Tag | Notes |
|---|---|---|---|---|
| 12.6.1 | 3.12 | cu126 |
12.6-py312 |
Default — also tagged :latest. |
| 12.6.1 | 3.11 | cu126 |
12.6-py311 |
Use for libraries not yet on 3.12. |
| 12.4.1 | 3.12 | cu124 |
12.4-py312 |
Older CUDA — required for some drivers / libraries. |
| 12.4.1 | 3.11 | cu124 |
12.4-py311 |
The conservative combo. |
Additional tag shapes published on v* releases:
0.1.0-12.6-py312— exact semver + variant (pin for reproducibility).0.1-12.6-py312— major.minor + variant (gets minor-version bugfixes).sha-<short>-12.6-py312— immutable SHA reference.
Pull examples:
docker pull ghcr.io/harshpatel333/cuda-code-server:latest
docker pull ghcr.io/harshpatel333/cuda-code-server:12.4-py311
docker pull docker.io/harshpatel333/cuda-code-server:0.1-12.6-py312Short version: paste examples/dokploy/docker-compose.yml into a Dokploy Compose → Raw service, set PASSWORD in the Environment tab, add your domain in the Domains tab, deploy. No Traefik labels needed — Dokploy wires them from the GUI.
Full step-by-step walkthrough (prereqs, gotchas, verification, GPU pinning, updating): docs/dokploy.md.
For a dev box on a trusted network (no reverse proxy). Copy examples/docker-compose/docker-compose.yml and run:
curl -LO https://raw.githubusercontent.com/harshpatel333/cuda-code-server/main/examples/docker-compose/docker-compose.yml
PASSWORD=$(openssl rand -base64 24) docker compose up -d
echo "PASSWORD=$PASSWORD"Port 8080 is exposed directly on the host. For anything internet-facing, front it with a TLS + auth layer — see SECURITY.md.
examples/kubernetes/deployment.yaml includes a Deployment + Service + two PVCs (20 GiB home, 100 GiB project), plus a commented-out Ingress block for cert-manager setups.
Prerequisites: the NVIDIA device plugin on the cluster and a default StorageClass supporting ReadWriteOnce.
kubectl create secret generic cuda-code-server \
--from-literal=password="$(openssl rand -base64 24)"
kubectl apply -f https://raw.githubusercontent.com/harshpatel333/cuda-code-server/main/examples/kubernetes/deployment.yamlThe full reference is docs/usage.md. Compact version below.
Open the deployed URL, enter PASSWORD. The tab is a full VS Code — extensions install, the integrated terminal is a real bash shell inside the container, keyboard shortcuts work. Gotcha: Ctrl+W closes the browser tab — fix by installing the page as a PWA (Chromium address bar → Install) or by rebinding the shortcut in code-server settings.
The image ships with openssh-server baked in — off by default. Set ENABLE_SSHD=true, supply SSH_AUTHORIZED_KEYS, expose port 2222, and connect from VS Code Desktop (or Cursor / Windsurf) with the Remote-SSH extension. Full step-by-step for each editor — including ~/.ssh/config, Dokploy TCP exposure, Kubernetes Service, and troubleshooting — lives at docs/remote-ssh.md.
One-liner for plain docker run:
docker run -d --gpus all \
-e PASSWORD="$PASSWORD" \
-e ENABLE_SSHD=true \
-e SSH_AUTHORIZED_KEYS="$(cat ~/.ssh/id_ed25519.pub)" \
-p 8080:8080 -p 2222:2222 \
-v cuda-code-server-home:/home/coder -v cuda-code-server-project:/home/coder/project \
ghcr.io/harshpatel333/cuda-code-server:latestThen ssh -p 2222 coder@<host> works, and VS Code Desktop's F1 → Remote-SSH: Connect to Host gives you a native-feeling IDE with access to the Microsoft marketplace (Pylance, Copilot, C/C++ IntelliSense) — the extensions the browser IDE can't use.
- Runs as the
coderuser (passwordlesssudois intentional — see SECURITY.md). - The Python venv at
/home/coder/.venvauto-activates (torch, numpy, pandas, matplotlib, ipykernel preinstalled). dockerCLI works against the host daemon if the socket is mounted — trydocker ps.ghandclaudeare onPATH.
code-server installs from the Open VSX registry (not Microsoft's marketplace — a licensing constraint). Most popular extensions are there. For Microsoft-exclusive ones: Pyright replaces Pylance, clangd replaces C/C++ IntelliSense, Continue or Cline for AI pair-programming. VSIX sideloading works too — see docs/usage.md.
Extensions persist across container restarts only if /home/coder is on a persistent volume.
Inside the container, git works but has no credentials. Either gh auth login (stores HTTPS token in ~/.config/gh, which is on the persistent volume) or generate a key with ssh-keygen -t ed25519 and add the public key to GitHub — ~/.ssh is also persistent.
Open a .ipynb file in VS Code — the built-in Jupyter support uses the container's kernel (ipykernel is preinstalled). No separate server needed. If you want JupyterLab specifically: uv pip install jupyterlab into a project venv, run jupyter lab --ip=0.0.0.0 --port=8888 --no-browser --NotebookApp.token='' with port 8888 exposed.
This table reflects the default build (CUDA 12.6.1, Python 3.12). The other matrix cells differ only in CUDA and Python minor version.
| Component | Version / source | How it was installed |
|---|---|---|
| Base image | nvidia/cuda:12.6.1-cudnn-devel-ubuntu22.04 |
FROM |
| CUDA Toolkit | 12.6.1 | from base image |
| cuDNN | from base image (devel variant) | included |
| Python | 3.12 (or 3.11) | deadsnakes PPA |
| PyTorch + torchvision + torchaudio | latest on cu126 (or cu124) |
uv pip install --index-url https://download.pytorch.org/whl/cu126 … |
| NumPy, pandas, matplotlib | latest on PyPI | uv pip install |
ipykernel |
latest on PyPI | uv pip install — backs VS Code's Jupyter support |
uv |
latest | Astral's official install script, system-wide |
| Node.js | 20.x | NodeSource apt |
| Docker CLI + Compose + Buildx | latest stable | Docker's official apt repo |
code-server |
latest stable | Coder's official install script |
| Claude Code CLI | latest on npm | npm install -g @anthropic-ai/claude-code (opt out with --build-arg INSTALL_CLAUDE_CODE=false) |
| Shell tools | git, git-lfs, build-essential, openssh-client, jq, tmux, htop, vim, nano, ripgrep, unzip, sudo, gosu | distro apt |
Persistent paths — mount these as volumes for a durable environment:
| Path | Contents |
|---|---|
/home/coder |
settings, VS Code extensions, .venv, SSH keys, gh auth, shell history |
/home/coder/project |
your code (default working directory inside the IDE) |
Environment variables. All are read by the container at startup.
| Variable | Required | Purpose |
|---|---|---|
PASSWORD |
yes (or HASHED_PASSWORD) |
Browser login password for code-server. |
HASHED_PASSWORD |
alternative | argon2id hash; takes precedence over PASSWORD. |
SUDO_PASSWORD |
no | Sudo password inside the container. Unset = passwordless (recommended for dev). |
TZ |
no | Timezone (e.g., America/New_York). Default UTC. |
CODE_SERVER_ARGS |
no | Extra args appended to the default code-server command. |
ENABLE_SSHD |
no | Start sshd for Remote-SSH access (VS Code Desktop / Cursor / Windsurf). Default false. |
SSH_PORT |
no | Port for sshd (default 2222). |
SSH_AUTHORIZED_KEYS |
no | Newline-separated public keys; merged into authorized_keys on boot. |
NVIDIA_VISIBLE_DEVICES |
no | Standard NVIDIA runtime var. Set to all for all GPUs. |
NVIDIA_DRIVER_CAPABILITIES |
no | Standard NVIDIA runtime var. Typical: compute,utility. |
Generate a hashed password (argon2id):
echo -n "your-password" | argon2 "$(openssl rand -hex 8)" -eRotate a password: change PASSWORD in your compose / k8s / docker-run config, restart the container.
Default variant:
git clone https://github.com/harshpatel333/cuda-code-server
cd cuda-code-server
docker build \
--build-arg CUDA_VERSION=12.6.1 \
--build-arg PYTHON_VERSION=3.12 \
--build-arg TORCH_CUDA=cu126 \
-t cuda-code-server:local .Expect ~15–30 min on a cold cache, ~5–10 min warm. Final image is ~14 GB uncompressed.
Without the Claude Code CLI:
docker build --build-arg INSTALL_CLAUDE_CODE=false -t cuda-code-server:local-noclaude .Other matrix cells: set CUDA_VERSION=12.4.1 TORCH_CUDA=cu124 for the older-CUDA variants, PYTHON_VERSION=3.11 for the older-Python variants.
In the integrated terminal:
nvidia-smi
python -c "import torch; print(torch.cuda.is_available(), torch.cuda.device_count())"
python -c "import torch; x = torch.randn(1000, 1000).cuda(); print((x @ x).sum().item())"All three should succeed. If nvidia-smi works on the host but not in the container, the NVIDIA Container Toolkit is missing or misconfigured on the host. On Ubuntu:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -fsSL https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart dockerCanonical reference: NVIDIA Container Toolkit install guide.
The NVIDIA Container Toolkit isn't installed or configured on the host. See Verifying GPU access above.
You're running a mismatched CUDA-and-torch pair. This image pairs 12.6.1 with cu126 wheels, 12.4.1 with cu124. If you installed torch yourself over the bundled venv, reinstall with the correct index: uv pip install --index-url https://download.pytorch.org/whl/cu126 --force-reinstall torch.
The entrypoint normally fixes this automatically by aligning the in-container docker group GID with the host socket's GID. If it didn't (e.g., you overrode USER in compose), open a fresh terminal — or run sudo groupmod -g "$(stat -c '%g' /var/run/docker.sock)" docker && newgrp docker.
For reproducible deployments, pin a tag with version + variant, e.g., 0.1-12.6-py312. For a dev box that tracks updates, latest (or just the variant, 12.6-py312) is fine.
Yes — with different persistence guarantees:
-
VS Code extensions — install from the in-browser marketplace; they persist on the
homevolume. -
Python packages —
uv pip install <pkg>inside the venv (persistent), or create a project-specific venv withuv venvin/home/coder/project. -
System packages —
sudo apt install <pkg>works but is not persistent (root filesystem is ephemeral). For persistent system-level changes, extend this image:FROM ghcr.io/harshpatel333/cuda-code-server:12.6-py312 USER root RUN apt-get update && apt-get install -y ffmpeg libgl1 && rm -rf /var/lib/apt/lists/* USER coder
Only with a strong random password and an auth-aware reverse proxy (or a VPN / IP allowlist). code-server with a weak password exposed on the internet is effectively remote code execution. See SECURITY.md.
Pull the new tag, recreate the container with the same volume mounts. Persistent volumes (home, project) survive. Breaking changes are called out per-version in CHANGELOG.md.
Build your own variant with --build-arg INSTALL_CLAUDE_CODE=false, or run sudo npm uninstall -g @anthropic-ai/claude-code at runtime (non-persistent — reinstalled on the next image pull).
The image bundles openssh-server. Set ENABLE_SSHD=true + SSH_AUTHORIZED_KEYS="$(cat ~/.ssh/id_ed25519.pub)", expose port 2222, then use your IDE's Remote-SSH extension. Full guide: docs/remote-ssh.md.
See CONTRIBUTING.md. Short version: bug fixes and small additions are welcome; project-specific tooling (R, Julia, particular Jupyter kernels) belongs in a child Dockerfile rather than the base image.
Security issues: private email per SECURITY.md, not GitHub issues.
- code-server — the browser-based VS Code this image wraps.
- Dokploy — the self-hosted PaaS this image targets as primary deploy.
- NVIDIA Container Toolkit — makes
--gpus allwork. uv— the Python package manager baked in.- Dev Containers spec — related specification for VS Code containerised development.
MIT. Copyright © 2026 Harsh Patel.
Built on top of code-server, NVIDIA CUDA base images, and the broader NVIDIA Container Toolkit ecosystem. Dokploy deployment patterns draw on community templates from the Dokploy Discord.
Keywords: GPU Docker container, CUDA code-server, VS Code GPU, remote ML development, Dokploy GPU, self-hosted development environment, PyTorch Docker, cuDNN code-server, browser IDE GPU, CUDA development container, Claude Code Docker, uv Docker, NVIDIA Container Toolkit, code-server GPU, self-hosted VS Code, devcontainer GPU.