在 Debian12 上使用 Docker 标准容器化部署 ComfyUI

本文针对配备 NVIDIA GPU 的 Debian12 服务器,提供完整的 ComfyUI Docker 部署方案:包括 Docker 和 CUDA(CUDA 13.1)安装步骤、daemon.json 的代理与 NVIDIA runtime 配置、基于 Dockerfile 与 compose.yaml 的容器设计、容器内安装与持久化策略,以及用于定期备份 ComfyUI 数据与容器环境的稳健脚本示例。
2026-01-11
8486 字 · 约 22 分钟阅读

ComfyUI 是一个基于节点的图形化界面,用于构建和运行深度学习模型,特别是在生成式 AI 领域。

通过 Docker 容器化部署 ComfyUI 可以简化安装和管理过程,同时确保环境的一致性,利于备份和回滚。

由于 ComfyUI 特性,各个节点都会单独安装第三方库,所以更新节点会导致环境不一致从而影响整体运行,利用 Docker 的备份机制就可以很好的回避这一点。

此次我的服务器使用的是 NVIDIA GPU,因此在安装过程中会涉及到 CUDA 的配置,配置如下:

  • CPU:Intel Core i9-14900KF
  • 显卡:NVIDIA RTX 4090 48G 版
  • 内存:128GB
  • 系统:Debian12

1. 准备

1.1. Docker 安装

Debian12 安装 Docker 如下:

# Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
sudo tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Signed-By: /etc/apt/keyrings/docker.asc
EOF

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker

测试安装:

sudo docker run hello-world

编辑 /etc/docker/daemon.json 文件,添加以下代理、ipv6 禁用以及 cuda 配置(代理可选):

{
    "proxies": {
        "http-proxy": "http://192.168.1.1:1080",
        "https-proxy": "http://192.168.1.1:1080",
        "no-proxy": "*.test.example.com,.example.org"
    },
    "ipv6": false,
    "runtimes": {
        "nvidia": {
            "args": [],
            "path": "nvidia-container-runtime"
        }
    }
}

重启 Docker 服务已生效:

sudo systemctl daemon-reload
sudo systemctl restart docker

1.2. CUDA 安装

访问 [NVIDIA CUDA Toolkit Download](https://developer.nvidia.com/cuda-downloads 下载对应版本的 CUDA 安装包,安装过程请参考官网说明。

下面是在 debian12 上安装 CUDA 13.1 的步骤:

wget https://developer.download.nvidia.com/compute/cuda/13.1.0/local_installers/cuda-repo-debian12-13-1-local_13.1.0-590.44.01-1_amd64.deb
sudo dpkg -i cuda-repo-debian12-13-1-local_13.1.0-590.44.01-1_amd64.deb
sudo cp /var/cuda-repo-debian12-13-1-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda-toolkit-13-1
apt-get install -y nvidia-container-toolkit

在安装完成后,验证 CUDA 是否安装成功:

nvcc -V
nvidia-smi

验证 Docker 是否可以使用 GPU:

docker run --rm --gpus all pytorch/pytorch:2.1.0-cuda11.8-cudnn8-runtime nvidia-smi

2. ComfyUI

2.1. 架构设计

ComfyUI 的 Docker 容器设计考虑到模型较大,以及更新和备份的需求,其主目录采用了以下目录结构:

$ tree -L 1
.
├── compose.yaml
├── Dockerfile
├── etc
├── logs
├── root
└── supervisor

compose.yaml 内容如下:

services:
  comfyui:
    container_name: comfyui
    hostname: 4090-comfyui
    build: 
      context: .
      args:
        http_proxy: ${PROXIES}
        https_proxy: ${PROXIES}
    image: comfyui:latest
    restart: unless-stopped
    ports:
      - "8188:8188" # ComfyUI 端口映射
      - "2222:22" # SSH 端口映射
    environment:
      - TZ=Asia/Shanghai
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
      - SAGEATTN_FORCE_SM90=1
      - http_proxy=${PROXIES}
      - https_proxy=${PROXIES}
      - HTTP_PROXY=${PROXIES}
      - HTTPS_PROXY=${PROXIES}
    runtime: nvidia
    volumes:
      - /mnt/sda/comfyui/comfyui:/comfy-ui
      - /mnt/sda/comfyui/models:/comfy-ui/models
      - /mnt/sda/comfyui/truetype:/usr/share/fonts/truetype
      - ./root:/root
      - ./etc:/etc
      - ./logs:/var/log/supervisor
      - ./supervisor:/etc/supervisor/conf.d
    stdin_open: true
    tty: true

解释:

  • .env: 用于存放环境变量,如代理设置 PROXIES
  • 2222 和 8188 端口映射: 2222 用于 SSH 连接,8188 用于 ComfyUI 的 Web 界面访问。
  • 卷挂载: 将主机的 /mnt/sda/comfyui/comfyui 目录挂载到容器的 /comfy-ui,用于存放 ComfyUI 的核心代码文件。/mnt/sda/comfyui/models 用于存放模型文件,方便模型的管理和更新。/mnt/sda/comfyui/truetype 用于存放字体文件,确保 ComfyUI 能够正确渲染文本。
  • supervisor 和 logs 文件夹: 因 Docker 容器本身缺少 systemd,使用 supervisor 用于管理容器内的进程,实现日志管理、进程监控等功能。
  • root 和 etc 文件夹: 用于存放用户和系统的配置文件,确保容器内的环境配置符合需求。

Dockerfile 文件如下:

FROM nvcr.io/nvidia/pytorch:25.01-py3

ENV TZ=Asia/Shanghai

# ==== 1) 基础依赖 ====
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
      python3 python3-venv python3-pip \
      build-essential git wget curl ca-certificates \
      openssh-server supervisor \
      ffmpeg zsh libpython3.12\
      libgl1 libglib2.0-0 libsm6 libxrender1 libxext6 \
      && rm -rf /var/lib/apt/lists/*

# 让 python/pip 指向 3.x
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3 1 && \
    update-alternatives --install /usr/bin/pip pip /usr/bin/pip3 1

# ==== 2) 工作区 ====
WORKDIR /workspace

# ==== 3) (可选)SageAttention 示例:需要时解开注释即可 ====
# ARG SAGEATTENTION_BRANCH=main
# RUN git clone --depth 1 -b ${SAGEATTENTION_BRANCH} https://github.com/thu-ml/SageAttention.git && \
#     cd SageAttention && uv pip install -v .

RUN mkdir -p /run/sshd && \
    chmod 0755 /run/sshd

CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisord.conf"]

supervisor 文件夹内有配置文件 sshd.conf 内容如下:

[program:sshd]
command=/usr/sbin/sshd -D
autostart=true
autorestart=true
stdout_logfile=/var/log/supervisor/%(program_name)s.log
stdout_logfile_maxbytes=16MB
redirect_stderr=true
user=root

2.2. 构建镜像

在包含 Dockerfilecompose.yaml 的目录下,运行以下命令构建并启动容器:

docker compose build

如不出意外,容器可以正常构建成功,接着临时运行容器,并提取出需要的 etc 以及 root 配置文件:

docker run --rm --name tmp comfyui:latest sleep 15s &
docker cp tmp:/etc ./etc
docker cp tmp:/root ./root

接着启动容器

docker compose up -d

2.3. 完善 ComfyUI 容器

进入容器,安装 ComfyUI 以及常用节点:

docker exec -it comfyui /bin/bash

修改 ssh 远程连接的密码,允许使用 ssh 接入容器(可选):

passwd root
systemctl restart sshd

安装要用的套件,包括 zsh、 oh-my-zsh、uv等:

# 安装常用工具
apt update && apt install -y zsh
apt install -y vim wget git curl htop gcc make openssl p7zip-full unzip  

# 安装 oh-my-zsh
sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

# 安装 uv
curl -LsSf https://astral.sh/uv/install.sh | sh

因为 root 目录已经挂载到主机,所以以上配置会被保存下来,即使容器重建也不会丢失。

接着来到 ComfyUI 目录,安装 ComfyUI 需要的运行环境以及常用节点:

cd /comfy-ui
uv venv .venv
uv pip install -r requirements.txt  

试运行:

python main.py

2.4. 备份

对于服务器而言,结合 ZFS 对 ComfyUI 目录进行快照是一个不错的选择,另外也可以使用下面的脚本进行定期备份。

备份只需备份两处位置:

  • 容器环境:/docker/comfyui
  • 代码环境:/mnt/sda/comfyui

为了方便,用以下脚本结合 crontab 实现自动定期备份,方便还原:

#!/usr/bin/env bash
# backup.sh - 稳健型目录备份脚本(Debian 12)
# 用法:./backup.sh <源目录> <备份前缀路径> <保留备份个数>

set -Euo pipefail
IFS=$' \t\n'

err()  { echo "[ERROR] $*" >&2; }
warn() { echo "[WARN ] $*" >&2; }
info() { echo "[INFO ] $*"; }
need_cmd() { command -v "$1" >/dev/null 2>&1 || { err "需要命令:$1"; exit 127; }; }

need_cmd tar; need_cmd gzip; need_cmd date; need_cmd du; need_cmd find; need_cmd sort; need_cmd awk
need_cmd mktemp; need_cmd hostname; need_cmd curl; need_cmd stat

# ===== 参数 =====
if [[ $# -ne 3 ]]; then
  err "用法:$0 <源目录> <备份前缀路径> <保留备份个数>"
  exit 2
fi
SRC_DIR="$1"
DST_PREFIX="$2"
KEEP="$3"

[[ -d "$SRC_DIR" ]] || { err "源目录不存在:$SRC_DIR"; exit 2; }

DST_DIR="$(dirname "$DST_PREFIX")"
FILE_PREFIX="$(basename "$DST_PREFIX")"

mkdir -p "$DST_DIR"

if ! [[ "$KEEP" =~ ^[0-9]+$ ]] || [[ "$KEEP" -lt 1 ]]; then
  err "保留备份个数必须为 >=1 的整数:$KEEP"
  exit 2
fi

HOST="$(hostname -s || echo unknown-host)"
DATE_STR="$(date +%F)"
DATE_TAG="$(date +%Y%m%d)"
START_ISO="$(date -Is)"
SECONDS=0

SRC_BASENAME="$(basename "$SRC_DIR")"

# ===== 文件名规则(按日期命名)=====
ARCHIVE_NAME="${FILE_PREFIX}-${DATE_TAG}.tar.gz"
ARCHIVE_PATH="$DST_DIR/$ARCHIVE_NAME"

# ===== 信息(非致命)=====
SRC_SIZE_HUMAN="$(du -sh --apparent-size "$SRC_DIR" 2>/dev/null | awk '{print $1}')"
FILE_COUNT="$(find "$SRC_DIR" -type f 2>/dev/null | wc -l | awk '{print $1}')"

# ===== 流式压缩(不写 /tmp)=====
info "开始打包:$SRC_DIR -> $ARCHIVE_PATH"

PARENT_DIR="$(dirname "$SRC_DIR")"
BASE_DIR="$(basename "$SRC_DIR")"

pushd "$PARENT_DIR" >/dev/null

tar --xattrs --acls --one-file-system --numeric-owner \
    --warning=no-file-changed --ignore-failed-read \
    -cf - "$BASE_DIR" \
  | gzip -9n > "$ARCHIVE_PATH"

# 立即捕获 PIPESTATUS
declare -a PIPE_STATUS=( "${PIPESTATUS[@]}" )
TAR_RC=${PIPE_STATUS[0]:-1}
GZIP_RC=${PIPE_STATUS[1]:-1}

popd >/dev/null

if [[ $TAR_RC -ne 0 || $GZIP_RC -ne 0 ]]; then
  warn "tar/gzip 流式压缩失败:tar=$TAR_RC gzip=$GZIP_RC"
  rm -f "$ARCHIVE_PATH"
fi

END_ISO="$(date -Is)"
ARCHIVE_SIZE_HUMAN="$(du -h "$ARCHIVE_PATH" | awk '{print $1}')"

# ===== 轮转 =====
info "执行备份轮转(保留 $KEEP 个)..."

readarray -d '' FILES < <(
  find "$DST_DIR" -maxdepth 1 -type f \
    -name "${FILE_PREFIX}-*.tar.gz" \
    -print0 2>/dev/null
)

TOTAL="${#FILES[@]}"

if (( TOTAL == 0 )); then
  info "未发现任何匹配备份。"
else
  TMP_LIST="$(mktemp)"
  for f in "${FILES[@]}"; do
    printf "%s\t%s\n" "$(stat -c %Y "$f" 2>/dev/null || echo 0)" "$f" >> "$TMP_LIST"
  done
  mapfile -t SORTED < <(sort -nr -k1,1 "$TMP_LIST" | awk -F'\t' '{print $2}')
  rm -f "$TMP_LIST"

  info "当前匹配到 $TOTAL 个备份:"
  for f in "${SORTED[@]}"; do
    ts="$(stat -c %y "$f" 2>/dev/null | awk '{print $1" "$2}')"
    printf "  - %s  %s\n" "$ts" "$f"
  done

  if (( TOTAL > KEEP )); then
    for (( i=KEEP; i<TOTAL; i++ )); do
      f="${SORTED[$i]}"
      info "删除旧备份:$f"
      rm -f -- "$f" || warn "删除失败:$f"
    done
  else
    info "无需删除:总数 $TOTAL <= 保留数 $KEEP。"
  fi
fi

info "备份完成。"

留言

发表留言