Skip to content
Open
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
54 changes: 0 additions & 54 deletions .github/workflows/modal-runtime-image.yml

This file was deleted.

12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,17 @@ To submit work to a remote GPU machine, start its workspace with an API key:
GTSFM_API_KEY=your-secret gtsfm run --host 0.0.0.0 --no-browser
```

Choose **Remote VM** in the workspace, then select an API or SSH connection. Modal is the first hosted API option: enter its token ID and token secret separately, or paste the full `modal token set --token-id … --token-secret …` command into either field and the workspace will split it automatically. If the account does not already have a GTSFM deployment, click **Set up & deploy Modal workspace**. GTSFM first pulls the versioned `ghcr.io/borglab/gtsfm-modal-runtime` image, then deploys the protected `gtsfm-studio` web app and persistent `gtsfm-studio-data` volume, discovers its URL, and verifies the selected GPU. If the prebuilt image is unavailable, setup automatically falls back to building the dependency image in Modal.
Choose **Remote VM** in the workspace, then select an API or SSH connection. Modal is the first hosted API option: enter its token ID and token secret separately, or paste the full `modal token set --token-id … --token-secret …` command into either field and the workspace will split it automatically. If the account does not already have a GTSFM deployment, click **Set up & deploy Modal workspace**. GTSFM pulls the configured prebuilt Docker Hub runtime, then deploys the protected `gtsfm-studio` web app and persistent `gtsfm-studio-data` volume, discovers its URL, and verifies the selected GPU. If no prebuilt image is configured, setup builds the dependency image in Modal.

The prebuilt runtime contains the stable CUDA and Python dependency layers but no repository source or model weights. It is published by [the Modal runtime image workflow](.github/workflows/modal-runtime-image.yml) whenever runtime dependencies change on `master`, and for release tags. Normal GTSFM and UI changes are mounted after the image layer and therefore do not reinstall PyTorch or CUDA libraries. After its first publication, a Borglab package administrator must make `gtsfm-modal-runtime` public in GitHub Packages so Modal can pull it anonymously. To test a different runtime image, start the local workspace with `GTSFM_MODAL_RUNTIME_IMAGE=registry/image:tag gtsfm run`; set the variable to an empty string to force Modal's source-build fallback.
The prebuilt runtime contains the stable CUDA and Python dependency layers but no repository source or model weights. Normal GTSFM and UI changes are mounted after the image layer and therefore do not reinstall PyTorch or CUDA libraries. Publish it manually to a public Docker Hub repository using Modal's fast-pull eStargz format:

```bash
docker login
./docker/publish-modal-runtime.sh su071301/gtsfm-modal-runtime firstclass
GTSFM_MODAL_RUNTIME_IMAGE=docker.io/su071301/gtsfm-modal-runtime:firstclass gtsfm run
```

The first command prompts for a Docker Hub username and access token. The publisher builds `linux/amd64`, pushes directly to Docker Hub, and does not require GitHub Actions. Set `GTSFM_MODAL_RUNTIME_IMAGE` to an empty string to force Modal's source-build fallback.

GTSFM sample datasets are downloaded directly by the remote workspace. User-selected folders are archived and streamed to the protected workspace before the job starts. Live preview splats and the final `.ply` result are mirrored back to the local viewer. Modal account credentials and the derived workspace API key are kept in memory and are not included in public job state. Lambda Cloud, RunPod, Vast.ai, and AWS EC2 are listed as coming soon.

Expand Down
28 changes: 13 additions & 15 deletions docker/modal-runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -28,33 +28,31 @@ RUN apt-get update \
libglib2.0-0 \
libgomp1 \
libx11-6 \
libzstd-dev \
ninja-build \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*

RUN curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh \
&& uv python install ${PYTHON_VERSION} \
&& ln -sf "$(uv python find ${PYTHON_VERSION})" /usr/local/bin/python \
&& ln -sf "$(uv python find ${PYTHON_VERSION})" /usr/local/bin/python3
&& uv venv --python ${PYTHON_VERSION} /opt/gtsfm-venv

ENV VIRTUAL_ENV=/opt/gtsfm-venv \
UV_PROJECT_ENVIRONMENT=/opt/gtsfm-venv \
PATH=/opt/gtsfm-venv/bin:/root/.local/bin:${PATH}

WORKDIR /opt/gtsfm-runtime
COPY pyproject.toml uv.lock ./

RUN uv export \
RUN uv sync \
--frozen \
--no-dev \
--no-emit-project \
--no-header \
--output-file /tmp/gtsfm-runtime-requirements.txt \
&& uv pip install \
--python /usr/local/bin/python \
--requirements /tmp/gtsfm-runtime-requirements.txt \
&& rm /tmp/gtsfm-runtime-requirements.txt \
--no-install-project \
&& python -c "import fastapi, gsplat, gtsam, torch; print(torch.__version__)"

ENV HF_HOME=/workspace/cache/huggingface \
TORCH_HOME=/workspace/cache/torch \
TORCH_EXTENSIONS_DIR=/workspace/cache/torch-extensions \
XDG_CACHE_HOME=/workspace/cache \
PYTHONPATH=/root
# Volume-backed cache locations are assigned by visualization/modal_app.py at
# container startup. They must not be present while Modal extends this image,
# because build tools can otherwise populate the future Volume mount target.
ENV PYTHONPATH=/root

WORKDIR /root
21 changes: 21 additions & 0 deletions docker/publish-modal-runtime.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail

if [[ $# -lt 1 || $# -gt 2 ]]; then
echo "Usage: $0 DOCKERHUB_NAMESPACE/gtsfm-modal-runtime [TAG]" >&2
exit 2
fi

repository="${1#docker.io/}"
tag="${2:-firstclass}"
image="docker.io/${repository}:${tag}"

docker info >/dev/null
docker buildx build \
--platform linux/amd64 \
--file docker/modal-runtime.Dockerfile \
--tag "${image}" \
--output type=registry,compression=estargz,force-compression=true,oci-mediatypes=true \
.

echo "Published ${image}"
2 changes: 1 addition & 1 deletion visualization/modal_deployment.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"H200",
"B200",
}
DEFAULT_MODAL_RUNTIME_IMAGE = "ghcr.io/borglab/gtsfm-modal-runtime:latest"
DEFAULT_MODAL_RUNTIME_IMAGE = "docker.io/su071301/gtsfm-modal-runtime:firstclass"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this path for? why are we changing from a borglab path to a su071301 path?

@suhanpark suhanpark Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it shouldnt be. im removing the custom prebuilt-image setup and using Modal’s direct build path. This registry reference is no longer needed.



def modal_remote_api_key(token_id: str, token_secret: str) -> str:
Expand Down
Loading