GPU guide

Run ComfyUI in Docker on an Ubuntu GPU server

Build your own ComfyUI Docker image from the official install steps, give it the GPU, keep models on the VM's disk and reach it through an SSH tunnel.

Faiz Ahmed10 min read

The reliable way to run ComfyUI in Docker is to build your own image, because ComfyUI does not publish an official one. Start from a Python base image, clone a pinned ComfyUI release, install PyTorch from the cu130 index as ComfyUI's README says, and start ComfyUI on 0.0.0.0 inside the container while Docker publishes it on 127.0.0.1 of the VM only. On QuantaCloud that runs on the Bare Metal template, which comes with the NVIDIA driver and Docker, and you open ComfyUI through an SSH tunnel. Models, inputs and outputs live in folders on the VM's disk mounted into the container, so a rebuild keeps them, and stopping the VM deletes them with everything else.

When your own image beats the ComfyUI template#

The honest answer is that most people should start with QuantaCloud's ComfyUI template, which already runs ComfyUI in a container on the VM and opens it behind your QuantaCloud login. Running ComfyUI on a cloud GPU covers it. The template's image is not pinned to a ComfyUI release, though, and you cannot change what is inside it. Build your own image when the ComfyUI version, the PyTorch build or the custom nodes have to be exact, or when the same image has to run on your own machines too.

ComfyUI's docs are blunt about the alternative: there is no official Docker image, and the community images on Docker Hub are "not supported by the ComfyUI team". A Dockerfile of your own is about fifteen lines, so I would rather read those than trust someone else's.

Prepare the Bare Metal VM#

The Bare Metal template gives you Ubuntu 22.04 with the NVIDIA driver and Docker, and you log in as ubuntu. An RTX A6000 is plenty for most image workflows, and ComfyUI GPU requirements covers the models that need more.

Launch an Ubuntu GPU VM on an RTX A6000
  1. Connect over SSH and check the driver. The CUDA Version in the header has to be 13.0 or higher, because the cu130 PyTorch build that ComfyUI requires needs driver R580 or newer.
Terminal
ssh ubuntu@<instance-ip>
nvidia-smi
  1. Check Docker and the Compose plugin.
Terminal
sudo docker version
sudo docker compose version
  1. Check that containers can see the GPU. This is NVIDIA's own Container Toolkit test, and it should print the same table as nvidia-smi on the host.
Terminal
sudo docker run --rm --gpus all ubuntu nvidia-smi

If Docker answers could not select device driver "" with capabilities: [[gpu]], install the NVIDIA Container Toolkit and point Docker at it:

Terminal
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L 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 docker

Running Docker with a GPU explains what the toolkit does.

A Dockerfile from the official install steps#

The Dockerfile below is ComfyUI's manual install, written down: clone a release tag, install PyTorch from the cu130 index with --extra-index-url, then install requirements.txt. I pin the base image, the ComfyUI release and PyTorch, the three parts that decide whether ComfyUI starts on the GPU, and let the release's own requirements.txt bring the rest. The PyTorch pins are the newest cu130 builds on 2026-09-28, and torchvision 0.29.0 only installs next to torch 2.14.0. Make a folder for the project and its data:

Terminal
mkdir -p ~/comfy-docker/data/{models,input,output,user}
cd ~/comfy-docker
echo data > .dockerignore

The .dockerignore line keeps the data folder out of the build context, so a rebuild never sends your models to the builder.

Save this as Dockerfile in ~/comfy-docker:

Dockerfile
FROM python:3.13.15-slim-trixie

ARG COMFYUI_VERSION=v0.37.0

RUN apt-get update \
 && apt-get install -y --no-install-recommends git \
 && rm -rf /var/lib/apt/lists/*

RUN git clone --depth 1 --branch "$COMFYUI_VERSION" https://github.com/Comfy-Org/ComfyUI.git /opt/ComfyUI
WORKDIR /opt/ComfyUI

RUN pip install --no-cache-dir torch==2.14.0 torchvision==0.29.0 torchaudio==2.11.0 \
      --extra-index-url https://download.pytorch.org/whl/cu130 \
 && pip install --no-cache-dir -r requirements.txt

EXPOSE 8188
CMD ["python", "main.py", "--listen", "0.0.0.0", "--port", "8188"]

Three choices in it deserve a sentence each. Python 3.13 is the version ComfyUI's docs recommend, and every package in requirements.txt ships a ready-made wheel for it, so the slim image needs no compiler. The PyTorch wheels bring their own CUDA libraries, and the Container Toolkit adds the driver from the VM at run time, so no CUDA base image is needed. ComfyUI listens on 0.0.0.0 because a published port reaches the container on its own network interface, which ComfyUI's default of 127.0.0.1 would ignore. I pass 0.0.0.0 rather than a bare --listen, which means 0.0.0.0,:: and adds IPv6 that the container does not need.

Run it with Docker Compose#

Compose keeps the port, the folders and the GPU in one file that you can commit next to the Dockerfile. Save this as compose.yaml in the same folder:

YAML
services:
  comfyui:
    build: .
    image: comfyui:v0.37.0
    ports:
      - "127.0.0.1:8188:8188"
    volumes:
      - ./data/models:/opt/ComfyUI/models
      - ./data/input:/opt/ComfyUI/input
      - ./data/output:/opt/ComfyUI/output
      - ./data/user:/opt/ComfyUI/user
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: all
              capabilities: [gpu]
    restart: unless-stopped

Build the image, start the container and follow its log:

Terminal
sudo docker compose up -d --build
sudo docker compose logs -f

Wait for To see the GUI go to: http://0.0.0.0:8188 in the log, then press Ctrl+C to leave it, and check that ComfyUI answers on the VM:

Terminal
curl -s http://127.0.0.1:8188/system_stats | head -c 300

Then confirm that PyTorch inside the container sees the GPU:

Terminal
sudo docker compose exec comfyui python -c "import torch; print(torch.__version__, torch.cuda.is_available(), torch.cuda.get_device_name(0))"

It should print 2.14.0+cu130 True and the name of the GPU. The ComfyUI log also says DynamicVRAM support detected and enabled, which is how ComfyUI v0.37.0 offloads weights to system RAM when a model does not fit.

Without Compose, the same container is one docker run:

Terminal
sudo docker build -t comfyui:v0.37.0 .
sudo docker run -d --name comfyui --gpus all --restart unless-stopped \
  -p 127.0.0.1:8188:8188 \
  -v "$HOME/comfy-docker/data/models:/opt/ComfyUI/models" \
  -v "$HOME/comfy-docker/data/input:/opt/ComfyUI/input" \
  -v "$HOME/comfy-docker/data/output:/opt/ComfyUI/output" \
  -v "$HOME/comfy-docker/data/user:/opt/ComfyUI/user" \
  comfyui:v0.37.0

Keep port 8188 on 127.0.0.1 and use a tunnel#

The one thing I always check is the port mapping, because ComfyUI has no authentication: anyone who reaches port 8188 can queue jobs, read your outputs and, through the Manager, install custom nodes. 127.0.0.1:8188:8188 publishes the port on the VM's loopback only. A plain 8188:8188 publishes it on every interface, and Docker's rules bypass ufw, so a firewall rule will not save you. Docker releases older than 28.0.0 also let hosts on the same network segment reach ports published to 127.0.0.1, which is why the version check above matters.

From your own machine, open the tunnel and browse to http://127.0.0.1:8188:

Terminal
ssh -N -L 8188:127.0.0.1:8188 ubuntu@<instance-ip>

-L forwards port 8188 on your machine to 127.0.0.1:8188 on the VM, and -N opens no shell. Connecting to a cloud GPU covers keys, SSH config and VS Code, and the ComfyUI API guide drives the same tunnel from Python.

Put models in the mounted folders#

The models folder in the container is ~/comfy-docker/data/models on the VM, so you download straight into it with the usual subfolders: checkpoints, diffusion_models, text_encoders, vae and loras. ComfyUI reads the files where they land, and there is nothing to copy into the container. For a single file:

Terminal
mkdir -p ~/comfy-docker/data/models/checkpoints
curl -fL -o ~/comfy-docker/data/models/checkpoints/sd_xl_base_1.0.safetensors \
  https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors

Press r in ComfyUI so the loader nodes list the new file. For a whole set, loading models on a new instance has a script and a model list that take MODELS=~/comfy-docker/data/models as their target.

The folders outlive a rebuild of the image or the container, but not the VM. Stopping a QuantaCloud instance terminates it and deletes its disk, image and data folders included, and there are no volumes or snapshots to keep them. Copy your outputs off before you stop, from your own machine:

Terminal
rsync -avP ubuntu@<instance-ip>:comfy-docker/data/output/ ./comfy-output/
rsync -avP ubuntu@<instance-ip>:comfy-docker/data/user/default/workflows/ ./comfy-workflows/

ComfyUI runs as root in the container, so the files it writes are owned by root on the VM. You can still read and copy them as ubuntu, and deleting them takes sudo. Moving files to and from a GPU server covers rsync, scp and rclone.

Bake custom nodes into the image#

Custom nodes belong in the image, not in a mounted folder, because their Python packages install into the image too. Clone each node at a fixed commit and install its requirements in the Dockerfile, before the EXPOSE line, with the installed PyTorch versions as a pip constraint so a node cannot swap your cu130 build. This example adds the Ollama nodes used in ComfyUI with Ollama:

Dockerfile
RUN git clone https://github.com/stavsap/comfyui-ollama.git custom_nodes/comfyui-ollama \
 && git -C custom_nodes/comfyui-ollama checkout 6db7560576e5a59488708e6be13e07b5aba2432a \
 && pip freeze | grep -E '^(torch|torchvision|torchaudio)==' > /tmp/torch-pins.txt \
 && pip install --no-cache-dir -r custom_nodes/comfyui-ollama/requirements.txt -c /tmp/torch-pins.txt

A custom node is Python code that runs with the container's permissions, and its requirements.txt decides what else pip installs, so read both before you add a node, then rebuild with sudo docker compose up -d --build. ComfyUI-Manager is optional here. Add pip install --no-cache-dir -r manager_requirements.txt and --enable-manager to use it, but inside a container ComfyUI listens on 0.0.0.0, so the Manager applies its stricter rules for servers reachable from other machines, even though Docker publishes the port on 127.0.0.1 only. Installing ComfyUI-Manager and custom nodes explains those rules and how to pin node versions.

When something goes wrong#

could not select device driver "" with capabilities: [[gpu]] means Docker has no GPU runtime. Install the NVIDIA Container Toolkit as in the preparation steps and restart Docker.

torch.cuda.is_available() prints False, or the log says the driver is too old. The cu130 build needs driver R580 or newer, and nvidia-smi on the host shows which one the VM has. On an older driver, use the ComfyUI template instead, because ComfyUI's README requires a cu130 build on this generation of GPUs.

The browser cannot connect through the tunnel. Check sudo docker compose ps for the container's state and curl http://127.0.0.1:8188/system_stats on the VM. If that works, the tunnel command or its port is the problem.

A loader node does not list your file. The file is in the wrong subfolder of data/models, or ComfyUI has not rescanned yet, so press r.

port is already allocated or address already in use on 8188. Another ComfyUI already holds the port, often the container you started with docker run before switching to Compose. Remove it with sudo docker rm -f comfyui, or publish a different port such as 127.0.0.1:8189:8188.

Frequently asked questions#

Is there an official ComfyUI Docker image?

No. ComfyUI's docs say it does not provide one, and that community images are not supported by the ComfyUI team. Building from the official install steps is the way to know what is inside.

Why does ComfyUI listen on 0.0.0.0 in the container?

Docker delivers a published port to the container's own network interface, not to its loopback. ComfyUI's default of 127.0.0.1 would never see that traffic, so the container listens on 0.0.0.0 and Docker decides who can reach it with the 127.0.0.1: mapping.

Do my models survive a rebuild?

Yes, while the VM runs, because they live in ~/comfy-docker/data on the VM's disk. Stopping the instance deletes that disk, so keep your Dockerfile, compose.yaml and model list in Git and rebuild on the next launch.

Can I use my own image as a QuantaCloud template?

No. The console has no custom-image template, and the four templates are fixed. Run your image on the Bare Metal template, which comes with Docker, as this page does.


My rule for ComfyUI in Docker: the template when you want ComfyUI in a browser within minutes, and your own pinned image when the version or the node set has to be exact. Either way, keep port 8188 on 127.0.0.1, reach it through the tunnel, and copy outputs off before you stop. The GPU VPS page covers the Ubuntu VM underneath, and pricing has the billing rules.

Launch an Ubuntu GPU VM

Keep building

Choose your next step.