Download handler/Dockerfile from tugrulv89/LTX_2.5-handler: direct link, hf CLI and curl.
- Browser
- Download file 12 kB
-
https://huggingface.co/tugrulv89/LTX_2.5-handler/resolve/main/handler/Dockerfile
- Command line
-
hf download hf://tugrulv89/LTX_2.5-handler/handler/Dockerfile
-
curl -L -o Dockerfile https://huggingface.co/tugrulv89/LTX_2.5-handler/resolve/main/handler/Dockerfile
12 kB
| # Base: torch~=2.7 is a real, hard dependency of ltx-core (its own | |
| # pyproject.toml, not a guess). CUDA 13.0 chosen deliberately over the | |
| # 12.6-based safer option -- Blackwell (96GB tier) support requires it, and | |
| # torch==2.7.1 is confirmed (Qwen-Image-Edit-runpod's own real 2026-07-31 | |
| # deploy) as the first stable torch release with real sm_120 kernels. This | |
| # reopens the "some RunPod hosts refuse CUDA>=12.8 containers" risk | |
| # documented in ../BiRefNet-runpod/README.md and ../Qwen-Image-Edit-runpod/ | |
| # Dockerfile -- a deliberate, discussed tradeoff for this repo, not an | |
| # oversight. | |
| # | |
| # ACTUAL RESOLVED VERSION, confirmed by a real local build + import test | |
| # (2026-07-31, no GPU needed for this check): `uv sync --frozen` installs | |
| # LTX-2's own uv.lock-pinned torch, which overrides whatever ships with this | |
| # base image -- the real result is torch==2.9.1+cu128, NOT 2.7.1+cu1300 as | |
| # this image's tag implies. Still satisfies ltx-core's `torch~=2.7` | |
| # constraint (2.9.1 is within the 2.x series, >=2.7). If anything this is a | |
| # BETTER outcome than planned: cu128 is exactly the build Qwen-Image-Edit- | |
| # runpod already confirmed has working Blackwell/sm_120 kernels in a real | |
| # deploy, not the untested cu130 this base image's own tag suggested. | |
| FROM runpod/pytorch:1.0.3-cu1300-torch271-ubuntu2404 | |
| WORKDIR /app | |
| # uv is LTX-2's own install method (uv sync --frozen from the repo root), | |
| # not a pip package -- see LTX-2/README.md's quickstart. Installed via the | |
| # official standalone installer rather than pip, matching upstream docs. | |
| RUN curl -LsSf https://astral.sh/uv/install.sh | sh | |
| ENV PATH="/root/.local/bin:${PATH}" | |
| # LTX v1.4.2: keep handler calls and all pipeline return formats at this pin. | |
| ARG LTX2_COMMIT=9ec55f9f22798a3198d9c923856824821bc3317e | |
| ENV LTX_UPSTREAM_COMMIT=${LTX2_COMMIT} | |
| # Full clone (not --depth 1): the pin is a specific commit, not the branch | |
| # tip, and a shallow clone would need its own --depth-1-of-that-commit | |
| # fetch anyway (a second network round trip, not a saving) since the commit | |
| # isn't known to git until it's fetched. | |
| # | |
| # The repo's own .gitattributes DOES configure git-lfs for several | |
| # extensions (*.safetensors, *.png, *.mp4, test-asset *.wav, etc.) -- but | |
| # the actual multi-GB model weights are NOT committed here; they live on | |
| # Hugging Face (Lightricks/LTX-2.3, google/gemma-3-...) and are only | |
| # referenced by path/URL from this repo's scripts. Confirmed by cloning it: | |
| # the checkout is 5.0MB total. git-lfs is NOT installed in this image on | |
| # purpose -- without it, any LFS-tracked file this repo does contain (small | |
| # doc/test assets under packages/ltx-pipelines/tests/assets/, not anything | |
| # this handler's code path touches) checks out as its plain-text LFS | |
| # pointer instead of real content. Harmless here since nothing in | |
| # ltx-core/ltx-pipelines' actual import path depends on those files, but | |
| # flagged rather than silently assumed away. | |
| RUN git clone https://github.com/Lightricks/LTX-2.git /opt/ltx2 \ | |
| && cd /opt/ltx2 \ | |
| && git checkout ${LTX2_COMMIT} | |
| # `uv sync` WITHOUT --frozen, and that is a real regression to be aware of. | |
| # The 2.3 build used `uv sync --frozen` against upstream's committed uv.lock. | |
| # Upstream DELETED uv.lock in commit 2362161 ("Automated PR - 2026-08-11", the | |
| # LTX-2.5 release), so --frozen now fails outright: there is no lockfile to | |
| # freeze against. Verified by `git cat-file -e <pin>:uv.lock` at both pins. | |
| # | |
| # CONSEQUENCE: dependency resolution now happens at BUILD time, so two builds | |
| # of the same LTX2_COMMIT can install different versions. To restore | |
| # reproducibility, copy the uv.lock this build produces out of the image | |
| # (/opt/ltx2/uv.lock), commit it to THIS repo, COPY it in before this step and | |
| # put --frozen back. Deliberately not done pre-first-build: the resolution has | |
| # to succeed once before it is worth pinning. | |
| # | |
| # `--extra natten` IS used, and skipping it was a real bug. The previous comment | |
| # here claimed natten "only matters for the compiled attention path this handler | |
| # does not enable" -- wrong on both counts, proven by a real job on 2026-08-25: | |
| # | |
| # DiffVAE: natten is NOT installed. Falling back to a slower neighborhood- | |
| # attention backend (Triton if available, else pure-PyTorch tiled SDPA). | |
| # This path is for compatibility only -- install natten for production | |
| # DiffVAE decode | |
| # DiffVAE NA fallback: using Triton na3d. | |
| # -> RuntimeError: Triton Error [CUDA]: invalid argument | |
| # | |
| # natten is the DiffVAE VIDEO DECODER's neighborhood-attention backend, which | |
| # EVERY job goes through. Without it the selection order is natten -> Triton -> | |
| # eager SDPA, and the Triton na3d kernel fails at large frame counts: a 481-frame | |
| # (20s) job generated fine through both denoising stages, peaked at 58.88 GiB, | |
| # and then died in decode. 121 frames survives the fallback; 481 does not. | |
| # | |
| # And the build-risk half was wrong too: ltx-core's `natten` extra pins | |
| # `natten==0.21.7+torch2130cu132` from https://whl.natten.org -- a PREBUILT wheel | |
| # matching the torch 2.13.0+cu132 this image already resolves to. No CUDA compile, | |
| # so the 30-minute build cap is not in play. Upstream's own README install line is | |
| # exactly `uv sync --extra natten`. | |
| # | |
| # HEADS UP on torch: at this pin ltx-core pins torch==2.13.0 (cu132), a large | |
| # jump from the 2.9.1+cu128 the 2.3 build actually resolved to. Upstream's own | |
| # pyproject carries an override-dependencies entry forcing | |
| # nvidia-cudnn-cu13==9.21.1.3, because the version torch pulls omits | |
| # libcudnn_engines_tensor_ir and the soname then resolves against the system | |
| # cuDNN, raising CUDNN_STATUS_SUBLIBRARY_VERSION_MISMATCH. That override comes | |
| # along automatically with the workspace sync -- but it is written against | |
| # THEIR CI image's system cuDNN, not this base image's. If the first build or | |
| # first job dies with a cuDNN version mismatch, this comment is the reason. | |
| # ltx-kernels (CUDA-compiled extensions) deliberately excluded from the | |
| # default workspace by LTX-2's own pyproject.toml (see its | |
| # `[tool.uv.workspace] exclude` comment) specifically so a plain `uv sync` | |
| # never forces a CUDA toolchain at build time -- not opting into the | |
| # `kernels` group here for that same reason, one fewer unverified variable. | |
| # | |
| # CONSEQUENCE, and it is the only thing gating the nvfp4 cost experiment: | |
| # LTX_QUANTIZATION=nvfp4-cast / nvfp4-prequant need `ltx_kernels.nvfp4`, which | |
| # lives in that excluded group. Enabling it means `uv sync --frozen --group | |
| # kernels` here AND accepting a CUDA toolchain at build time (longer builds, | |
| # and RunPod's GitHub integration caps a build at 30 minutes). Do that as a | |
| # deliberate experiment -- with the nvfp4 transformer file already in the | |
| # bundle -- not as a default. | |
| WORKDIR /opt/ltx2 | |
| RUN uv sync --extra natten | |
| # This project's own glue code (runpod SDK, GCS upload, image download) -- | |
| # kept separate from ltx-pipelines' own dependency set rather than editing | |
| # their pyproject.toml/uv.lock, so `uv sync --frozen` above stays a clean, | |
| # unmodified reproduction of upstream's own resolution. | |
| COPY requirements.txt /app/requirements.txt | |
| RUN uv pip install --python /opt/ltx2/.venv/bin/python -r /app/requirements.txt | |
| # NOTE: do NOT bake the ~71GB of model weights into this image -- same | |
| # reasoning as every other repo in this family (BiRefNet-runpod, | |
| # Qwen-Image-Edit-runpod, Wan2.2-runpod): RunPod's own 80GB image-size cap | |
| # (confirmed via RunPod's docs while building Qwen-Image-Edit-runpod) would be | |
| # blown by this alone, before even counting the base image. | |
| # | |
| # SINGLE BUNDLE REPO STRATEGY. RunPod's cached-model feature ("Model" field) | |
| # allows exactly ONE repo per endpoint ("Each endpoint is currently limited to | |
| # one cached model at a time", docs.runpod.io/serverless/endpoints/model-caching) | |
| # but provisions it UNBILLED ("You aren't billed for worker time while your | |
| # model is being downloaded"). It also CANNOT filter which files it pulls (the | |
| # same docs page: "the system currently downloads all quantization versions"), | |
| # so naming Lightricks/LTX-2.5 in that field would pre-pull the whole 298.7GB | |
| # repo -- including the *-comfy-int8-convrot.safetensors variants ltx-pipelines | |
| # cannot even read. | |
| # | |
| # Fix: ONE user-owned HF "bundle" repo mirroring only what is needed, at | |
| # IDENTICAL paths to upstream so handler.py's defaults work against either. | |
| # Built 2026-08-24 with huggingface_hub.copy_files() -- server-side repo-to-repo | |
| # copies, no bytes downloaded or uploaded, 140.75GB in ~11 seconds. Contents | |
| # (see ../shoupapp/LTX_25_MIGRATION_PLAN.md section 3): | |
| # diffusion_models/ltx-2.5-22b-distilled-transformer-bf16.safetensors 42.02 GB <- this handler | |
| # text_encoders/gemma4-12b-with-proj-ltx-2.5-bf16.safetensors 26.26 GB <- this handler | |
| # vae/ltx-2.5-video-vae-bf16.safetensors 1.47 GB <- this handler | |
| # vae/ltx-2.5-audio-vae-bf16.safetensors 0.36 GB <- this handler | |
| # latent_upscale_models/ltx-2.5-latent-spatial-upscaler-x2-bf16-1.0... 1.00 GB <- this handler | |
| # diffusion_models/ltx-2.5-22b-distilled-transformer-nvfp4.safetensors 18.72 GB <- nvfp4 experiment | |
| # diffusion_models/ltx-2.5-22b-dev-transformer-bf16.safetensors 42.02 GB <- 360deg/keyframe, later | |
| # loras/ltx-2.5-22b-distilled-lora-450-bf16.safetensors 8.90 GB <- 360deg/keyframe, later | |
| # The last three are NOT loaded by this handler; they are staged so the | |
| # keyframe-interpolation and premium-quality work needs no re-provisioning. | |
| # | |
| # !! REQUIRED CONSOLE SETTINGS !! | |
| # - "Model" field = the bundle repo id (tugrulv89/LTX_2.5-handler) | |
| # - Endpoint env var: LTX_MODEL_REPO=<same bundle repo id> | |
| # handler.py resolves everything through the standard HF cache (HF_HUB_CACHE -> | |
| # /runpod-volume/huggingface-cache/hub), which is exactly where the Model field | |
| # pre-provisions files -- no local_dir= anywhere, so nothing is downloaded | |
| # twice, and no Network Volume is required. Without the Model field the handler | |
| # still works standalone -- it downloads ~71GB into that same cache, on BILLED | |
| # time, on every cold start. Configure the field. | |
| # | |
| # The 2.3 trio (LTX_CHECKPOINT_REPO / LTX_GEMMA_REPO / LTX_GEMMA_SUBFOLDER) is | |
| # GONE: 2.5's text encoder is a single file, not a Gemma snapshot directory, so | |
| # there is no subfolder to point at. Per-file overrides are LTX_TRANSFORMER_FILE | |
| # / LTX_TEXT_ENCODER_FILE / LTX_VIDEO_VAE_FILE / LTX_AUDIO_VAE_FILE / | |
| # LTX_UPSCALER_FILE. | |
| # | |
| # !! HF_TOKEN !! required as a RunPod Secret while the bundle repo is private or | |
| # the upstream repo is gated (Lightricks/LTX-2.5 is gated:auto -- the license | |
| # must be accepted once on the model page). | |
| COPY handler.py /app/handler.py | |
| # DEV OVERRIDE (added 2026-07-31, after the 4th from-scratch GitHub build): | |
| # if /runpod-volume/dev/handler.py exists, run IT instead of the baked-in | |
| # /app/handler.py. Rationale: this endpoint deploys via RunPod's GitHub | |
| # integration, which builds and redeploys on EVERY push to the watched | |
| # branch (docs.runpod.io/serverless/workers/github-integration -- also the | |
| # source of the 30-minute docker-build cap and 80GB image cap); its builder | |
| # cache is not under our control, so in practice each handler-only tweak | |
| # cost a full from-scratch image build. With this override, iterating on | |
| # handler.py needs ZERO builds: copy the new file to the network volume | |
| # (from any pod attached to it, e.g. `curl -o /runpod-volume/dev/handler.py | |
| # <raw github url>`), then let the endpoint's workers restart. DELETE the | |
| # file to return to the image's own pinned handler -- that is the | |
| # production configuration; the override is a development mechanism. | |
| # The echo makes an active override loudly visible in every worker log so | |
| # it can't linger unnoticed. | |
| CMD ["/bin/sh", "-c", "if [ -f /runpod-volume/dev/handler.py ]; then echo '!! DEV handler override active: /runpod-volume/dev/handler.py (delete it to use the baked-in handler)' && exec /opt/ltx2/.venv/bin/python -u /runpod-volume/dev/handler.py; else exec /opt/ltx2/.venv/bin/python -u /app/handler.py; fi"] | |