Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
outputs:
changed-apps: ${{ steps.changed-apps.outputs.changed_files }}
changed-bases: ${{ steps.changed-bases.outputs.changed_files }}
changed-bases-runtime: ${{ steps.filter-runtime.outputs.bases }}
steps:
- name: Get Changed Apps
id: changed-apps
Expand All @@ -54,10 +55,39 @@ jobs:
include_only_directories: true
max_depth: 1

- name: Checkout (for runtime filter)
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false

- name: Filter Bases With Runtime Variant
id: filter-runtime
env:
CHANGED_BASES: ${{ steps.changed-bases.outputs.changed_files }}
DISPATCH_TYPE: ${{ github.event_name == 'workflow_dispatch' && inputs.type || '' }}
DISPATCH_IMAGE: ${{ github.event_name == 'workflow_dispatch' && inputs.image || '' }}
shell: bash
run: |
set -euo pipefail
if [[ "$DISPATCH_TYPE" == "base" ]]; then
candidates=$(jq -nc --arg b "$DISPATCH_IMAGE" '[$b]')
else
candidates="${CHANGED_BASES:-[]}"
fi
bases='[]'
for b in $(echo "$candidates" | jq -r '.[]'); do
if [[ -f "base/$b/Dockerfile.runtime" ]]; then
bases=$(echo "$bases" | jq --arg b "$b" '. + [$b]')
fi
done
echo "Runtime-variant bases: $bases"
echo "bases=$bases" >> "$GITHUB_OUTPUT"

# Build base images: devel first, then runtime (runtime pulls from published devel)
# Runtime variant - depends on devel being published first
# Runtime variant - depends on devel being published first.
# Only runs for bases that have a Dockerfile.runtime; devel-only bases are skipped.
build-bases:
if: ${{ always() && !failure() && !cancelled() && (needs.prepare.outputs.changed-bases != '[]' || (github.event_name == 'workflow_dispatch' && inputs.type == 'base')) }}
if: ${{ always() && !failure() && !cancelled() && needs.prepare.outputs.changed-bases-runtime != '[]' }}
name: Build Base ${{ matrix.base }}
needs: ["prepare", "build-bases-devel"]
uses: ./.github/workflows/image-builder.yaml
Expand All @@ -70,7 +100,7 @@ jobs:
secrets: inherit
strategy:
matrix:
base: ${{ github.event_name == 'workflow_dispatch' && fromJSON(format('["{0}"]', inputs.image)) || fromJSON(needs.prepare.outputs.changed-bases) }}
base: ${{ fromJSON(needs.prepare.outputs.changed-bases-runtime) }}
fail-fast: false
with:
image: ${{ matrix.base }}
Expand Down
5 changes: 5 additions & 0 deletions base/pytorch/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore everything except the files we explicitly need in the build context.
*

!Dockerfile
!docker-bake.hcl
80 changes: 80 additions & 0 deletions base/pytorch/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CUDA 13.0 + PyTorch 2.11 + Python 3.13 base image.
#
# Slim PyTorch foundation. uv-managed Python; venv at /opt/venv.
# Devel variant only — provides nvcc, NVRTC, CUPTI, cuDNN headers for
# downstream apps that compile CUDA extensions.
#
# Build locally:
# docker buildx bake image-devel-local
#
# Tags: pytorch:cuda13.0-torch2.11-devel, pytorch:devel, pytorch:latest

ARG CUDA_VERSION="13.0.3"
ARG CUDA_DISTRO="ubuntu24.04"
ARG UV_VERSION="0.11.8"

FROM ghcr.io/astral-sh/uv:${UV_VERSION} AS uv

FROM nvidia/cuda:${CUDA_VERSION}-cudnn-devel-${CUDA_DISTRO}

ARG PYTHON_VERSION="3.13"
ARG TORCH_VERSION="2.11.0"
ARG TORCHVISION_VERSION="0.26.0"
ARG TORCHAUDIO_VERSION="2.11.0"
ARG XFORMERS_VERSION="0.0.35"
ARG TRITON_VERSION="3.6.0"
ARG TORCH_INDEX="https://download.pytorch.org/whl/cu130"

ENV DEBIAN_FRONTEND=noninteractive \
PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
CUDA_HOME=/usr/local/cuda \
CPATH=/usr/local/cuda/include \
TORCH_CUDA_ARCH_LIST="12.0" \
UV_COMPILE_BYTECODE=1 \
UV_LINK_MODE=copy \
UV_HTTP_TIMEOUT=300 \
UV_PYTHON_INSTALL_DIR=/opt/python \
UV_PYTHON_PREFERENCE=only-managed \
VIRTUAL_ENV=/opt/venv \
PATH=/usr/local/cuda/bin:/opt/venv/bin:${PATH}

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential ninja-build git curl ca-certificates tini && \
apt-get clean && rm -rf /var/lib/apt/lists/*

COPY --from=uv /uv /uvx /usr/local/bin/

RUN uv python install ${PYTHON_VERSION} && \
uv venv /opt/venv --python ${PYTHON_VERSION}

# Single resolve across all packages — internally consistent pins.
# `--index-strategy unsafe-best-match` makes resolution deterministic across
# the cu130 index + PyPI mix.
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install \
--index-url ${TORCH_INDEX} \
--extra-index-url https://pypi.org/simple \
--index-strategy unsafe-best-match \
torch==${TORCH_VERSION} \
torchvision==${TORCHVISION_VERSION} \
torchaudio==${TORCHAUDIO_VERSION} \
xformers==${XFORMERS_VERSION} \
triton==${TRITON_VERSION} \
accelerate numpy safetensors nvidia-ml-py \
sympy packaging pybind11 ninja psutil wheel

# Constraints file for downstream apps to inherit pins (torch ecosystem
# plus the nvidia-* transitive deps and the cuda-toolkit umbrella).
RUN uv pip freeze | grep -E \
"^(torch|torchvision|torchaudio|xformers|triton|numpy|safetensors|accelerate|nvidia-|cuda-toolkit)==" \
> /constraints.txt && \
echo "Constraints:" && cat /constraints.txt

# Build-time validation. torch.cuda.is_available() requires --gpus all and is
# intentionally not checked here — only that torch was built against CUDA and
# that xformers' py3-none wheel imports against torch's C++ ABI on cp313.
RUN python -c "import torch; print(f'PyTorch {torch.__version__} CUDA {torch.version.cuda}'); assert torch.version.cuda is not None" && \
python -c "import xformers; print(f'xformers {xformers.__version__}')"

ENTRYPOINT ["/usr/bin/tini", "--"]
41 changes: 41 additions & 0 deletions base/pytorch/docker-bake.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
target "docker-metadata-action" {}

variable "APP" {
default = "pytorch"
}

variable "VERSION" {
// Format: cuda{CUDA_VERSION}-torch{TORCH_VERSION}
default = "cuda13.0-torch2.11"
}

variable "SOURCE" {
default = "https://github.com/arsac/containers"
}

variable "VENDOR" {
default = "arsac"
}

group "default" {
targets = ["image-devel-local"]
}

target "image-devel" {
inherits = ["docker-metadata-action"]
dockerfile = "Dockerfile"
labels = {
"org.opencontainers.image.source" = "${SOURCE}"
}
}

target "image-devel-local" {
inherits = ["image-devel"]
output = ["type=docker"]
tags = ["${APP}:${VERSION}-devel", "${APP}:devel", "${APP}:latest"]
}

target "image-devel-all" {
inherits = ["image-devel"]
platforms = ["linux/amd64"]
}
Loading
Loading