🐳 Expose GPU in devenv (#12726)

* ✨ Add GPU support to the devenv

AI-assisted-by: gpt-6.1-sol

* 📚 Fix broken link in dev docs
This commit is contained in:
Miguel de Benito Delgado 2026-10-30 15:13:29 +01:00 committed by GitHub
parent 94836cb8c8
commit 723267cfdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 96 additions and 5 deletions

View File

@ -431,6 +431,8 @@ RUN set -ex; \
fontforge \
\
xvfb \
xauth \
libvulkan1 \
fonts-noto-color-emoji \
fonts-unifont \
libfontconfig1 \

View File

@ -0,0 +1,14 @@
services:
main:
environment:
- NVIDIA_DRIVER_CAPABILITIES=all
- PENPOT_GPU=1
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
volumes:
- /usr/share/vulkan/icd.d:/usr/share/vulkan/icd.d:ro

View File

@ -12,7 +12,7 @@ in order to correctly set up the development environment.
You can [look here][1] for complete instructions.
[1]: /technical-guide/getting-started/#install-with-docker
[1]: /technical-guide/getting-started/docker
Optionally, to improve performance, you can also increase the maximum number of
@ -119,8 +119,9 @@ Container-internal ports stay fixed. Target a specific instance with
accepts a **non-negative integer only** — `--ws main` or `--ws ws1` is
rejected, keeping the flag shape uniform across commands. `run-devenv` is
ws0-only and takes no workspace flag. `run-devenv` also accepts
`--serena-context CTX` (used together with `--agentic`) and `--git-user-name NAME` / `--git-user-email
EMAIL` (see below).
`--serena-context CTX` (used together with `--agentic`), `--gpu` to enable
hardware rendering in Chromium (e.g. for benchmarks), and `--git-user-name NAME`
/ `--git-user-email EMAIL` (see below).
Configuration lives in one tracked file, `docker/devenv/defaults.env` (the
ws0 baseline); `ws1+` values (offset ports, `wsN` container/volume names) are
@ -166,6 +167,49 @@ container (same UID mapping as the source tree). Opencode's own state
(sessions, `auth.json`) lives in `~/.local/share/opencode`, which stays in
the container's data volume regardless of this flag.
### NVIDIA GPU access
On Linux, `run-devenv --gpu` grants the selected container access to NVIDIA
GPUs. The host needs an NVIDIA driver, NVIDIA Container Toolkit configured
with a Docker runtime named `nvidia`, and a readable Vulkan driver manifest
at `/usr/share/vulkan/icd.d/nvidia_icd.json`. The option checks the runtime
and manifest before provisioning.
Build an image from this checkout to include `xauth` and the Vulkan loader
(`libvulkan1`), then use the same tag when starting the container:
```bash
./manage.sh build-devenv --tag gpu-local
DEVENV_TAG=gpu-local ./manage.sh run-devenv --gpu
```
`--gpu` works with `--ws N`, `--agentic`, `--attach`, and
`--opencode-config-dir DIR`. For example:
```bash
DEVENV_TAG=gpu-local ./manage.sh run-devenv --gpu --ws 1 --agentic
```
The GPU overlay requests all NVIDIA devices and driver capabilities and
mounts the host Vulkan manifest directory read-only. Processes inside the
container inherit `PENPOT_GPU=1` and `NVIDIA_DRIVER_CAPABILITIES=all`.
`PENPOT_GPU` signals GPU access to applications. The explicit `--gpu` flag
selects the overlay.
GPU mode applies at container creation and lasts for that container's
lifetime. To change it, stop the selected instance and run it again with
or without `--gpu`:
```bash
./manage.sh stop-devenv --ws 1
DEVENV_TAG=gpu-local ./manage.sh run-devenv --ws 1
```
Run browser commands inside devenv. The Chromium ANGLE Vulkan backend
needs an X display in this setup, so run those commands under
`xvfb-run -a`. Each caller starts its own Xvfb display. Applications choose
their rendering backend.
### Shared state and workers
All instances share one Penpot database and one RustFS bucket; users, teams,
@ -385,7 +429,7 @@ Warning: by default Cursor won't support HTTPS with a self-signed certificate. I
An example of your cursor configuration can be:
```javascript
```json
{
"mcpServers": {
"penpot-devenv": {

View File

@ -236,6 +236,24 @@ function infra-compose {
"$@"
}
function check-devenv-gpu-support {
local runtimes
if ! runtimes=$(env -i HOME="$HOME" PATH="$PATH" PWD="$PWD" \
docker info --format '{{range $name, $_ := .Runtimes}}{{$name}} {{end}}'); then
echo "run-devenv: cannot query Docker runtimes; check that Docker is running and accessible." >&2
return 1
fi
if [[ " $runtimes " != *" nvidia "* ]]; then
echo "run-devenv: --gpu requires Docker runtime 'nvidia'; install and configure NVIDIA Container Toolkit." >&2
return 1
fi
local icd=/usr/share/vulkan/icd.d/nvidia_icd.json
if [[ ! -f "$icd" || ! -r "$icd" ]]; then
echo "run-devenv: --gpu requires a readable $icd; install the host NVIDIA Vulkan driver." >&2
return 1
fi
}
function instance-compose {
local instance="$1"; shift
local source_path
@ -258,6 +276,10 @@ function instance-compose {
compose_files+=(-f docker/devenv/docker-compose.opencode.yml)
opencode_env=("PENPOT_OPENCODE_CONFIG_DIR=${PENPOT_OPENCODE_CONFIG_DIR}")
fi
# Bash dynamic scope exposes run-devenv's local option here.
if [[ "${devenv_gpu:-false}" == "true" ]]; then
compose_files+=(-f docker/devenv/docker-compose.gpu.yml)
fi
env -i HOME="$HOME" PATH="$PATH" PWD="$PWD" \
CURRENT_USER_ID="${CURRENT_USER_ID:-$(id -u)}" \
@ -875,6 +897,7 @@ function run-devenv {
local do_sync=false
local do_attach=false
local agentic=false
local devenv_gpu=false
local serena_context="desktop-app"
local git_user_name=""
local git_user_email=""
@ -889,6 +912,8 @@ function run-devenv {
do_sync=true; shift;;
--agentic)
agentic=true; shift;;
--gpu)
devenv_gpu=true; shift;;
--attach)
do_attach=true; shift;;
--serena-context)
@ -904,12 +929,13 @@ function run-devenv {
-e*)
extra_env_args+=(-e "${1#-e}"); shift;;
-h|--help)
echo "Usage: run-devenv [--ws N] [--sync] [--attach] [--agentic] [--serena-context CTX] [--opencode-config-dir DIR] [--git-user-name NAME] [--git-user-email EMAIL] [-e KEY=VAL]"
echo "Usage: run-devenv [--ws N] [--sync] [--attach] [--agentic] [--gpu] [--serena-context CTX] [--opencode-config-dir DIR] [--git-user-name NAME] [--git-user-email EMAIL] [-e KEY=VAL]"
echo " Bring a single workspace up."
echo " --ws N target workspace (default: 0)."
echo " --sync re-seed the wsN clone from the live repo (forbidden on ws0)."
echo " --attach attach to the tmux session after startup."
echo " --agentic enable MCP + Serena (AI-agent mode)."
echo " --gpu grant NVIDIA GPU access at container creation."
echo " --serena-context CTX context passed to Serena (default: desktop-app)."
echo " --opencode-config-dir DIR bind-mount DIR at ~/.config/opencode inside the"
echo " container (personal agents/prompts/skills kept in a"
@ -963,6 +989,10 @@ function run-devenv {
return 1
fi
if [[ "$devenv_gpu" == "true" ]]; then
check-devenv-gpu-support || return 1
fi
pull-devenv-if-not-exists
ensure-devenv-network
ensure-infra-up
@ -1485,6 +1515,7 @@ function usage {
echo " start of a wsN with no on-disk workspace yet)."
echo " --attach attach to the tmux session after startup."
echo " --agentic enable MCP + Serena (AI-agent mode)."
echo " --gpu grant NVIDIA GPU access at container creation."
echo " --serena-context CTX passed to Serena (default: desktop-app)."
echo " --opencode-config-dir DIR"
echo " bind-mount DIR over the container's"