Briefing

DIY Deepfake Mastery: Craft Stealthy Face Swaps at Home for Just $0.24/Hour Using RunPod and ComfyUI

DIY Deepfake Mastery: Craft Stealthy Face Swaps at Home for Just $0.24/Hour Using RunPod and ComfyUI

Buckle up, AI enthusiasts! In the electrifying world of generative tech, deepfakes aren't just Hollywood magic anymore—they're your next DIY project, right from your living room. Imagine swapping faces in stunning images with pinpoint accuracy, all powered by open-source tools and cloud GPUs that cost pennies per hour. We're talking zero upfront hardware investment, total stealth in your setup, and results that rival pro studios. This tutorial dives deep into creating deepfakes using ComfyUI's Reactor node on RunPod, leveraging Stable Diffusion XL (SDXL) for base image generation. Whether you're experimenting with artistic visuals or pushing creative boundaries, we'll guide you through every electrifying step. Get ready to unleash your inner tech wizard!

How to make deepfakes illustration

Step 1: Gear Up with RunPod – Your Cloud Playground Awaits

First things first: you need a RunPod account to rent GPU power on demand. RunPod is a beast for AI workloads, offering scalable pods without the hassle of local rigs. It's free to sign up, and with minimum credits starting at $10 (enough for over 40 hours on our budget setup), it's a steal for stealthy, home-based experiments.

Creating Your RunPod Account

  1. Head to runpod.io and click "Sign Up." Use your email or Google account for instant access.
  2. Verify your email and log in to the dashboard. This is your command center for pods, storage, and billing.
  3. Add credits: Click "Billing" and deposit at least $10 via card or crypto. Pro tip: Enable auto-termination in pod settings to pause idle instances and save every cent—perfect for discreet, on-demand sessions.

Why RunPod? It's optimized for AI, with global data centers for low-latency access, and supports persistent storage to keep your models safe across sessions. No local GPU? No problem—this keeps everything cloud-bound and invisible from your home network.

Deploying Your Minimal Pod: RTX 2000 Ada at $0.24/Hour

Now, launch a pod tailored for ComfyUI. We'll use the RTX 2000 Ada GPU—compact power at a blistering $0.24 per hour, ideal for SDXL inference without breaking the bank.

  1. In the dashboard, navigate to "Pods" > "Deploy."
  2. GPU Selection: Search for "RTX 2000 Ada" and select it. Choose a region close to you (e.g., US East for speed).
  3. Template: From the library, pick "runpod/pytorch:2.8.0-py3.11-cuda12.8.1-cudnn-devel-ubuntu22.04." This pre-loads PyTorch 2.8, CUDA 12.8, and Python 3.11—perfect for ComfyUI's demands. It ensures seamless compatibility with Reactor's face-swapping magic.
  4. Storage: Opt for a 100GB Network Volume (~$7/month persistent storage). Mount it at /workspace to save models and workflows across pod restarts. Skip local disk for true stealth—everything lives in the cloud.
  5. Environment Variables: Leave defaults, but set "Auto-Terminate After Inactivity" to 10 minutes to avoid surprise bills.
  6. Ports: Expose TCP ports 8888 (Jupyter) and 3000 (ComfyUI). This lets you tunnel securely without exposing your IP.
  7. Click "Deploy." Your pod spins up in 1-2 minutes. Note the Pod ID (e.g., "abc123")—you'll need it for connections.

Highlight: At $0.24/hour, a full deepfake session (install + 10 generations) costs under $1. Stealth mode: Pods are ephemeral; terminate anytime, and your data persists only on the volume.

Step 2: Connect via Jupyter – Your Gateway to the Terminal

With the pod running, connect using JupyterLab for a browser-based terminal and file explorer. No SSH hassles—just pure, energetic workflow.

  1. In the RunPod dashboard, select your pod and click "Connect."
  2. Under "HTTP Services," click "Connect to HTTP Service [Port 8888]." This opens Jupyter at https://[PodId]-8888.proxy.runpod.net/lab.
    • Format: Replace [PodId] with your actual ID, e.g., https://abc123-8888.proxy.runpod.net/lab.
    • If prompted for a token, grab it from the pod logs (dashboard > Logs) or run jupyter server list in the web terminal.
  3. Jupyter loads in a new tab. Navigate the file browser to /workspace—your persistent home base.

Pro Tip: Bookmark this URL; it's your stealth portal. Jupyter handles file uploads/downloads seamlessly for input images.

Opening the Terminal in Jupyter

  1. In Jupyter's file browser (left panel), right-click in /workspace and select "New" > "Terminal." A bash shell pops open.
  2. Verify your setup: Run nvidia-smi to confirm the RTX 2000 Ada is active (expect ~16GB VRAM). Then python3 -V for Python 3.11.

You're now in the driver's seat—time to install ComfyUI and supercharge it with Reactor!

Step 3: One-Liner Magic – Install ComfyUI, Reactor, and Models in /workspace

Here's the electrifying core: a single, powerhouse command that clones ComfyUI, sets up a virtual env, installs dependencies, adds the Reactor node, and downloads essentials like SDXL and face models. Paste this into your Jupyter terminal at /workspace and hit Enter. It chains everything for zero-fuss execution—watch the progress fly!

Critical Command (Copy-Paste Exactly):

cd /workspace && git clone https://github.com/comfyanonymous/ComfyUI.git && cd ComfyUI && apt update && apt install -y build-essential && python -m venv venv && source venv/bin/activate && pip install --upgrade pip && pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu128 && pip install -r requirements.txt && cd custom_nodes && git clone https://github.com/Gourieff/comfyui-reactor-node.git && cd comfyui-reactor-node && pip install insightface==0.7.3 && python install.py && mkdir -p ../../models/insightface/models/antelopev2 && wget -O ../../models/insightface/models/antelopev2/inswapper_128.onnx https://huggingface.co/ezioruan/inswapper_128.onnx/resolve/main/inswapper_128.onnx && cd ../../models/facerestore_models && wget -O codeformer-v0.1.0.pth https://github.com/sczhou/CodeFormer/releases/download/v0.1.0/codeformer.pth && cd ../checkpoints && wget -O sd_xl_base_1.0.safetensors https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors && cd ../vae && wget -O sdxl_vae.safetensors https://huggingface.co/stabilityai/sdxl-vae/resolve/main/sdxl_vae.safetensors && cd /workspace/ComfyUI

What This Command Does (Breakdown for Power Users)

  • cd /workspace && git clone ...: Navigates and clones ComfyUI's repo—your node-based AI canvas.
  • apt update && apt install -y build-essential: Updates packages and installs compilers for Insightface (Reactor's face detection engine).
  • python -m venv venv && source ...: Creates and activates a virtual environment to isolate installs—keeps things clean and conflict-free.
  • pip install --upgrade pip && pip install torch ...: Upgrades pip, then installs PyTorch (CUDA 12.8 compatible) and ComfyUI's requirements (transformers, diffusers, etc.).
  • cd custom_nodes && git clone ... && pip install insightface==0.7.3 && python install.py: Clones Reactor, installs its Insightface dependency (version 0.7.3 for stability), and runs the setup script to build face models.
  • mkdir -p ... && wget ... (multiple): Creates directories and downloads:
    • inswapper_128.onnx (~246MB): Reactor's core face-swapping model from Hugging Face.
    • codeformer-v0.1.0.pth (~540MB): Face restoration model for seamless swaps (superior to alternatives like GFPGAN for detail preservation).
    • sd_xl_base_1.0.safetensors (~6.9GB): SDXL base checkpoint for high-res text-to-image generation.
    • sdxl_vae.safetensors (~335MB): VAE for crisp image decoding.
  • Final cd: Returns to ComfyUI root.

Runtime: 5-15 minutes (downloads dominate). Monitor with top or Jupyter's output. If it stalls on wget, retry or check internet in the pod.

Highlight: This one-liner ensures stealth— all assets are cloud-downloaded, no local traces. Post-install, verify: ls models/checkpoints should show SDXL; ls custom_nodes lists Reactor.

Step 4: Uncensor Reactor – Bypass NSFW Filters for Full Control

Reactor includes a built-in NSFW detector to prevent misuse, but for unrestricted artistic deepfakes, we'll stealthily disable it. This mod is quick and reversible—edit the source code directly.

  1. In Jupyter's file browser, navigate to /workspace/ComfyUI/custom_nodes/comfyui-reactor-node/scripts/.
  2. Open reactor_sfw.py (right-click > Open With > Text Editor).
  3. Locate the function def nsfw_image(...) (around line 10-20). At the very beginning of the function, add:
    return False
    
    • Full edit example:
      def nsfw_image(image: Image.Image) -> bool:
          return False  # Bypass NSFW check for unrestricted use
          # Original code follows...
      
  4. Save the file (Ctrl+S). Restart ComfyUI (next step) to apply.

Why This Works: It forces the detector to always return False, allowing any content. Sources like Reddit's r/comfyui confirm this as the cleanest hack—ethical note: Use responsibly, only for consensual, legal experiments.

Highlight: Stealth achieved—no external tools needed. If issues arise, revert by deleting the line.

Step 5: Launch ComfyUI – Ignite the Interface

Time to fire up the UI! ComfyUI's node graph is intuitive yet powerful—drag, connect, generate.

  1. In the Jupyter terminal (ensure you're in /workspace/ComfyUI and venv activated: source venv/bin/activate), run:

    python main.py --listen 0.0.0.0 --port 3000
    
    • This starts the server on all interfaces, port 3000 for proxy access.
  2. If port 3000 is busy (rare), kill it: fuser -k 3000/tcp then re-run.

  3. Back in RunPod dashboard, click "Connect" on your pod > "Connect to HTTP Service [Port 3000]." Opens at https://[PodId]-3000.proxy.runpod.net/.

  4. ComfyUI loads—a blank canvas awaits. If nodes are missing, refresh via the menu (Queue > Refresh) or restart the server.

Highlight: The UI is your stealth cockpit—browser-only, no installs on your machine. Auto-saves outputs to /workspace/ComfyUI/output.

Step 6: Load the Workflow – Paste and Activate the JSON Magic

We've crafted a minimal, high-octane workflow: Generate a base image from text via SDXL, then swap in a source face with Reactor. Copy-paste this complete JSON into ComfyUI.

  1. In ComfyUI, click "Load" (top-right menu) > Paste the JSON below into the text box > "Load."
  2. The nodes appear: CheckpointLoader, prompts, sampler, etc., connected for seamless flow.

Complete JSON Workflow:

{
  "id": "minimal-reactor-workflow",
  "revision": 0,
  "last_node_id": 10,
  "last_link_id": 14,
  "nodes": [
    {
      "id": 1,
      "type": "CheckpointLoaderSimple",
      "pos": [
        100,
        100
      ],
      "size": [
        270,
        98
      ],
      "flags": {},
      "order": 0,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "MODEL",
          "type": "MODEL",
          "links": [
            1
          ]
        },
        {
          "name": "CLIP",
          "type": "CLIP",
          "links": [
            2,
            3
          ]
        },
        {
          "name": "VAE",
          "type": "VAE",
          "links": [
            4
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "CheckpointLoaderSimple"
      },
      "widgets_values": [
        "sd_xl_base_1.0.safetensors"
      ]
    },
    {
      "id": 2,
      "type": "CLIPTextEncode",
      "pos": [
        400,
        100
      ],
      "size": [
        400,
        200
      ],
      "flags": {},
      "order": 1,
      "mode": 0,
      "inputs": [
        {
          "name": "clip",
          "type": "CLIP",
          "link": 2
        }
      ],
      "outputs": [
        {
          "name": "CONDITIONING",
          "type": "CONDITIONING",
          "links": [
            5
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "CLIPTextEncode"
      },
      "widgets_values": [
        "a beautiful landscape, mountains, river, sunset"
      ]
    },
    {
      "id": 3,
      "type": "CLIPTextEncode",
      "pos": [
        400,
        350
      ],
      "size": [
        400,
        200
      ],
      "flags": {},
      "order": 2,
      "mode": 0,
      "inputs": [
        {
          "name": "clip",
          "type": "CLIP",
          "link": 3
        }
      ],
      "outputs": [
        {
          "name": "CONDITIONING",
          "type": "CONDITIONING",
          "links": [
            6
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "CLIPTextEncode"
      },
      "widgets_values": [
        "blurry, low quality, artifacts"
      ]
    },
    {
      "id": 4,
      "type": "EmptyLatentImage",
      "pos": [
        100,
        250
      ],
      "size": [
        270,
        106
      ],
      "flags": {},
      "order": 3,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "LATENT",
          "type": "LATENT",
          "links": [
            7
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "EmptyLatentImage"
      },
      "widgets_values": [
        1024,
        1024,
        1
      ]
    },
    {
      "id": 5,
      "type": "KSampler",
      "pos": [
        850,
        100
      ],
      "size": [
        270,
        262
      ],
      "flags": {},
      "order": 4,
      "mode": 0,
      "inputs": [
        {
          "name": "model",
          "type": "MODEL",
          "link": 1
        },
        {
          "name": "positive",
          "type": "CONDITIONING",
          "link": 5
        },
        {
          "name": "negative",
          "type": "CONDITIONING",
          "link": 6
        },
        {
          "name": "latent_image",
          "type": "LATENT",
          "link": 7
        }
      ],
      "outputs": [
        {
          "name": "LATENT",
          "type": "LATENT",
          "links": [
            8
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "KSampler"
      },
      "widgets_values": [
        156680208522230,
        "randomize",
        20,
        1,
        "euler",
        "normal",
        1
      ]
    },
    {
      "id": 6,
      "type": "VAEDecode",
      "pos": [
        1150,
        100
      ],
      "size": [
        147,
        46
      ],
      "flags": {},
      "order": 5,
      "mode": 0,
      "inputs": [
        {
          "name": "samples",
          "type": "LATENT",
          "link": 8
        },
        {
          "name": "vae",
          "type": "VAE",
          "link": 4
        }
      ],
      "outputs": [
        {
          "name": "IMAGE",
          "type": "IMAGE",
          "links": [
            9
          ]
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "VAEDecode"
      },
      "widgets_values": []
    },
    {
      "id": 7,
      "type": "LoadImage",
      "pos": [
        100,
        400
      ],
      "size": [
        340,
        314
      ],
      "flags": {},
      "order": 6,
      "mode": 0,
      "inputs": [],
      "outputs": [
        {
          "name": "IMAGE",
          "type": "IMAGE",
          "links": [
            10
          ]
        },
        {
          "name": "MASK",
          "type": "MASK",
          "links": null
        }
      ],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "LoadImage"
      },
      "widgets_values": [
        "source_face_image.png",
        "image"
      ]
    },
    {
      "id": 8,
      "type": "ReActorFaceSwap",
      "pos": [
        1350,
        100
      ],
      "size": [
        285,
        358
      ],
      "flags": {},
      "order": 7,
      "mode": 0,
      "inputs": [
        {
          "name": "input_image",
          "type": "IMAGE",
          "link": 9
        },
        {
          "name": "source_image",
          "type": "IMAGE",
          "link": 10
        },
        {
          "name": "face_model",
          "type": "FACE_MODEL",
          "link": null
        },
        {
          "name": "face_boost",
          "type": "FACE_BOOST",
          "link": null
        }
      ],
      "outputs": [
        {
          "name": "SWAPPED_IMAGE",
          "type": "IMAGE",
          "links": [
            11
          ]
        },
        {
          "name": "FACE_MODEL",
          "type": "FACE_MODEL",
          "links": null
        },
        {
          "name": "ORIGINAL_IMAGE",
          "type": "IMAGE",
          "links": null
        }
      ],
      "properties": {
        "cnr_id": "comfyui-reactor",
        "ver": "48a3ad27f99f775dcf63e61276e0110d256597ef",
        "Node name for S&R": "ReActorFaceSwap"
      },
      "widgets_values": [
        true,
        "inswapper_128.onnx",
        "retinaface_resnet50",
        "codeformer-v0.1.0.pth",
        1,
        1,
        "no",
        "no",
        "0",
        "0",
        1
      ]
    },
    {
      "id": 9,
      "type": "SaveImage",
      "pos": [
        1650,
        100
      ],
      "size": [
        308,
        270
      ],
      "flags": {},
      "order": 8,
      "mode": 0,
      "inputs": [
        {
          "name": "images",
          "type": "IMAGE",
          "link": 11
        }
      ],
      "outputs": [],
      "properties": {
        "cnr_id": "comfy-core",
        "ver": "0.3.41",
        "Node name for S&R": "SaveImage"
      },
      "widgets_values": [
        "ComfyUI"
      ]
    }
  ],
  "links": [
    [
      1,
      1,
      0,
      5,
      0,
      "MODEL"
    ],
    [
      2,
      1,
      1,
      2,
      0,
      "CLIP"
    ],
    [
      3,
      1,
      1,
      3,
      0,
      "CLIP"
    ],
    [
      4,
      1,
      2,
      6,
      1,
      "VAE"
    ],
    [
      5,
      2,
      0,
      5,
      1,
      "CONDITIONING"
    ],
    [
      6,
      3,
      0,
      5,
      2,
      "CONDITIONING"
    ],
    [
      7,
      4,
      0,
      5,
      3,
      "LATENT"
    ],
    [
      8,
      5,
      0,
      6,
      0,
      "LATENT"
    ],
    [
      9,
      6,
      0,
      8,
      0,
      "IMAGE"
    ],
    [
      10,
      7,
      0,
      8,
      1,
      "IMAGE"
    ],
    [
      11,
      8,
      0,
      9,
      0,
      "IMAGE"
    ]
  ],
  "groups": [],
  "config": {},
  "extra": {},
  "version": 0.4
}

Workflow Flow: Loads SDXL > Encodes prompt > Samples latent > Decodes to image > Loads source face > Swaps via Reactor > Saves output. Customize by editing node widgets (e.g., prompt text).

Step 7: Choose Inputs, Prompt, and Run – Generate Your Deepfake

Energize the creation! This is where the magic explodes.

  1. Upload Input Image (Source Face):

    • In ComfyUI (or Jupyter file browser), upload your source face to /workspace/ComfyUI/input/. Name it source_face_image.png (or edit the LoadImage node's widget to match).
    • Action: Drag-drop in Jupyter or use "Upload" button. Ensure it's a clear, front-facing photo for best swaps—Reactor excels at single faces.
  2. Choose a Prompt:

    • Double-click the positive CLIPTextEncode node (ID 2). Edit the text widget: e.g., "a confident person standing in a futuristic city, high detail, cinematic lighting."
    • Negative prompt (ID 3): Keep "blurry, low quality, artifacts" or add "deformed face" for cleaner results.
    • Tweak KSampler (ID 5): Set steps to 20-30 for quality; seed to fixed number for reproducibility.
  3. Run the Workflow:

    • Click "Queue Prompt" (top-right). Watch the progress bar—SDXL generates the base (~10-20s), Reactor swaps (~2-5s).
    • Output saves automatically to /workspace/ComfyUI/output/ as PNG. Download via Jupyter (right-click > Download).

Highlight: For stealth, generate in batches—adjust EmptyLatentImage batch size to 4 for multiples. Results? Hyper-realistic deepfakes blending your prompt's scene with the source face seamlessly.

Troubleshooting: Conquer Common Hiccups

Even in this high-energy setup, glitches happen. Here's your fix-it arsenal:

  • Pod Won't Start: Check credits/billing. If GPU unavailable, switch regions or upgrade to RTX A4000 ($0.39/hr).
  • Download Fails (wget): Run individually, e.g., cd /workspace/ComfyUI/models/checkpoints && wget [URL]. Use --continue for resumes.
  • Insightface Error: Re-run pip install insightface==0.7.3 in venv. If compilation fails: apt install -y gcc g++ cmake.
  • Reactor No Swap: Verify models in paths (ls models/insightface). Ensure uncensor edit; test with simple faces.
  • VRAM Overflow: Lower resolution in EmptyLatentImage to 512x512. Monitor with nvidia-smi.
  • ComfyUI Crashes: Kill/restart: fuser -k 3000/tcp && python main.py --listen 0.0.0.0 --port 3000. Check logs for missing deps.
  • Slow Generation: RTX 2000 Ada handles 1024x1024 fine; for speed, use 20 steps Euler.

Backup: Save workflows as JSON (menu > Save) to /workspace for persistence.

Frequently Asked Questions (FAQ)

Q: Is this truly $0.24/hour?
A: Yes! RTX 2000 Ada pricing is locked; monitor usage in dashboard. With auto-terminate, idle costs zero.

Q: Can I do video deepfakes?
A: Absolutely—extend with AnimateDiff nodes (git clone in custom_nodes). Frame-by-frame swaps via batch processing.

Q: Ethical concerns?
A: Deepfakes pack power—use for art, education, or satire only. Always get consent for faces; respect laws like the EU AI Act. Reactor's original intent is creative, not harmful.

Q: Persist Across Sessions?
A: Network Volume saves everything. Re-deploy pod, mount volume, activate venv: cd /workspace/ComfyUI && source venv/bin/activate && python main.py...

Q: Alternatives to RunPod?
A: Vast.ai for cheaper spot instances, but RunPod's templates shine for ease.

Level Up: Advanced Tweaks and Ethical Power-Ups

For extra zest, install ComfyUI-Manager: In terminal, cd /workspace/ComfyUI/custom_nodes && git clone https://github.com/ltdrdata/ComfyUI-Manager.git && cd ComfyUI-Manager && pip install -r requirements.txt. Restart ComfyUI—now add nodes via GUI!

Experiment with Reactor weights (0-1 in node widgets) for subtle blends. For stealthier ops, script API calls: curl -X POST [proxy-url]/prompt -d @workflow.json.

Remember, this tech is a double-edged sword—wield it with integrity to fuel innovation, not deception. Dive in, create boldly, and share your triumphs in the comments. Your deepfake empire starts now! 🚀