README.md
Range opens a shell in a container image, a Hugging Face repository, or an environment in S3 or on any HTTP server, without downloading it first. Only the bytes your program reads cross the network.
$ range shell python:3.12 $ range shell python:3.12 --mount hf://moonshotai/Kimi-K2-Instruct:/model $ range shell s3://<your-bucket>/dev.range $ range shell python:3.12 --mount .:/work -p 8000:8000 $ range shell ebs://ami-0123456789abcdef0 $ range shell ubuntu --commands
No Docker, no daemon and no pull. Each session runs in a small VM of its own, on Linux and on a Mac.
Measured on EC2 in us‑east‑1, with the image indexed once. See bench.log.
demo.txt
$ range run ghcr.io/ggml-org/llama.cpp:light-b11206 \
--mount hf://unsloth/gemma-3-270m-it-GGUF:/model -- \
llama-cli -m /model/gemma-3-270m-it-Q4_K_M.gguf -st \
-p "Why is the sky blue? Answer in one sentence."
The sky is blue because of a phenomenon called Rayleigh scattering,
where blue light is scattered more than other colors.
Range opens the llama.cpp image from its registry and mounts the model repository at
/model. The repository holds 6.38 GB in 24 files. Range reads one of them.
With the image indexed, the answer took 6.7 s from an empty cache.
docker pull plus hf download took 18.3 s. The image comes
pre-indexed from a public catalog, so a new user starts there.
$ sudo range run --gpus all ghcr.io/ggml-org/llama.cpp:light-cuda-b11206 \
--mount hf://unsloth/gemma-3-270m-it-GGUF:/model -- \
llama-cli -m /model/gemma-3-270m-it-Q4_K_M.gguf -ngl 99 -st -p "Hi"
Range keeps the kernels CUDA compiles for your GPU. On a Tesla T4, every run after the first answered in 3.1 s. With Docker, every run took about two minutes.
$ range run python:3.12 --mount hf://moonshotai/Kimi-K2-Instruct:/model -- \
du -sh --apparent-size /model
959G /model
Kimi K2 is 1.03 TB in 61 shards. A Python script inside read its config, the header of one shard and one tensor. That took 3.4 s with the image indexed, and moved 9.5 MB of the model. The other 60 shards never left Hugging Face.
Range reads a file when a program opens it. A program that reads a whole model still downloads the whole model, once.
$ range run ebs://snap-0fb55b083ff371aff -- \
sh -c 'grep PRETTY /etc/os-release; dpkg -l | wc -l'
PRETTY_NAME="Ubuntu 24.04.5 LTS"
641
Range opens an EBS snapshot or an AMI with no instance and no volume. From a MacBook, the answer took 6.3 s. Starting an instance from the AMI took 21.9 s.
$ range shell ubuntu --commands root@range:~# python3 -c 'print(6*7)' [range] python3 from python:3.12-slim 42 root@range:~# go@1.22 version go version go1.22.12 linux/arm64
A command Ubuntu lacks comes from the image that has it. go@1.22 picks the
version, and so does a project's go.mod or .nvmrc.
agent_sandbox.txt
$ id=$(range run -d --commands --cpus 2 --memory 4g \
--allow-host pypi.org --allow-host files.pythonhosted.org ubuntu)
$ range cp ./repo $id:/root/
$ range exec -w /root/repo $id go test ./...
ok example.com/demo 0.001s
$ range exec -it $id bash
$ range stop $id
go, pip or nvcc come from the image that has them.--network none cuts it off. --allow-host opens one host.run -d, exec, cp, ps, stop,
--cpus, --memory.bench.log
| Empty cache to output | docker pull | Range, first run | Range, indexed | Range, again |
|---|---|---|---|---|
| Chat demo | 18.3 s 579 MB | 15.5 s 590 MB | 6.7 s 317 MB | 4.2 s 0 MB |
| python:3.12 | 15.8 s 435 MB | 16.5 s 415 MB | 2.8 s 48 MB | 1.1 s 0 MB |
| rust:1.82 | 19.6 s 569 MB | 22.4 s 546 MB | 8.0 s 125 MB | 1.6 s 0 MB |
| eclipse-temurin:21 | 7.9 s 232 MB | 9.2 s 225 MB | 2.9 s 49 MB | 1.0 s 0 MB |
| Kimi K2, 1 TB | not tried 1.03 TB | 17.6 s 433 MB | 3.4 s 56 MB | 1.2 s 0 MB |
Every run but "again" starts from an empty cache. Bars replay at 3x speed.
| Command | Range | Docker Desktop |
|---|---|---|
| pip install requests numpy | 12.2 s | 17.4 s |
| python -m compileall | 10.3 s | 14.6 s |
| cargo new, cargo build | 15.2 s | 16.7 s |
| go build, shared directory | 10.9 s | 15.1 s |
Nothing downloaded yet, on a MacBook Pro with Apple silicon.
problem.txt
A machine that needs a large environment downloads all of it, every time, to use a small part.
Range reads only the bytes each machine touches, and the next run fetches them before it asks.
design.txt
ReadAt(offset, length) -> bytes. Range turns a source into a disk, and turns
each read of that disk into a ranged request to the source.
install.txt
A release archive for macOS or Linux, x86-64 or arm64. Then open a shell in any image:
$ curl -fsSL https://github.com/andreygrehov/range/releases/latest/download/range_$(uname -s)_$(uname -m).tar.gz | tar -xz $ ./range shell python:3.12
For your own environments, build once, and every first run reads lazily:
$ range build --from-oci python:3.12 -o py.range $ range publish py.range s3://<your-bucket>/py.range $ range shell s3://<your-bucket>/py.range
A published artifact needs no indexing. The go1.23 demo artifact was ready in 0.41 s on its first run, and moved 6 MB of 1.03 GB.
Or build from source, with Go 1.25 or newer:
$ git clone https://github.com/andreygrehov/range && cd range && make install
On Linux, Range needs QEMU and KVM, as root or in the kvm group. Without KVM, as
root, it runs natively with the erofs and overlay kernel modules. A Mac with Apple silicon needs
nothing more. An Intel Mac needs Lima: brew install lima. range doctor checks
all of it. Windows works through WSL2, untested.
about_me.txt
I am a software engineer at AWS. Range is my personal project.