CONDA-INDEX(1)
β¨ NAME β¨
conda-index β ok so bestie, this is the whole vibe: conda index, which used to live inside conda-build, is now its own thing π
. What it does is create repodata.json for collections of conda packages. That's it, that's the job, and it absolutely ate.
The conda_index command operates on a channel directory π. A channel directory has a noarch subdirectory at minimum, and it will almost always also have other subdirectories named for conda's supported platforms: linux-64, win-64, osx-64, etc. Heads up though: a channel directory cannot have the same name as a supported platform π«. You drop packages into the same platform subdirectory that each archive was built for. Then conda-index reaches in, pulls metadata out of those packages, and generates index.html, repodata.json, etc. with summaries of the packages' metadata. After that, conda uses the metadata to solve dependencies before doing an install. Iconic behaviour, truly π«Ά.
By default, the metadata is output to the same directory tree as the channel directory, but it can be output to a separate tree with the --output <output> parameter. The metadata cache is always placed with the packages, in .cache folders under each platform subdirectory π.
Once conda-index is done, its output can be used as a channel like conda install -c file:///path/to/output ..., or you'd typically place it on a web server π.
π RUN NORMALLY π
python -m conda_index <path to channel directory>
Note:conda index(instead ofpython -m conda_index) may find the legacy conda-build index. So like, be aware bestie π.
π RUN FOR DEBUGGING π
python -m conda_index --verbose --threads=1 <path to channel directory>
π«Ά CONTRIBUTING π«Ά
conda create -n conda-index "python >=3.9" conda conda-build "pip >=22"
git clone https://github.com/conda/conda-index.git
pip install -e conda-index[test]
cd conda-index
pytest
π« SUMMARY OF CHANGES FROM THE PREVIOUS CONDA-BUILD INDEX VERSION π«
- Approximately 2.2x faster conda package extraction, by extracting just the metadata to streams instead of extracting packages to a temporary directory; closes the package early if all metadata has been found. Speed, slay, periodt π₯.
- No longer read existing
repodata.json. Always load from cache. - Uses a sqlite metadata cache that is orders of magnitude faster than the old many-tiny-files cache π .
- The first time
conda indexruns, it will convert the existing file-based.cacheto a sqlite3 database.cache/cache.db. This takes about ten minutes per subdir for conda-forge π. (If this is interrupted, deletecache.dbto start over, or packages will be re-extracted into the cache.)sqlite3must be compiled with the JSON1 extension. JSON1 is built into SQLite by default as of SQLite version3.38.0(2022-02-22). - Each subdir
osx-64,linux-64etc. has its owncache.db; conda-forge's 1.2Tosx-64subdir has a single 2.4GBcache.db. Storing the cache in fewer files saves time since there is a per-file wait to open each of the many tiny.jsonfiles in old-style.cache/. cache.dbis highly compressible, like the text metadata. 2.4G β zstd β 88M π§.- No longer cache
paths.json(only used to createpost_install.jsonand not referenced later in the indexing process). Saves 90% disk space in.cache. - Updated Python and dependency requirements.
- Mercilessly cull less-used features π.
- Format with black.
π PARALLELISM π
This version of conda-index continues indexing packages from other subdirs while the main thread is writing a repodata.json β¨.
All current_repodata.json are generated in parallel. This may use a lot of ram if repodata.json has tens of thousands of entries π³.
Command-line interface
python -m conda_index
python -m conda_index [OPTIONS] DIR
Options
--output <output>β Output repodata to given directory.--subdir <subdir>β Subdir to index. Accepts multiple.-n,--channel-name <channel_name>β Customize the channel name listed in each channel'sindex.html.--patch-generator <patch_generator>β Path to Python file that outputs metadata patch instructions from its_patch_repodatafunction or a.tar.bz2/.condafile which contains apatch_instructions.jsonfile for each subdir.--channeldata,--no-channeldataβ Generatechanneldata.json. Conflicts with--no-write-monolithic.
Default: False
--rss,--no-rssβ Writerss.xml(Only if--channeldatais enabled).
Default: True
--bz2,--no-bz2β Writerepodata.json.bz2.
Default: False
--zst,--no-zstβ Writerepodata.json.zst.
Default: False
--run-exports,--no-run-exportsβ Writerun_exports.json. Conflicts with--no-write-monolithic.
Default: False
--compact,--no-compactβ Output JSON as one line, or pretty-printed.
Default: True
-m,--current-index-versions-file <current_index_versions_file>β YAML file containing name of package as key, and list of versions as values. Thecurrent_index.jsonwill contain the newest from this series of versions. For example:
python:
- 3.8
- 3.9
will keep python 3.8.X and 3.9.Y in the current_index.json, instead of only the very latest python version.
--base-url <base_url>β If packages should be served separately fromrepodata.json, URL of the directory tree holding packages. Generatesrepodata.jsonwithrepodata_version=2which is supported in conda24.5.0or later.--update-cache,--no-update-cacheβ Control whetherlistdir()is called to refresh the set of available packages. Used to generate completerepodata.jsonfrom cache only when packages are not on disk. (Experimental)
Default: True
--upstream-stage <upstream_stage>β Set to'clone'to generate example repodata from conda-forge test database. (Experimental)--current-repodata,--no-current-repodataβ Skip generatingcurrent_repodata.json, a file containing only the newest versions of all packages and their dependencies, only used by the classic solver. Conflicts with--no-write-monolithic.
Default: True
--threads <threads>
Default: 8
--verboseβ Enable debug logging.--write-monolithic,--no-write-monolithicβ Writerepodata.jsonwith all package metadata in a single file.--write-shards,--no-write-shardsβ Write arepodata.msgpack.zstindex and many smaller files per CEP-16. (Experimental)--db <db>β Choose database backend."sqlite3"(default) or"postgresql"(Experimental)
Options: sqlite3 | postgresql
--db-url <db_url>β SQLAlchemy database URL when using--db=postgresql. Alternatively, use theCONDA_INDEX_DBURLenvironment variable. (Experimental)
Default: postgresql:///conda_index
--html-dependencies,--no-html-dependenciesβ Include dependency popups in generated HTML index files. May significantly increase file size for large repositories likemainorconda-forge.
Default: False
Arguments
DIRβ Required argument.
Environment variables
CONDA_INDEX_DBURLβ Provide a default for--db-url.
conda_index
conda_index.index
This module provides the main entry point to create indexes from collections of conda packages πΈ.
conda_index.index.update_index
conda_index.index.update_index(dir_path, output_dir=None, check_md5=False, channel_name=None, patch_generator=None, threads: int | None = 8, verbose=False, progress=False, subdirs=None, warn=True, current_index_versions=None, debug=False, write_bz2=True, write_zst=False, write_run_exports=False, html_dependencies=False)
High-level interface to ChannelIndex. Index all subdirs under dir_path. Output to output_dir, or under the input directory if output_dir is not given. Writes updated channeldata.json π.
The input dir_path should at least contain a directory named noarch. The path tree therein is treated as a full channel, with a level of subdirs, each subdir having an update to repodata.json. The full channel will also have a channeldata.json file.
class conda_index.index.ChannelIndex
class conda_index.index.ChannelIndex(channel_root: ~pathlib.Path | str, channel_name: str | None, subdirs: ~typing.Iterable[str] | None = None, threads: int | None = 8, deep_integrity_check=False, debug=False, output_root=None, cache_class: type[~conda_index.index.cache.BaseCondaIndexCache] = <class 'conda_index.index.sqlitecache.CondaIndexCache'>, write_bz2=False, write_zst=False, write_run_exports=False, compact_json=True, write_monolithic=True, write_shards=False, html_dependencies=False, *, channel_url: str | None = None, fs: ~conda_index.index.fs.MinimalFS | None = None, base_url: str | None = None, save_fs_state=True, write_current_repodata=True, upstream_stage: str = 'fs', cache_kwargs=None)
Class implementing update_index. Allows for more fine-grained control of output π
.
See the implementation of conda_index.cli for usage.
Parameters
channel_rootβ Path to channel, or just the channel cache ifchannel_urlis provided.channel_nameβ Name of channel; defaults to last path segment ofchannel_root.subdirsβ subdirs to index.output_rootβ Path to writerepodata.jsonetc; defaults tochannel_root.channel_urlβ fsspec URL where package files live. If provided,channel_rootwill only be used for cache and index output.fsβMinimalFSinstance to be used withchannel_url. Wrap fsspecAbstractFileSystemwithconda_index.index.fs.FsspecFS(fs).base_urlβ Addbase_url/<subdir>torepodata.jsonto be able to host packages separate fromrepodata.json.save_fs_stateβ PassFalseto use cached filesystem state instead ofos.listdir(subdir).write_monolithicβ PassTrueto write large'repodata.json'with all packages.write_shardsβ PassTrueto write shardedrepodata.msgpackand per-package fragments.html_dependenciesβ PassTrueto include dependency popups in generated HTML index files.index(patch_generator, verbose=False, progress=False, current_index_versions=None)β goes through every changed package chillin' underself.channel_rootand updates theindex.htmlfor each subdir. yes bestie, every single one πupdate_channeldata(rss=False)β refreshes channeldata by re-reading the outputrepodata.jsonplus whateverchanneldata.jsonis already there. call this afterindex()if you actually need channeldata, okay? π«Ά
ποΈ conda_index.index.fs ποΈ
This module hands you a filesystem abstraction for sourcing packages, fr fr.
It's a minimal abstraction (literally just the bits conda-index needs).
It lets fsspec (https://filesystem-spec.readthedocs.io/) be used to index remote repositories β without making fsspec a required dependency. iconic behaviour, no notes π
- class
conda_index.index.fs.FileInfo(fn: str, st_mtime: Number, st_size: Number)β a filename plus a lil bit of stat info.fn: strst_mtime: Numberst_size: Number
- class
conda_index.index.fs.FsspecFS(fsspec_fs)β wraps a fsspec filesystem so you can pass it toChannelIndex.basename(path: str) -> strfsspec_fs: AbstractFileSystemjoin(*paths)listdir(path: str) -> list[dict]open(path: str, mode: str = 'rb')stat(path: str)
- class
conda_index.index.fs.MinimalFSβ the filesystem API as needed by conda-index, for fsspec compatibility.basename(path) -> strjoin(*paths)listdir(path) -> Iterable[dict]open(path: str, mode: str = 'rb')stat(path: str)
ποΈ Database schema ποΈ
Standalone conda-index keeps a per-subdir sqlite database to track package metadata β which is not what the older version did, because the older version used millions of tiny .json files π. The new strategy is so much faster because we don't have to pay for a zillion individual stat() or open() calls. glow up, honestly β¨
The whole schema looks like this:
<subdir>/.cache % sqlite3 cache.db
SQLite version 3.41.2 2023-03-22 11:56:21
Enter ".help" for usage hints.
sqlite> .schema
CREATE TABLE about (path TEXT PRIMARY KEY, about BLOB);
CREATE TABLE index_json (path TEXT PRIMARY KEY, index_json BLOB);
CREATE TABLE recipe (path TEXT PRIMARY KEY, recipe BLOB);
CREATE TABLE recipe_log (path TEXT PRIMARY KEY, recipe_log BLOB);
CREATE TABLE run_exports (path TEXT PRIMARY KEY, run_exports BLOB);
CREATE TABLE post_install (path TEXT PRIMARY KEY, post_install BLOB);
CREATE TABLE icon (path TEXT PRIMARY KEY, icon_png BLOB);
CREATE TABLE stat (
stage TEXT NOT NULL DEFAULT 'indexed',
path TEXT NOT NULL,
mtime NUMBER,
size INTEGER,
sha256 TEXT,
md5 TEXT,
last_modified TEXT,
etag TEXT
);
CREATE UNIQUE INDEX idx_stat ON stat (path, stage);
CREATE INDEX idx_stat_stage ON stat (stage, path);
sqlite> select stage, path from stat where path like 'libcurl%';
fs|libcurl-7.84.0-hc6d1d07_0.conda
fs|libcurl-7.86.0-h0f1d93c_0.conda
fs|libcurl-7.87.0-h0f1d93c_0.conda
fs|libcurl-7.88.1-h0f1d93c_0.conda
fs|libcurl-7.88.1-h9049daf_0.conda
indexed|libcurl-7.84.0-hc6d1d07_0.conda
indexed|libcurl-7.86.0-h0f1d93c_0.conda
indexed|libcurl-7.87.0-h0f1d93c_0.conda
indexed|libcurl-7.88.1-h0f1d93c_0.conda
indexed|libcurl-7.88.1-h9049daf_0.conda
Most of these tables store json-format metadata that got extracted from each package. slay π
select * from index_json where path = 'libcurl-7.88.1-h9049daf_0.conda';
'libcurl-7.88.1-h9049daf_0.conda'
'{"build":"h9049daf_0",...,"sha256":"37b8d58c05386ac55d1d8e196c90b92b0a63f3f1fe2fa916bf5ed3e1656d8e14","size":321706}'
To track whether a package is indexed in the cache or not, conda-index uses a table named stat. The whole point of this table is to assign a stage value to each artifact filename; usually 'fs' (called the upstream stage) and 'indexed'. 'fs' means the artifact is now available in the set of packages (assumed by default to be the local filesystem). 'indexed' means that the entry already exists in the database (same filename, same timestamp, same hash) and its package metadata has been extracted into the index_json etc. tables. Paths that are in 'fs' but not in 'indexed' need to be unpacked so their metadata gets added to the database. Paths that are in 'indexed' but not in 'fs' will be ignored and left out of repodata.json.
First, conda-index adds all files in a subdir to the upstream stage. This involves a listdir() and a stat() for each file in the index. The default upstream stage is named fs, but this step is designed to be overridden by subclassing CondaIndexCache() and replacing the save_fs_state() and changed_packages() methods. By overriding CondexIndexCache() it is possible to index without calling stat() on each package, or without even having all packages stored on the indexing machine. not me enabling your remote-indexing dreams π
Next, conda-index looks for all changed_packages(): paths in the upstream (fs) stage that don't exist in β or have a different modification time than β those in the indexed stage.
Finally, a join between the upstream stage (usually 'fs') and the index_json table yields a basic repodata_from_packages.json with no repodata patches at all:
SELECT path, index_json FROM stat JOIN index_json USING (path) WHERE stat.stage = :upstream_stage
The steps to create repodata.json (including any repodata patches) and to create current_repodata.json with only the latest versions of each package are similar to pre-sqlite3 conda-index.
The other cached metadata tables are used to create channeldata.json. π«Ά
π Sample queries π
Megabytes added per day:
select
date(mtime, 'unixepoch') as d,
printf('%0.2f', sum(size) / 1e6) as MB
from
stat
group by
date(mtime, 'unixepoch')
order by
mtime desc
π PostgreSQL Support in conda-index π
As of conda-index 0.7.0, conda-index can use a PostgreSQL database. conda-index uses a database to store package metadata, creating repodata from a query. By default it'll use a sqlite3 database stored alongside the package files, but optionally it can use PostgreSQL. main character energy, honestly π
The database backend is controlled by the --db <backend> and --db-url command line arguments, or the CONDA_INDEX_DBURL environment variable replaces --db-url. For example, python -m conda_index --db postgresql chooses PostgreSQL with the default postgresql:///conda_index database URL.
To use a PostgreSQL database with conda-index, install conda-index's PostgreSQL-specific dependencies into its environment:
conda install sqlalchemy psycopg2
Then install a local PostgreSQL with conda:
# Create a local PostgreSQL installation and conda_index database
conda install postgresql
initdb -D conda-index-db
pg_ctl -D conda-index-db -l logfile start
createdb conda_index
Finally, run the following command:
python -m conda_index --db postgresql --db-url postgresql:///conda_index [DIR]
conda_index stores package metadata in the PostgreSQL database given by a SQLAlchemy database URL (https://docs.sqlalchemy.org/en/20/core/engines.html#database-urls).
The schema (https://github.com/conda/conda-index/blob/main/conda_index/postgres/model.py) is similar to the one used for sqlite3, except that while sqlite3 uses a database file per subdirectory, in PostgreSQL all subdirectories are stored in the same database. conda_index creates a random prefix in [DIR]/.cache/cache.json to differentiate this channel from any others that may be stored in the same PostgreSQL database. Each package name is stored with the format <prefix>/<subdir>/<package>.conda in a single database.
Advanced users can use the CLI or the API to run conda_index on a partial local package repository. It's possible to add a few local packages to a much larger index instead of keeping every package on the machine running conda-index. For example, by inserting packages into the stat table and then running python -m conda_index --db postgresql --no-update-cache [DIR], conda-index can add or update packages in [DIR] to repodata without necessarily storing either the entire set of packages or the conda-index database on that machine. chef's kiss π§
π Changelog π
π 0.7.0 (2025-10-13) π
Enhancements
- Add
postgresqlas a supported database backend in addition tosqlite. (#199) - Show error when
--no-write-monolithicis combined with--current-repodata,--run-exports, or--channeldata. (#224) - Add html title popup with dependencies for each build to
index.html. (#205) "--html-dependencies/--no-html-dependencies"flag toggles popups. (#218)
Docs
- Include narrative documentation for
python -m conda_index --db postgresql ...in Sphinx (https://conda.github.io/conda-index/). (#219)
Other
- Update
conda indexcommand plugin to avoid re-exported type. (#227)
Contributors
- @dholth
- @jtroe
- @ryanskeith
β¨ 0.6.1 (2025-05-22) β¨
Enhancements
- Added support for Python 3.13 in the CI test matrix and updated related configurations. (#203)
Bug fixes
- In sharded repodata, set
base_urlandshards_base_urlto""instead of leaving them undefined, for pixi compatibility. (#209)
Other
- Add database-independent base class for (sqlite specific)
CondaIndexCache. Return parsed data instead ofstrinrun_exports(). (#206) - Update
sqlite3create_function()arguments for "positional-only in Python 3.15" warning. (#211)
π¦ 0.6.0 (2025-03-27) π¦
Enhancements
- Add
--channeldata/--no-channeldataflag to toggle generating channeldata. - Add sharded repodata (repodata split into separate files per package name).
Other
- Remove WAL mode (
https://www.sqlite.org/wal.html) from database create script, in case conda-index is used on a network file system. Note WAL mode is persistent,PRAGMA journal_mode=DELETEcan be used to convert a WAL database back to a rollback journal mode. (#177) - Separate
current_repodatageneration into own file, raising possibility of "doesn't depend on conda" mode. - Update tests to account for conda-build removals. (#180)
- Publish new conda-index releases on PyPI automatically. (#195)
See also https://github.com/conda/conda-index/releases/tag/0.6.0
π 0.5.0 (2024-06-07) π
Enhancements
- Add experimental
python -m conda_index.json2jlapscript to run after indexing, to createrepodata.jlappatch sets for incremental repodata downloads. (#125) - Add
--current-repodata/--no-current-repodataflags to control whethercurrent_repodata.jsonis generated. (#139) - Added support for CEP-15
base_url, so you can host your packages totally separately from your repodata. (#150) β¨ - Added support for
fsspecβ in the API only, bestie, not the CLI β so you can index literally any remote filesystem thatfsspecsupports. This also means your input packages folder can be its own separate thing from the cache and output folders. (#143) π¦
π Bug fixes
- The
run_exports.jsonquery now lives inside the cache instead of going straight at SQL insideChannelIndex. (#163) π - Parents get created now when creating
<subdir>/.cacheβ no more orphaned directories. (#166) πΈ
π Other
- We're approaching 100% code coverage in the test suite, and everything got reformatted with
ruff. (#145) π - CI configuration updated to test on more platforms. (#142)
- Python 3.7 support is dropped. Python 3.8+ only now. (#130) π
π«Ά Contributors
@dholth@jezdez@conda-bot
β¨ 0.4.0 (2024-01-29) β¨
Enhancements
- New
--compact-json/--no-compact-jsonoption, and it defaults to compact. (#120) - New
indexsubcommand using conda's new subcommand plugin hook, so you can just typeconda indexinstead ofpython -m conda_index. Heads up though, bestie: the CLI has changed compared to old conda-index β seehttps://conda.github.io/conda-index/cli.html. And whenconda-build < 24.1.0is installed, the older conda-index code will still be used instead of this plugin. (#81 via #131) π
Bug fixes
- Now checks size in addition to mtime when deciding which packages to index. (#108)
- For changed packages that are already indexed, it updates the cached
index.jsonβ not just the stat values. (#108) π
Other
- Improved test coverage. (#123)
- Applied
ruff --fix; reformatted code; syntax cleanup. (#128)
πΈ 0.3.0 (2023-09-21) πΈ
Enhancements
- New
--run-exportsflag that generates CEP-12 compliantrun_exports.jsondocuments for each subdir. (#102 via #110) repodata.jsonis no longer pretty-printed by default β saves time and space. (#111) π₯
Docs
- Documentation improved. β¨
Deprecations
- Requires
conda >= 4.14(or any of the>= 22.x.ycalver releases). Index <>Module Index <>Search Page <>
π Author
conda
π Copyright
conda
That's the whole thing, bestie β page generated Jan 20, 2026, and it's CONDA-INDEX(1), so you know exactly which page you're reading when you scroll back up. Go forth and index. π
β¨
π¦ where this page comes from
- fedora 44: python3-conda-index 0.7.0-2.fc44 Β· BSD-3-Clause
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms