Conda create/install fails in read-only multi-user deployments

We run a shared system installation of Anaconda in a read-only location, on a multi-user system, where regular users can: use Anaconda content as such, or providing supplemental packages installed in their own home directories, or creating individual virtual environments using conda from the shared Anaconda deployment. In all cases the system (base) Anaconda deployment remains (read-only) untouched to preserve system integrity, and we force all user specific changes into their respective home dirs. This model worked fine until Anaconda 2023-09 (Python 3.11, Conda 23.x) inclusive.

Since upgrading to Anaconda 2025-06 (Py 3.13, Conda 25.x), and also tested with Anaconda 2024-x (Py 3.12, Conda 24.x), we seem to not be able to maintain this model as the new versions of conda(?) seem to insist on – at least-- being able to write (or open r/w) content in <anacondaPrefix>/pkgs/cache, for (at least) any invocation of “conda create” or “conda install”.

Here is the traceback of this failure:

(base) (live)razvan@research30:~$ conda create -n toto-2
2 channel Terms of Service accepted
Channels:
 - defaults
Platform: linux-64
Collecting package metadata (repodata.json): failed

# >>>>>>>>>>>>>>>>>>>>>> ERROR REPORT <<<<<<<<<<<<<<<<<<<<<<

    Traceback (most recent call last):
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/exception_handler.py", line 28, in __call__
        return func(*args, **kwargs)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/cli/main.py", line 61, in main_subshell
        exit_code = do_call(args, parser)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/cli/conda_argparse.py", line 206, in do_call
        result = getattr(module, func_name)(args, parser)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/notices/core.py", line 132, in wrapper
        return func(*args, **kwargs)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/cli/main_create.py", line 153, in execute
        install(args, parser, "create")
        ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/cli/install.py", line 440, in install
        unlink_link_transaction = solver.solve_for_transaction(
            deps_modifier=deps_modifier,
        ...<4 lines>...
            ),
        )
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/core/solve.py", line 153, in solve_for_transaction
        unlink_precs, link_precs = self.solve_for_diff(
                                   ~~~~~~~~~~~~~~~~~~~^
            update_modifier,
            ^^^^^^^^^^^^^^^^
        ...<5 lines>...
            should_retry_solve,
            ^^^^^^^^^^^^^^^^^^^
        )
        ^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/core/solve.py", line 222, in solve_for_diff
        final_precs = self.solve_final_state(
            update_modifier,
        ...<4 lines>...
            should_retry_solve,
        )
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/solver.py", line 166, in solve_final_state
        index = self._collect_all_metadata(
            channels=channels,
        ...<2 lines>...
            in_state=in_state,
        )
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/common/io.py", line 87, in decorated
        return f(*args, **kwds)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/solver.py", line 265, in _collect_all_metadata
        index = LibMambaIndexHelper(
            channels=[*conda_build_channels, *channels],
        ...<6 lines>...
            pkgs_dirs=context.pkgs_dirs if context.offline else (),
        )
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/index.py", line 181, in __init__
        self.repos: list[_ChannelRepoInfo] = self._load_channels()
                                             ~~~~~~~~~~~~~~~~~~~^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/index.py", line 285, in _load_channels
        urls_to_json_path_and_state = self._fetch_repodata_jsons(tuple(urls_to_channel.keys()))
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/index.py", line 349, in _fetch_repodata_jsons
        for (url, path, state) in executor.map(self._fetch_one_repodata_json, urls)
                                  ~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/apps/anaconda3/lib/python3.13/concurrent/futures/_base.py", line 619, in result_iterator
        yield _result_or_cancel(fs.pop())
              ~~~~~~~~~~~~~~~~~^^^^^^^^^^
      File "/apps/anaconda3/lib/python3.13/concurrent/futures/_base.py", line 317, in _result_or_cancel
        return fut.result(timeout)
               ~~~~~~~~~~^^^^^^^^^
      File "/apps/anaconda3/lib/python3.13/concurrent/futures/_base.py", line 456, in result
        return self.__get_result()
               ~~~~~~~~~~~~~~~~~^^
      File "/apps/anaconda3/lib/python3.13/concurrent/futures/_base.py", line 401, in __get_result
        raise self._exception
      File "/apps/anaconda3/lib/python3.13/concurrent/futures/thread.py", line 59, in run
        result = self.fn(*self.args, **self.kwargs)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda_libmamba_solver/index.py", line 375, in _fetch_one_repodata_json
        json_path, state = subdir_data.repo_fetch.fetch_latest_path()
                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/gateways/repodata/__init__.py", line 749, in fetch_latest_path
        _, state = self.fetch_latest()
                   ~~~~~~~~~~~~~~~~~^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/gateways/repodata/__init__.py", line 789, in fetch_latest
        cache.load_state()
        ~~~~~~~~~~~~~~~~^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/gateways/repodata/__init__.py", line 585, in load_state
        self.load(state_only=True)
        ~~~~~~~~~^^^^^^^^^^^^^^^^^
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/gateways/repodata/__init__.py", line 532, in load
        with self.lock("r+") as state_file:
             ~~~~~~~~~^^^^^^
      File "/apps/anaconda3/lib/python3.13/contextlib.py", line 141, in __enter__
        return next(self.gen)
      File "/apps/anaconda3/lib/python3.13/site-packages/conda/gateways/repodata/__init__.py", line 651, in lock
        with self.cache_path_state.open(mode) as state_file, lock(state_file):
             ~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^
      File "/apps/anaconda3/lib/python3.13/pathlib/_local.py", line 537, in open
        return io.open(self, mode, buffering, encoding, errors, newline)
               ~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    PermissionError: [Errno 13] Permission denied: '/apps/anaconda3/pkgs/cache/47929eba.info.json'

`$ /apps/anaconda3/bin/conda create -n toto-2`

  environment variables:
                 CIO_TEST=<not set>
    CONDA_ALLOW_SOFTLINKS=false
CONDA_BACKUP_QT_XCB_GL_INTEGRATION=
        CONDA_DEFAULT_ENV=base
                CONDA_EXE=/apps/anaconda3/bin/conda
             CONDA_PREFIX=/apps/anaconda3
    CONDA_PROMPT_MODIFIER=(base)
         CONDA_PYTHON_EXE=/apps/anaconda3/bin/python
               CONDA_ROOT=/apps/anaconda3
              CONDA_SHLVL=1
           CURL_CA_BUNDLE=<not set>
          LD_LIBRARY_PATH=:/opt/gurobi/linux64/lib
               LD_PRELOAD=<not set>
                     PATH=/apps/anaconda3/bin:/apps/anaconda3/condabin:/usr/local/bin:/usr/bin:/
                          bin:/apps/wrappers:/apps/bin:/opt/n1ge/bin
                          /lx24-amd64:/opt/gurobi/linux64/bin
       REQUESTS_CA_BUNDLE=<not set>
            SSL_CERT_FILE=<not set>

     active environment : base
    active env location : /apps/anaconda3
            shell level : 1
       user config file : /home/razvan/.condarc
 populated config files : /apps/anaconda3/.condarc
          conda version : 25.5.1
    conda-build version : 25.5.0
         python version : 3.13.5.final.0
                 solver : libmamba (default)
       virtual packages : __archspec=1=x86_64_v4
                          __conda=25.5.1=0
                          __glibc=2.28=0
                          __linux=4.19.0=0
                          __unix=0=0
       base environment : /apps/anaconda3  (read only)
      conda av data dir : /apps/anaconda3/etc/conda
  conda av metadata url : None
           channel URLs : [...]
          package cache : /apps/anaconda3/pkgs
                          /home/razvan/.conda/pkgs
       envs directories : /home/razvan/.conda/envs
                          /apps/anaconda3/envs
               platform : linux-64
             user-agent : conda/25.5.1 requests/2.32.3 CPython/3.13.5 Linux/4.19.0-25-amd64 debian/10.13 glibc/2.28 solver/libmamba conda-libmamba-solver/25.4.0 libmambapy/2.0.5 aau/0.7.1 c/. s/. e/.
                UID:GID : 1000:1000
             netrc file : None
           offline mode : False


An unexpected error has occurred. Conda has prepared the above report...
[...]
# >>>>>>>>>>>>>>>>>>>>>> ... <<<<<<<<<<<<<<<<<<<<<<

  • Is this a new feature introduced in Conda 24.x or is this a regression? We discovered the same behavior in Conda 24.x and 25.x.

  • One would have expected the system try other locations in the search list when the failure above was encountered! Afterall “pkgs_dirs” var (see “package cache” list in the error report) includes usable alternatives by default!

  • Is there a way to force the system to let users proceed without write access to the Anaconda folders, using a system-only (not user) configuration change? (We discovered that altering the “pkgs_dirs” variable in user’s .condarc config files can accomplish that, but we don’t want to ask each individual user to perform the change.)

  • Lastly, we observed what looks like an awkward behavior if we do grant user write access to the package cache directory: conda create or conda install will use <AnacondaPrefix>/pkgs/cache to store indices but will store the downloaded packages in the user’s home dir at ~/.conda/pkgs. Somewhat logically inconsistent - won’t you agree?!

Are we missing some wiser approach here, or do we have a regression?!

Thanks for any suggestion, comment, fix or form of support!
Razvan