From b63a4f91870415592989e2b120311f483f4e0ca6 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 14 Jun 2026 00:38:45 +0000 Subject: [PATCH 1/5] =?UTF-8?q?README:=20simplify=20AI=20model=20offline?= =?UTF-8?q?=20download=20=E2=80=94=20remove=20iptables=20option?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace two-option (iptables vs download) with a single clear offline download path using huggingface-cli into ./data/hf_cache. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- README.md | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 86186d9..5914686 100644 --- a/README.md +++ b/README.md @@ -208,32 +208,33 @@ docker compose -f docker-compose.gpu.yml logs | grep -i sam If Docker created `./data/` as root and you can't write there without `sudo`, you can also use root's curl as above — the container reads the file regardless of owner. -**AI Edit returns "model files not yet downloaded" or "Errno -3 / DNS" error** +**AI models not downloading (container DNS blocked)** -The container's DNS is blocked (common on corporate networks or custom iptables rules), so it can't download SDXL models from HuggingFace. Two options: +If the container can't reach HuggingFace (`Errno -3` in logs), download the model files directly on the host — no rebuild needed, the container reads from the same `./data/hf_cache/` folder. -*Option A — fix Docker DNS (recommended, one command):* -```bash -sudo iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT -docker compose -f docker-compose.gpu.yml restart -``` - -*Option B — pre-download models on the host (if iptables fix isn't possible):* ```bash pip install huggingface-hub -# Download the inpainting model (~6.5 GB, needed for AI Edit): +# Inpainting model (~6.5 GB) — needed for AI Edit, Make less symmetrical, etc. huggingface-cli download diffusers/stable-diffusion-xl-1.0-inpainting-0.1 \ --cache-dir ./data/hf_cache \ --exclude "*.msgpack" "flax_*" "tf_*" -# Download the text-to-image model (~6.5 GB, needed for Text → Image): +# Text-to-image model (~6.5 GB) — needed for Text → Image huggingface-cli download stabilityai/stable-diffusion-xl-base-1.0 \ --cache-dir ./data/hf_cache \ --exclude "*.msgpack" "flax_*" "tf_*" ``` -The models land in `./data/hf_cache/` which is bind-mounted into the container — no rebuild needed. Restart the container and the first AI Edit request loads from local disk. +Once both downloads finish, restart the container: +```bash +docker compose -f docker-compose.gpu.yml restart +``` + +The first AI Edit request loads from local disk (10–30 s, not a download). Verify in logs: +```bash +docker compose -f docker-compose.gpu.yml logs -f | grep -E "local_gpu|Cached|failed" +``` **Out of VRAM during generation** - Reduce `LOCAL_GPU_MAX_PIPELINES=1` in `.env` (default 2) From d83f94cc241263df18a72fd6b25c4595b09c1d7d Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 14 Jun 2026 00:53:54 +0000 Subject: [PATCH 2/5] README: use Docker helper container for offline model download Replaces 'pip install huggingface-hub' (breaks on PEP 668 / Debian 12+) with a docker run --rm python:3.11-slim one-liner that downloads directly into ./data/hf_cache without touching host Python packages. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5914686..f445a3f 100644 --- a/README.md +++ b/README.md @@ -210,20 +210,24 @@ If Docker created `./data/` as root and you can't write there without `sudo`, yo **AI models not downloading (container DNS blocked)** -If the container can't reach HuggingFace (`Errno -3` in logs), download the model files directly on the host — no rebuild needed, the container reads from the same `./data/hf_cache/` folder. +If the container can't reach HuggingFace (`Errno -3` in logs), use a lightweight Docker helper container to download the models on your behalf — no host Python packages required. The files land in `./data/hf_cache/` which is bind-mounted into the GPU container. ```bash -pip install huggingface-hub - # Inpainting model (~6.5 GB) — needed for AI Edit, Make less symmetrical, etc. -huggingface-cli download diffusers/stable-diffusion-xl-1.0-inpainting-0.1 \ - --cache-dir ./data/hf_cache \ - --exclude "*.msgpack" "flax_*" "tf_*" +docker run --rm \ + -v "$(pwd)/data/hf_cache:/root/.cache/huggingface" \ + python:3.11-slim \ + bash -c "pip install -q huggingface-hub && \ + huggingface-cli download diffusers/stable-diffusion-xl-1.0-inpainting-0.1 \ + --exclude '*.msgpack' 'flax_*' 'tf_*'" # Text-to-image model (~6.5 GB) — needed for Text → Image -huggingface-cli download stabilityai/stable-diffusion-xl-base-1.0 \ - --cache-dir ./data/hf_cache \ - --exclude "*.msgpack" "flax_*" "tf_*" +docker run --rm \ + -v "$(pwd)/data/hf_cache:/root/.cache/huggingface" \ + python:3.11-slim \ + bash -c "pip install -q huggingface-hub && \ + huggingface-cli download stabilityai/stable-diffusion-xl-base-1.0 \ + --exclude '*.msgpack' 'flax_*' 'tf_*'" ``` Once both downloads finish, restart the container: From ce29cc1d30a1bb0a4e5098a080f91be8f3b114e7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 14 Jun 2026 00:55:09 +0000 Subject: [PATCH 3/5] README: restore iptables as primary DNS fix with isolation clarification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iptables fix is simpler (one command, no 13 GB download) and does not affect container isolation — adds clarifying note so users understand it only restores Docker's default outbound DNS behaviour. Docker helper container kept as fallback option. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- README.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index f445a3f..7a05e13 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,19 @@ If Docker created `./data/` as root and you can't write there without `sudo`, yo **AI models not downloading (container DNS blocked)** -If the container can't reach HuggingFace (`Errno -3` in logs), use a lightweight Docker helper container to download the models on your behalf — no host Python packages required. The files land in `./data/hf_cache/` which is bind-mounted into the GPU container. +If the container can't reach HuggingFace (`Errno -3` in logs), your host firewall is blocking outbound DNS queries from the Docker bridge. The fix below restores Docker's default behaviour — it does **not** affect container isolation (filesystem, network namespace, PID namespace all remain separate): + +```bash +sudo iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT +docker compose -f docker-compose.gpu.yml restart +``` + +The container will now resolve hostnames and download the models automatically (~13 GB on first run, then cached). Watch progress: +```bash +docker compose -f docker-compose.gpu.yml logs -f | grep -E "local_gpu|Cached|failed" +``` + +**If you can't run the iptables command**, download with a Docker helper container instead (no host Python needed): ```bash # Inpainting model (~6.5 GB) — needed for AI Edit, Make less symmetrical, etc. @@ -230,15 +242,7 @@ docker run --rm \ --exclude '*.msgpack' 'flax_*' 'tf_*'" ``` -Once both downloads finish, restart the container: -```bash -docker compose -f docker-compose.gpu.yml restart -``` - -The first AI Edit request loads from local disk (10–30 s, not a download). Verify in logs: -```bash -docker compose -f docker-compose.gpu.yml logs -f | grep -E "local_gpu|Cached|failed" -``` +Then restart: `docker compose -f docker-compose.gpu.yml restart` **Out of VRAM during generation** - Reduce `LOCAL_GPU_MAX_PIPELINES=1` in `.env` (default 2) From 7cfbb6034f5b77627dbe2a54fea2eb7d4e63cf54 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 14 Jun 2026 00:58:43 +0000 Subject: [PATCH 4/5] Add start-gpu.sh: auto-applies iptables DNS fix before container start The iptables DOCKER-USER rule is lost on reboot; the script re-applies it each run, checks for duplicates, and is silently skipped on macOS/WSL. Default behaviour (no args): docker compose up -d --build. All docker compose subcommands can be passed as args (logs, down, etc.). README Quick Start and Updates sections now reference ./start-gpu.sh. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- README.md | 9 +++++---- start-gpu.sh | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) create mode 100755 start-gpu.sh diff --git a/README.md b/README.md index 7a05e13..99ca583 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi # Clone and run: git clone https://github.com/outis1one/editmaskwithai cd editmaskwithai -docker compose -f docker-compose.gpu.yml up --build +chmod +x start-gpu.sh +./start-gpu.sh ``` Open **http://localhost:3080** @@ -52,15 +53,15 @@ Open **http://localhost:3080** ```bash git pull # GPU: -docker compose -f docker-compose.gpu.yml up -d --build -# or cloud: +./start-gpu.sh # applies DNS fix then rebuilds + starts +# or cloud (no GPU): docker compose up -d --build ``` If pip packages seem stale after a pull (e.g., wrong diffusers version), force a pip layer rebuild without re-downloading the entire PyTorch base image: ```bash -BUILDID=$(date +%s) docker compose -f docker-compose.gpu.yml up -d --build +BUILDID=$(date +%s) ./start-gpu.sh --build ``` --- diff --git a/start-gpu.sh b/start-gpu.sh new file mode 100755 index 0000000..bc43b80 --- /dev/null +++ b/start-gpu.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# start-gpu.sh — start the GPU container with Docker DNS fixed. +# +# The iptables rule restores Docker's default outbound DNS behaviour. +# It does NOT affect container isolation (namespaces, filesystems, etc.). +# The rule is lost on reboot, so this script re-applies it each time. +# +# Usage: +# ./start-gpu.sh # start (detached, with build) +# ./start-gpu.sh --build # force rebuild +# ./start-gpu.sh logs -f # tail logs +# ./start-gpu.sh down # stop and remove container + +set -euo pipefail + +# Apply DNS fix on Linux hosts that have iptables. +# Skipped silently on macOS and Windows (WSL without iptables). +if command -v iptables &>/dev/null && command -v sudo &>/dev/null; then + if ! sudo iptables -C DOCKER-USER -p udp --dport 53 -j ACCEPT 2>/dev/null; then + sudo iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT + echo "[start-gpu] Docker DNS fix applied (iptables DOCKER-USER)" + else + echo "[start-gpu] Docker DNS rule already present — skipping" + fi +fi + +# Default: start detached with build. Pass any args to override. +if [ $# -eq 0 ]; then + exec docker compose -f docker-compose.gpu.yml up -d --build +else + exec docker compose -f docker-compose.gpu.yml "$@" +fi From ea77d95ab0fe17fe02b91d5297afea087d5b4ab3 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 14 Jun 2026 01:04:26 +0000 Subject: [PATCH 5/5] Replace start-gpu.sh with install-local-gpu.sh + bring-up-local-gpu.sh install-local-gpu.sh (run once): - Installs nvidia-container-toolkit (Ubuntu/Debian/RHEL auto-detected) - Installs docker-dns-fix.service systemd unit: permanent iptables DNS fix that runs after docker.service on every boot, without touching ufw - Restarts Docker and applies the rule immediately - Verifies GPU is accessible inside Docker bring-up-local-gpu.sh (run each time): - Thin wrapper: docker compose -f docker-compose.gpu.yml up -d --build - Accepts pass-through args (down, logs -f, --no-build, etc.) - BUILDID=$(date +%s) ./bring-up-local-gpu.sh for pip cache bust README Quick Start, Updates, and Troubleshooting updated accordingly. https://claude.ai/code/session_01WVDg7amsy1TTtxvpku7bcM --- README.md | 40 +++++++--------- bring-up-local-gpu.sh | 22 +++++++++ install-local-gpu.sh | 107 ++++++++++++++++++++++++++++++++++++++++++ start-gpu.sh | 32 ------------- 4 files changed, 145 insertions(+), 56 deletions(-) create mode 100755 bring-up-local-gpu.sh create mode 100755 install-local-gpu.sh delete mode 100755 start-gpu.sh diff --git a/README.md b/README.md index 99ca583..158e90c 100644 --- a/README.md +++ b/README.md @@ -7,30 +7,22 @@ A self-hosted, web-based AI photo editor. Paint over any object, describe what y ### GPU machine (recommended — free inference, best quality) ```bash -# Prerequisites: Docker + nvidia-container-toolkit -# Install toolkit once (Ubuntu/Debian): -curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \ - | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-ctk.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-ctk.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 - -# Verify GPU passes through into Docker: -docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi - -# Clone and run: git clone https://github.com/outis1one/editmaskwithai cd editmaskwithai -chmod +x start-gpu.sh -./start-gpu.sh + +# One-time setup: installs nvidia-container-toolkit, configures Docker, +# and sets up a permanent DNS fix so the container can download models. +chmod +x install-local-gpu.sh +./install-local-gpu.sh + +# Start the app (run this each time): +chmod +x bring-up-local-gpu.sh +./bring-up-local-gpu.sh ``` Open **http://localhost:3080** -**First startup downloads the AI model for your GPU (5–20 GB, one time).** Models are cached in a Docker volume and survive rebuilds. +**First startup downloads the AI model for your GPU (~13 GB, one time).** Models are cached in `./data/hf_cache/` and survive rebuilds. --- @@ -53,7 +45,7 @@ Open **http://localhost:3080** ```bash git pull # GPU: -./start-gpu.sh # applies DNS fix then rebuilds + starts +./bring-up-local-gpu.sh # or cloud (no GPU): docker compose up -d --build ``` @@ -61,7 +53,7 @@ docker compose up -d --build If pip packages seem stale after a pull (e.g., wrong diffusers version), force a pip layer rebuild without re-downloading the entire PyTorch base image: ```bash -BUILDID=$(date +%s) ./start-gpu.sh --build +BUILDID=$(date +%s) ./bring-up-local-gpu.sh ``` --- @@ -211,19 +203,19 @@ If Docker created `./data/` as root and you can't write there without `sudo`, yo **AI models not downloading (container DNS blocked)** -If the container can't reach HuggingFace (`Errno -3` in logs), your host firewall is blocking outbound DNS queries from the Docker bridge. The fix below restores Docker's default behaviour — it does **not** affect container isolation (filesystem, network namespace, PID namespace all remain separate): +If you ran `./install-local-gpu.sh`, this is already permanently fixed. Otherwise, the container's host firewall is blocking outbound DNS from the Docker bridge — apply the fix manually (does **not** affect container isolation): ```bash sudo iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT -docker compose -f docker-compose.gpu.yml restart +./bring-up-local-gpu.sh ``` -The container will now resolve hostnames and download the models automatically (~13 GB on first run, then cached). Watch progress: +The container will now resolve hostnames and download models automatically (~13 GB on first run, then cached). Watch progress: ```bash docker compose -f docker-compose.gpu.yml logs -f | grep -E "local_gpu|Cached|failed" ``` -**If you can't run the iptables command**, download with a Docker helper container instead (no host Python needed): +**Alternative: download with a Docker helper container** (no iptables, no host Python needed): ```bash # Inpainting model (~6.5 GB) — needed for AI Edit, Make less symmetrical, etc. diff --git a/bring-up-local-gpu.sh b/bring-up-local-gpu.sh new file mode 100755 index 0000000..7a13cec --- /dev/null +++ b/bring-up-local-gpu.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# bring-up-local-gpu.sh — start the GPU container. +# +# Run this each time you want to start the app. +# Run ./install-local-gpu.sh once first on a new machine. +# +# Usage: +# ./bring-up-local-gpu.sh # start (detached, rebuild if needed) +# ./bring-up-local-gpu.sh --no-build # start without rebuilding +# ./bring-up-local-gpu.sh down # stop and remove container +# ./bring-up-local-gpu.sh logs -f # tail logs +# +# Force pip layer rebuild (e.g. after requirements change): +# BUILDID=$(date +%s) ./bring-up-local-gpu.sh + +set -euo pipefail + +if [ $# -eq 0 ]; then + exec docker compose -f docker-compose.gpu.yml up -d --build +else + exec docker compose -f docker-compose.gpu.yml "$@" +fi diff --git a/install-local-gpu.sh b/install-local-gpu.sh new file mode 100755 index 0000000..814f179 --- /dev/null +++ b/install-local-gpu.sh @@ -0,0 +1,107 @@ +#!/usr/bin/env bash +# install-local-gpu.sh — one-time setup for local GPU inference. +# +# Run this once on a new machine. It: +# 1. Installs the NVIDIA container toolkit (so Docker can use the GPU) +# 2. Installs a systemd service that permanently fixes Docker container DNS +# (allows containers to resolve hostnames — does not touch ufw) +# 3. Restarts Docker so both changes take effect +# 4. Verifies the GPU is accessible inside Docker +# +# After this, use ./bring-up-local-gpu.sh each time to start the app. + +set -euo pipefail + +# ── Must run as root (or via sudo) ─────────────────────────────────────────── +if [ "$EUID" -ne 0 ]; then + exec sudo bash "$0" "$@" +fi + +echo "==================================================" +echo " EditmaskwithAI — Local GPU one-time setup" +echo "==================================================" +echo "" + +# ── 1. NVIDIA container toolkit ────────────────────────────────────────────── +if command -v nvidia-ctk &>/dev/null; then + echo "✓ nvidia-container-toolkit already installed — skipping" +else + echo "Installing nvidia-container-toolkit..." + . /etc/os-release + case "$ID" in + ubuntu|debian) + curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \ + | gpg --dearmor -o /usr/share/keyrings/nvidia-ctk.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-ctk.gpg] https://#g' \ + | tee /etc/apt/sources.list.d/nvidia-container-toolkit.list + apt-get update -qq + apt-get install -y nvidia-container-toolkit + ;; + rhel|fedora|rocky|centos|almalinux) + dnf install -y nvidia-container-toolkit + ;; + *) + echo "⚠ Unrecognised distro ($ID). Install nvidia-container-toolkit manually." + echo " See: https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html" + ;; + esac +fi + +nvidia-ctk runtime configure --runtime=docker + +# ── 2. Permanent Docker DNS fix via systemd ─────────────────────────────────── +# Adds a rule to the DOCKER-USER iptables chain so containers can resolve +# hostnames. Runs after docker.service on every boot. Does NOT touch ufw. +echo "" +echo "Installing docker-dns-fix systemd service..." + +cat > /etc/systemd/system/docker-dns-fix.service << 'EOF' +[Unit] +Description=Allow Docker containers to resolve DNS (DOCKER-USER iptables rule) +After=docker.service +Requires=docker.service +BindsTo=docker.service + +[Service] +Type=oneshot +ExecStart=/bin/sh -c \ + 'iptables -C DOCKER-USER -p udp --dport 53 -j ACCEPT 2>/dev/null || \ + iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT' +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable docker-dns-fix.service +echo "✓ docker-dns-fix.service installed and enabled" + +# ── 3. Restart Docker ───────────────────────────────────────────────────────── +echo "" +echo "Restarting Docker..." +systemctl restart docker +sleep 2 +echo "✓ Docker restarted" + +# ── 4. Apply DNS rule now (don't wait for next boot) ───────────────────────── +systemctl start docker-dns-fix.service +echo "✓ DNS fix applied" + +# ── 5. Verify GPU access ───────────────────────────────────────────────────── +echo "" +echo "Verifying GPU access inside Docker..." +if docker run --rm --gpus all nvidia/cuda:12.1.0-base-ubuntu22.04 nvidia-smi &>/dev/null; then + echo "✓ GPU is accessible inside Docker" +else + echo "⚠ GPU check failed. Is the NVIDIA driver installed on the host?" + echo " Check: nvidia-smi" + echo " Minimum driver version: 525" +fi + +echo "" +echo "==================================================" +echo " Setup complete." +echo " Start the app with: ./bring-up-local-gpu.sh" +echo "==================================================" diff --git a/start-gpu.sh b/start-gpu.sh deleted file mode 100755 index bc43b80..0000000 --- a/start-gpu.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash -# start-gpu.sh — start the GPU container with Docker DNS fixed. -# -# The iptables rule restores Docker's default outbound DNS behaviour. -# It does NOT affect container isolation (namespaces, filesystems, etc.). -# The rule is lost on reboot, so this script re-applies it each time. -# -# Usage: -# ./start-gpu.sh # start (detached, with build) -# ./start-gpu.sh --build # force rebuild -# ./start-gpu.sh logs -f # tail logs -# ./start-gpu.sh down # stop and remove container - -set -euo pipefail - -# Apply DNS fix on Linux hosts that have iptables. -# Skipped silently on macOS and Windows (WSL without iptables). -if command -v iptables &>/dev/null && command -v sudo &>/dev/null; then - if ! sudo iptables -C DOCKER-USER -p udp --dport 53 -j ACCEPT 2>/dev/null; then - sudo iptables -I DOCKER-USER -p udp --dport 53 -j ACCEPT - echo "[start-gpu] Docker DNS fix applied (iptables DOCKER-USER)" - else - echo "[start-gpu] Docker DNS rule already present — skipping" - fi -fi - -# Default: start detached with build. Pass any args to override. -if [ $# -eq 0 ]; then - exec docker compose -f docker-compose.gpu.yml up -d --build -else - exec docker compose -f docker-compose.gpu.yml "$@" -fi