Examples see history edit this page

Talks about:

The following examples show how ilo shell can be used to cache a build tool’s downloaded dependencies across runs, so they are not re-downloaded every time.

Caching dependencies

Persisting a tool’s download cache means mounting a host directory into the container. The robust, image-agnostic way is to mount one host directory at a fixed container path (/cache below) and point the tool’s cache there with its own environment variable — rather than mounting onto the image’s default cache location. Default locations vary between images and, because most current official images run as a non-root user (e.g. docker.io/library/maven:latest runs as ubuntu, not root, with its repo under /home/ubuntu), mounting onto /root/... is usually both the wrong path and unwritable.

ilo keeps the cached files owned by you on the host (--update-remote-user-uid, on by default), so the cache stays usable from the host too. See File Ownership.

All rows use the same mount; pick the image that carries your toolchain — always in long-form (docker.io/library/golang:latest, not golang:latest; short names resolve differently under Podman/Fedora, long-form works everywhere) — and add the tool’s cache environment variable:

--volume ${XDG_CACHE_HOME:-$HOME/.cache}/<tool>:/cache:z
Tool / ecosystemImage (long-form)Add this to point the cache at /cache
Mavendocker.io/library/maven:latest--env MAVEN_ARGS=-Dmaven.repo.local=/cache
Gradledocker.io/library/gradle:latest--env GRADLE_USER_HOME=/cache
Cargo (Rust)docker.io/library/rust:latest--env CARGO_HOME=/cache
Godocker.io/library/golang:latest--env GOMODCACHE=/cache/mod --env GOCACHE=/cache/build
npmdocker.io/library/node:latest--env npm_config_cache=/cache
pipdocker.io/library/python:latest--env PIP_CACHE_DIR=/cache
uv (Python)ghcr.io/astral-sh/uv:python3.13-bookworm-slim--env UV_CACHE_DIR=/cache
Composer (PHP)docker.io/library/composer:latest--env COMPOSER_CACHE_DIR=/cache
NuGet (.NET)mcr.microsoft.com/dotnet/sdk:latest--env NUGET_PACKAGES=/cache
Denodocker.io/denoland/deno:latest--env DENO_DIR=/cache
Bundler (Ruby)docker.io/library/ruby:latest--env GEM_HOME=/cache
Bun (JS/TS)docker.io/oven/bun:latest--env BUN_INSTALL_CACHE_DIR=/cache
Dartdocker.io/library/dart:latest--env PUB_CACHE=/cache
Elixir (Mix/Hex)docker.io/library/elixir:latest--env MIX_HOME=/cache --env HEX_HOME=/cache
Haskell (Cabal)docker.io/library/haskell:latest--env CABAL_DIR=/cache
Juliadocker.io/library/julia:latest--env JULIA_DEPOT_PATH=/cache

Tools not bundled in a base image follow the same pattern once installed: enable Yarn/pnpm with corepack enable, or install Poetry in your Containerfile, then point that tool’s own cache-dir environment variable (YARN_CACHE_FOLDER, POETRY_CACHE_DIR, …) at /cache. An image that runs as root (e.g. docker.io/library/gradle:latest) is the exception on a host whose rootless runtime defaults to a keep-id user namespace: its cache is written under a sub-UID, so the container reuses it fine but it is not owned by your user and you cannot manage it from the host. The fix is to run the image as its non-root user with --remote-user <name>, which maps to you, so the cache is owned by you. See File Ownership.

Full examples

# Maven
$ ilo shell \
    --volume ${XDG_CACHE_HOME:-$HOME/.cache}/maven:/cache:z \
    --env MAVEN_ARGS=-Dmaven.repo.local=/cache \
    docker.io/library/maven:latest mvn verify

# Go
$ ilo shell \
    --volume ${XDG_CACHE_HOME:-$HOME/.cache}/go:/cache:z \
    --env GOMODCACHE=/cache/mod --env GOCACHE=/cache/build \
    docker.io/library/golang:latest go test ./...

Or bake the mount and environment variable into an argument file (or .ilo.rc), one argument per line, so every call is just ilo <command>:

shell
--volume ${XDG_CACHE_HOME:-$HOME/.cache}/cargo:/cache:z
--env CARGO_HOME=/cache
docker.io/library/rust:latest

If you prefer to mount onto the image’s native cache path instead, first confirm where that image keeps it (ilo shell <image> sh -c 'echo $HOME; id') — it is image-specific.

Caching tools without a dedicated cache variable

Some toolchains keep their caches under $HOME, or in several places with no single cache-dir variable. These need a full invocation rather than a table row.

Swift keeps its package cache under $HOME (~/.cache/org.swift.swiftpm, ~/.swiftpm), so redirect HOME to the mount:

$ ilo shell \
    --volume ${XDG_CACHE_HOME:-$HOME/.cache}/swift:/cache:z --env HOME=/cache \
    docker.io/library/swift:latest swift build

Clojure’s git dependencies honor GITLIBS, but its Maven local repository has no environment variable (the JVM derives it from the passwd home, not HOME), so set it on the command line with -Sdeps:

$ ilo shell \
    --volume ${XDG_CACHE_HOME:-$HOME/.cache}/clojure:/cache:z --env GITLIBS=/cache/gitlibs \
    docker.io/library/clojure:latest \
    clojure -Sdeps '{:mvn/local-repo "/cache/m2"}' -P

Maven deps land in /cache/m2, git deps in /cache/gitlibs.