nixos-search: the search engine behind the Nix package catalogue
Search NixOS packages and options
At a glance
- What is it?
- search.nixos.org is the place you go to find out whether a package exists in the Nix package collection, and the repository behind it is a small system with a big idea: the catalogue used to be one large JSON file that the browser downloaded whole, and it was replaced with a real search backend, an index that can be queried and ranked, with a typed front end on top of it.
- Who is it for?
- nixos-search fits a Nix user who needs to find a package or an option, and a contributor who wants their own flake listed in the search. It does not fit somebody who wants to run the site as their own package index, since the pipeline is tied to the official collection and the importer.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository received new commits within the last day.
- What is it written in?
- Mainly Elm, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Why one big JSON file stopped working
The section on how the project came to be is the most useful thing in the readme, because it explains a design decision that would otherwise look arbitrary.
The original approach was to replace the search for packages and options with a single JSON file containing all of them, which the browser downloaded. And the readme's judgement of that approach is that it is good for its simple setup but started to show its problems as the number of packages got bigger and bigger.
That is a well known failure mode, and it is worth being precise about why it happens. A single file means the whole catalogue is downloaded before anything can be shown, and the size of that download grows with the number of packages, so the site gets slower for everyone as the collection grows, and the slowness is not proportional to what the user asked for. It is also a single point of failure for the whole feature, and it makes the search itself impossible, because there is no index to query.
And then the sentence that shows how the decision was actually made: the author says the setup could be optimised further, but the idea of having a database at the back was too tempting not to try.
So the project is a search engine in the ordinary sense, and the readme says what the use case is: a visitor wants to see whether a package exists, or to look up the details of a particular package.
The backend is an Elasticsearch instance, sponsored by a hosting company, and the front end is written in Elm.
Those two choices together are the design. A search engine gives you ranking, which a downloaded file cannot, and a compiled language with no runtime dependencies gives you a front end that is fast and small, which matters when the whole point of the exercise was to stop shipping the catalogue to the browser.
The project is MIT licensed, the site is search.nixos.org, the last push was on 2026-10-16, and the recorded primary language is Elm.
What the search is actually trying to do
The search section describes the behaviour the user should see, and it is written in terms of what a person wants rather than in terms of a query language.
The first goal is convergence. A user wants to converge to a single result if possible, and the more characters are added to a query the narrower the search becomes, and so the fewer results should be shown.
That is a design decision about the interface, not about the index. A conventional search engine returns as many results as match and lets the user narrow down. Here, the interface narrows for you, which means a query of a few characters is broad and a long one is precise, and the result list shrinks as you type. For a catalogue of packages, where the user usually has a name in mind, that is the right behaviour: the person typing a long package name wants the one package, not a ranked list of two hundred.
The second goal is ranking, and the readme is honest about why it matters: it is often hard to produce a search query that outputs only one result item, so the ordering is what brings the most relevant results to the top.
So narrowing and ranking work together. The narrowing reduces the set, and the ranking decides which of what remains is at the top, and the second is what makes the first usable when the query is ambiguous.
The third element is the least important by the readme's own account, and it is the one that is easiest to overlook: suggestions for writing a better search query. The point is that the suggestion feature should guide the user towards writing a query that will produce better results, which in turn produces a better result list. So the type-ahead is not a convenience, it is a way of making the user more likely to write a query the index can answer precisely.
Taken together, these three behaviours are what distinguish a catalogue search from a filter. The index does the matching, the interface does the narrowing, the ranking decides the order, and the suggestions teach the user how to use the first two.
The toolchain, and the two pieces a contributor needs
The development section is a set of commands, and the fact that they all go through a development environment is the first thing to notice.
The instructions are to open a terminal and run a command that enters the development environment and then installs the front end dependencies.
nix develop -c npm --prefix=frontend installAnd then a second one that starts the development server.
nix develop -c npm --prefix=frontend run devThe browser then points at a local port, and any change to the front end source triggers a hot reload. And the readme adds the sentence that explains why the development setup is worth the trouble: this lets you test the front end against the production package index.
That is a genuinely good decision, and it is the opposite of the usual one. Most search front ends are developed against a fixture, a small sample of the data, and the difference between the sample and the real thing is where the bugs live, because the sample has a hundred packages and the real index has a hundred thousand. Here, a contributor runs the real front end against the real index and sees exactly what a user sees.
The rest of the tooling is the same idea. There is a command to run the type checker over the Elm code, with a flag for automatic fixes and a flag to run it in watch mode during development. And there is a set of instructions for testing the type-ahead behaviour locally from static assets, which starts by building the front end and then copying the result into the directory the preview serves, with a command to set the permissions so the copy can be made.
The environment variables come next, and they are the point of the whole procedure: the channel data comes from a build of the channel metadata, and the output directory for the generated completions is set explicitly.
env \
NIXOS_CHANNELS="$(cat "$(nix build --no-link --print-out-paths .#nixosChannelsJson)")" \
AUTOCOMPLETE_OUT_DIR=./dist/autocomplete \
nix develop -c npm --prefix=frontend run build:autocompleteSo the type-ahead data is generated from the real channel metadata rather than checked in, which means it is always current and never goes stale in the repository.
The importer, and testing it against a local index
The end to end testing section describes a full round trip: import information with the importer, then view it in the front end. And to do that locally you need a search backend of your own.
The readme's answer is an ephemeral instance run in a virtual machine, provided as a configuration in the project, and it can be started with a single command.
Then you push information into it with the importer, and the command line for that is long enough to be worth reading in full, because every argument is a decision.
flake-info --elastic-schema-version 43 --elastic-index-name=nixos-unstable --push --elastic-exists recreate nixpkgs unstableThe schema version is pinned, so the index is created with the shape the code expects. The index name says which one. The push flag says to send the data. The exists flag says what to do if it is already there, and the choice here is to recreate it, which is the right choice for a throwaway local instance. And the last two arguments are what to import: the package collection and the channel to import it from.
Then the front end has to be pointed at the local index, and the readme gives the three environment variables to set: the search engine's address, and an empty user name and password. And then the warning, which is the kind of sentence that saves an afternoon: you may need to edit the front end source by hand to use the right index.
That last part is the one that gives the project its character. Everything else in the workflow is automated and reproducible, and the one step that is manual and undocumented in the code is the index name, which means a new contributor hits it, is confused for an hour, and then reads the same line in the readme.
It is also a reasonable thing for the readme to say out loud. A development setup that requires a manual edit of the source is a rough edge, and the readme documents it rather than leaving it to be discovered.
Adding flakes, and the criteria for saying no
The section on adding flakes is short, and the criteria list underneath it is the interesting part.
Adding your own flake means editing a configuration file in the flakes directory, keeping the alphabetical ordering. And the readme is explicit that the ordering is manual, which is the kind of small rule that exists because an alphabetical list is what a human reads, and a human reads it when they are looking for a flake that is already there.
The types of source that can be added are listed: a GitHub repository, a GitLab one, a SourceHut one, and a plain git repository, which is the fallback for anything else but which currently requires setting a revision by hand.
And there is a way to test whether a flake works with the importer without submitting anything: run the importer against it, by handle, and ask for the result as JSON. Several examples are given, covering the four source types.
The criteria for inclusion come next, and the first line is the one every submission needs to read: inclusion in the search does not imply endorsement by the Nix project or anyone else.
Then the list of reasons for refusal, and each one is a category rather than a rule. Flakes that do not actually work. Malicious packages. Flakes with incorrect or incomplete licence metadata. A lack of relevance. Programs that are already part of the main package collection, and the reason given is that the project would rather you helped maintain them there. Outdated packages or packaging. And flakes that require a specific build permission to import from a derivation.
That list is short and blunt, and two of the entries are worth pausing on. The one about duplicates is a policy about where the work should happen, and it is a generous reading of the situation: rather than letting a flake duplicate something in the main collection, the project asks you to contribute it upstream. And the one about licence metadata being incorrect or incomplete is a supply chain concern, because a catalogue that lists a package without saying what licence it is under is a catalogue that cannot be used to answer a compliance question.
The site, the deployment, and the maintenance note
The deployment section describes a pipeline with four steps, and it is short enough to hold in your head.
Each commit to the main branch triggers a GitHub action. The action builds the production version of the web application using the build command. The built application, in the result directory, is then deployed to a static hosting service. And the action can also be triggered from a pull request, which, if the pull request came from a branch of the repository rather than a fork, produces a preview address in a comment.
That last one is a good piece of practice. A pull request from a fork cannot be trusted to run with the deployment credentials, and a pull request from a branch can, and the difference is a preview of the real thing rather than a local approximation.
The contributing section is where a project states its norms, and this one is unusually well written. Patches are very welcome, and you can send a pull request without opening an issue first, but for significant work it is better to discuss first. Reviews by people without write access are welcome, which is a specific encouragement aimed at a specific group. Every pull request needs at least one approval from somebody with write access other than the author, and the readme spells out the three things an approver can do: approve and merge immediately, approve and leave feedback so the author can merge afterwards, or add comments without approving.
That third option is the one that matters. It says the approval is not a gate to be passed but a review to be had, and that a comment counts even when the reviewer is not sure.
And the maintenance section is one line: the project is low on maintainers, and help is appreciated.
The rest of that section is about the search backend itself. There is a management configuration file through which authorised users can make requests to the search engine, and a warning that errors do not fail the jobs, so the output has to be checked by hand. Then the commands for the maintenance work, which are the kind of thing nobody writes down unless somebody has had to do it: listing the indices, and then paging through the old ones in batches of fifty with a pipeline that cuts, sorts and joins the names, because a single listing of every old index is unreadable and the batches are what make it possible to find the one you want to delete.
The repository layout ends the story. There is a directory for the importer, one for the front end, one for the flakes, an overlay, a formatter configuration, a linter configuration, a version file, a lock file, and an environment file for the development directory. It is the layout of a project with three parts that do not talk much to each other, which is the right shape for one.
Editorial conclusion
nixos-search fits a Nix user who needs to find a package or an option, and a contributor who wants their own flake listed in the search. It does not fit somebody who wants to run the site as their own package index, since the pipeline is tied to the official collection and the importer. Verify first which index the site is querying when you are testing locally, because the readme warns that the front end may need a manual edit to point at the right one, and that the local search runs against the production index unless you set up the local one, and that the search only covers the channels that have been imported.
Frequently asked questions
What is the nixos-search repository?
It contains the scripts and the web application behind search.nixos.org, the site used to find out whether a package exists in the Nix package collection or to look up the details of one. The front end is written in Elm and the backend is an Elasticsearch instance sponsored by a hosting company.
Why did the NixOS search site move away from a single JSON file?
Because the file held every package or option and the browser downloaded it whole, which worked while the setup was simple but degraded as the collection grew. The author judged that a database at the back was worth trying, and the search engine that replaced it brought ranking and narrowing that a downloaded file could not provide.
How do I develop the nixos-search front end locally?
Enter the development environment and install the front end dependencies, then start the development server, and point a browser at the local port. Changes to the front end source trigger a hot reload, and the setup deliberately runs the front end against the production package index so that you test what users see.
How do I add a flake to the NixOS search index?
Edit the manual configuration file in the flakes directory, keeping the alphabetical ordering, and choose the source type: GitHub, GitLab, SourceHut, or plain git, which is the fallback but needs a revision set by hand. You can test a flake against the importer before submitting it by running the importer against it and asking for the result as JSON.
What criteria does the NixOS search project use to refuse a flake?
The listed reasons are flakes that do not work, malicious packages, flakes with incorrect or incomplete licence metadata, a lack of relevance, programs that are already part of the main package collection, outdated packages or packaging, and flakes that need a specific permission to import from a derivation. Inclusion in the search does not imply endorsement.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nixos-nixos-search)Was this useful?