Skip to content

Instantly share code, notes, and snippets.

@parsapoorsh
Last active May 30, 2025 12:24
Show Gist options
  • Save parsapoorsh/e024b1617560324c8547ae23ae1d4c33 to your computer and use it in GitHub Desktop.
Save parsapoorsh/e024b1617560324c8547ae23ae1d4c33 to your computer and use it in GitHub Desktop.
a script to download ollama models using WGET with hash verification
#!/bin/bash
die(){
echo "$1"
exit 1
}
# Function to verify SHA256 hash of a downloaded file.
verify_hash(){
local expected="$1"
local file="$2"
# Strip the "sha256:" prefix from the expected hash, if present
expected="${expected#sha256:}"
echo "[=] Computing hash for: $file"
if ! computed=$(sha256sum "$file" 2>/dev/null | awk '{print $1}'); then
die "[x] Failed to compute hash for $file"
fi
if [ "$expected" != "$computed" ]; then
echo "[x] Hash mismatch:"
echo "[-] expected: $expected"
die "[x] computed: $computed"
else
echo "[+] Hash verified"
fi
}
# Check for required commands
command -v jq >/dev/null || die "Need jq"
command -v wget >/dev/null || die "Need wget"
# Check if a model name is provided
if [ -z "$1" ]; then
die "usage: $0 modelname[:tag] [path-to-save-models]"
fi
# The first argument is the model name with optional tag
model_name="$1"
# The second argument is the path to save the model (optional)
model_dir="${2:-./models}"
# Remove trailing slash from model_dir if it exists
model_dir="${model_dir%/}"
# Parse model name and tag
name=${model_name%%:*}
[[ "$model_name" = *:* ]] && tag="${model_name##*:}" || tag=latest
[[ "$model_name" = */* ]] && base_path="" || base_path="/library"
# Ensure the model directory is valid
if [ ! -d "$model_dir" ]; then
echo "[=] Directory $model_dir does not exist. Creating it."
mkdir -p "$model_dir" || die "[x] Couldn't create directory $model_dir"
fi
# Create necessary subdirectories in the model directory
mkdir -p "$model_dir/blobs" || die "Couldn't create blobs directory in $model_dir"
mkdir -p "$model_dir/manifests/registry.ollama.ai/library/$name" || die "Couldn't create manifests directory in $model_dir"
# Define manifest file path (we save it as <tag>)
manifest_file="$model_dir/manifests/registry.ollama.ai/library/$name/$tag"
if [ -e "$manifest_file" ]; then
die "[x] Manifest file $manifest_file already exists"
fi
# Construct the URL for the manifest
url="https://registry.ollama.ai/v2$base_path/$name/manifests/$tag"
echo "[=] Fetching manifest from: $url"
# Fetch the manifest using wget (quietly outputting to stdout)
manifest=$(wget -qO- "$url") || die "[x] Couldn't fetch manifest"
# Check for errors in the manifest
errors=$(jq -cn "$manifest | .errors")
[ "$errors" = "null" ] || die "$errors"
# Extract config digest from the manifest
config=$(jq -rn "$manifest | .config.digest") || die "[x] No config digest"
# Download the config blob with resume support
echo "[=] Downloading config: $config"
config_file="$model_dir/blobs/${config/:/-}"
wget -q --show-progress --continue --output-document="$config_file" "https://registry.ollama.ai/v2$base_path/$name/blobs/$config" || die "[x] Couldn't fetch config blob"
verify_hash "$config" "$config_file"
# Download each layer listed in the manifest
for layer in $(jq -rn "$manifest | .layers[].digest"); do
echo "[=] Downloading layer : $layer"
layer_file="$model_dir/blobs/${layer/:/-}"
wget -q --show-progress --tries=0 --read-timeout=30 --timeout=30 --waitretry=5 --continue --output-document="$layer_file" "https://registry.ollama.ai/v2$base_path/$name/blobs/$layer" || die "[x] Couldn't fetch layer $layer"
verify_hash "$layer" "$layer_file"
done
# Write the manifest file
echo -ne "$manifest" > "$manifest_file" || die "[x] Couldn't write manifest"
echo "[+] Download complete."
echo "[+] Files saved in $model_dir"
@parsapoorsh
Copy link
Author

poweruser@debian:~$ ~/Scripts/ollama-pull.sh granite-embedding:30m /media/poweruser/data/2-ollama-models/
[=] Fetching manifest from: https://registry.ollama.ai/v2/library/granite-embedding/manifests/30m
[=] Downloading config: sha256:b4bff7b70b564130d0b58da27a04a04e97143106fb0185e65a0c855db6999fdf
/media/poweruser/data/2-ollama-model 100%[=====================================================================>]     336  --.-KB/s    in 0s      
[=] Computing hash for: /media/poweruser/data/2-ollama-models/blobs/sha256-b4bff7b70b564130d0b58da27a04a04e97143106fb0185e65a0c855db6999fdf
[+] Hash verified
[=] Downloading layer : sha256:27d24c87a53d110b95abecbff83f966206857a9dc0ba1efd336d08dbd0afc833
/media/poweruser/data/2-ollama-model 100%[=====================================================================>]  59.63M  3.46MB/s    in 17s     
[=] Computing hash for: /media/poweruser/data/2-ollama-models/blobs/sha256-27d24c87a53d110b95abecbff83f966206857a9dc0ba1efd336d08dbd0afc833
[+] Hash verified
[=] Downloading layer : sha256:492069a62c2545ecde9f8cbc5c3d46adadaa3f95c0e1f68aea7ed2cca548484b
/media/poweruser/data/2-ollama-model 100%[=====================================================================>]  11.07K  --.-KB/s    in 0.01s   
[=] Computing hash for: /media/poweruser/data/2-ollama-models/blobs/sha256-492069a62c2545ecde9f8cbc5c3d46adadaa3f95c0e1f68aea7ed2cca548484b
[+] Hash verified
[+] Download complete.
[+] Files saved in /media/poweruser/data/2-ollama-models
poweruser@debian:~$ 

@Decdev125
Copy link

i modified your script to download models to default ollama location to save some work, it works great. thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment