# "ansible-galaxy collection download" always downloads?

**URL:** <https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870>\
**Category:** Get Help\
**Tags:** galaxy-ng\
**Created:** [November 20, 2025, 10:54am UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870 "2025-11-20T10:54:24Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [November 20, 2025, 10:54am UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/1 "2025-11-20T10:54:24Z")

</div>

I have a use case where I need to install a number of collections with ansible-galaxy. This process needs to happen many times, into various locations, but on the same machine.  
To avoid downloading things from [https://galaxy.ansible.com](https://galaxy.ansible.com) repeatedly, I am looking into ways to improve this.  
The obvious thing to consider is to use `ansible-galaxy collection download`. That works, and now I have my collections locally, and I can install those.  
BUT, each time I run `ansinble-galaxy collection download`, with the same requirements file, this downloads everything.

This obviously defeats what I’m trying to do…

I was hoping this would work similar to say, python’s `pip` which by default will cache all content in a single location locally, and use that on subsequent installs, and only download things that are not available locally.

Is there a way to make `ansible-galaxy` behave like this?

BTW such a feature would greatly reduce the burden on [https://galaxy.ansible.com](https://galaxy.ansible.com), which we know suffers from time to time…

---

<div class="post-metadata">

**Author:** ![webknjaz](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/webknjaz/32/5183_2.png) [@webknjaz](https://forum.ansible.com/u/webknjaz)\
**Post date:** [November 20, 2025, 11:27am UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/2 "2025-11-20T11:27:46Z")

</div>

There’s some HTTP cache, at least. But I think we might download the collections themselves. Feel free to file a feature request on the tracker.

Though, there’s room for discussion — pip has cache for wheels but its download command is meant for placing them into requested places on disk. I’d also be concerned about the disk space consumption as some projects can be rather big.

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [November 20, 2025, 1:36pm UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/3 "2025-11-20T13:36:30Z")

</div>

thx, I just added ["ansible-galaxy collection download" always downloads content · Issue #3562 · ansible/galaxy · GitHub](https://github.com/ansible/galaxy/issues/3562)

---

<div class="post-metadata">

**Author:** ![webknjaz](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/webknjaz/32/5183_2.png) [@webknjaz](https://forum.ansible.com/u/webknjaz)\
**Post date:** [November 20, 2025, 1:53pm UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/4 "2025-11-20T13:53:08Z")

</div>

That’s the web service repo. You want `ansible/ansible` for the CLI.

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [November 20, 2025, 3:14pm UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/5 "2025-11-20T15:14:53Z")

</div>

Right. Created ['ansible-galaxy collection download' always downloads · Issue #86233 · ansible/ansible · GitHub](https://github.com/ansible/ansible/issues/86233) instead

---

<div class="post-metadata">

**Author:** ![markstos](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/markstos/32/9706_2.png) [@markstos](https://forum.ansible.com/u/markstos)\
**Post date:** [November 20, 2025, 10:12pm UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/6 "2025-11-20T22:12:23Z")

</div>

In your case, are all the places you need to install the collections on the same machine in the same user account or different user accounts?

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [November 21, 2025, 9:48am UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/7 "2025-11-21T09:48:18Z")

</div>

On the same user account. So I had planned on using something like `~/.cache/ansible-galaxy-collections` for this

---

<div class="post-metadata">

**Author:** ![lennysh](https://sea1.discourse-cdn.com/ansible/user_avatar/forum.ansible.com/lennysh/32/9144_2.png) [@lennysh](https://forum.ansible.com/u/lennysh)\
**Post date:** [November 21, 2025, 6:56pm UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/8 "2025-11-21T18:56:59Z")

</div>

Not sure this helps, but I just download collections once, and use symlinks if I ever need them in other spots… Though, that use-case is very rare. I normally just build my collections in my EE’s, and then use the EE’s in various locations with navigator. I may require more information about your use case before I could help further.

---

<div class="post-metadata">

**Author:** ![dnmvisser](https://avatars.discourse-cdn.com/v4/letter/d/8e8cbc/32.png) [@dnmvisser](https://forum.ansible.com/u/dnmvisser)\
**Post date:** [October 27, 2026, 8:24am UTC](https://forum.ansible.com/t/ansible-galaxy-collection-download-always-downloads/44870/9 "2026-10-27T08:24:42Z")

</div>

I ended up writing some shell wrapper to iterate over the galaxy requirements and storing the content locally first. Once done, any subsequent run will use the cached content. This saves time/resources, and also avoids dependencies on [galaxy.ansible.com](http://galaxy.ansible.com) (which may not always be online). Note that this does not do dependency resolution, but that is OK in our case as we have listed everything statically in the `ansible_galaxy.yml` file.  
Boilerplate:

```bash
#!/usr/bin/env bash

# Check for ansible_galaxy.yml file
GALAXY_REQS="ansible_galaxy.yml"

usage() {
  local script_name
  script_name=$(basename "$0")
  cat <<EOF
Usage: ${script_name} [-h|--help]

Cache and install Ansible Galaxy collections defined in ${GALAXY_REQS}.

Options:
  -h, --help Display this help message and exit
EOF
}

for arg in "$@"; do
  case "$arg" in
    -h|--help)
      usage
      exit 0
      ;;
  esac
done

# Check for required tools
for cmd in yq jq ansible-galaxy python3; do
  if ! command -v "$cmd" >/dev/null 2>&1; then
    echo "Error: Required command '${cmd}' not found in PATH." >&2
    exit 1
  fi
done

if [! -r "${GALAXY_REQS}"]; then
  echo "No readable ansible_galaxy.yml file found, but we do need one.
Exiting"
  exit 1
fi

# Set commands
PYTHON="python3"
GALAXY="ansible-galaxy"

# Galaxy part
function install_galaxy_requirements() {

  CACHE_DIR="${HOME}/.cache/galaxy"
  if [! -d "$CACHE_DIR"]; then
    mkdir -p "$CACHE_DIR"
  fi

  # Start with empty data structure, to build a local requirements file
  TEMP_REQS="$(mktemp).yml"
  echo -e "---\ncollections: []" > "${TEMP_REQS}"

  # Iterate over requirements
  yq -rc '.collections[]' "${GALAXY_REQS}" | while read -r item; do
    NAME=$(jq -r '.name' <<< "$item")
    SOURCE=$(jq -r '.source // "https://galaxy.ansible.com"' <<< "$item")
    VERSION=$(jq -r '.version' <<< "$item")
    CACHE_FILE="${CACHE_DIR}/${NAME/./-}-${VERSION}.tar.gz"
    if [! -f "${CACHE_FILE}"]; then
      # Download collection
      "$GALAXY" collection download \
        --server "${SOURCE}" \
        --download-path "${CACHE_DIR}" \
        "${NAME}:${VERSION}"
    else
      echo "Collection $NAME is available in cache, not downloading it"
    fi

    # Append item to collection list in the cached requirements file.
    # For the "name" we use the absolute path to the tarball
    yq --yaml-output --in-place \
      --arg name "$(realpath "${CACHE_DIR}")/${NAME/./-}-${VERSION}.tar.gz" \
      --arg version "${VERSION}" \
      '.collections += [{name: $name, version: $version}]' \
      "${TEMP_REQS}"
  done

  # Now install from the cached requirements file
  ANSIBLE_COLLECTIONS_PATH=$("${PYTHON}" -c "import site; print(site.getsitepackages()[0])") \
    "$GALAXY" collection install -r "${TEMP_REQS}"

  # Remove temp requirements file
  rm -f "${TEMP_REQS}"
}

# Install the collections
install_galaxy_requirements

```
