# docs.rs: building every crate on crates.io with rustdoc, and how the pipeline is arranged

> docs.rs, formerly cratesfyi, hosts documentation for crates published on crates.io by building each one with rustdoc on the nightly toolchain, using rustwide to run the builds in isolated containers. The repository is a Rust workspace of five binaries and several libraries, driven by just recipes and Docker Compose, and a developer can build a single crate locally in a few commands.

**rust-lang/docs.rs** — crates.io documentation generator

- Repository: https://github.com/rust-lang/docs.rs
- Website: https://docs.rs
- Stars: 1,179 · Forks: 235
- Language: Rust
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/rust-lang-docs-rs

## What the service does, and why it needs a nightly toolchain

Docs.rs, formerly cratesfyi, hosts documentation for crates published on crates.io, and it builds that documentation with rustdoc and the nightly Rust toolchain. The nightly requirement is the interesting constraint. rustdoc's rendered output depends on the compiler's own code, so a documentation build is only reproducible if the toolchain version is fixed, and docs.rs pins its own nightly rather than tracking stable. The consequence for a user is that the documentation you see on docs.rs can differ slightly from what rustdoc produces on your machine with a stable toolchain, and for a contributor it is the reason the service has to maintain and update its own toolchain images. The homepage is docs.rs, the licence is MIT, and the last push to the repository was on 2026-10-15.

## rustwide is why Docker is not optional

The development setup is described in a way that makes the hard requirement clear. The recommended setup runs the Rust binaries on the host and external services with Docker Compose, which provides fast incremental Rust builds without requiring PostgreSQL or S3-compatible storage on the host. But building crates still requires Docker, because docs.rs uses rustwide to run crate builds in isolated containers. That is the crux of the whole design. A crate published to crates.io is arbitrary code, and it may have a build script that does anything at all, so it cannot be built in the service's own environment. rustwide is the harness that spins up a clean container per build, with the target toolchain and the crate's dependencies, and the isolation is what makes it acceptable to build untrusted code at scale.

## The five binaries, and the libraries underneath them

The Cargo workspace lists five binaries as the default members, and their names describe the pipeline. docs_rs_web is the site itself, the part that serves the rendered documentation. docs_rs_builder is the build server, the component that actually compiles crates. docs_rs_watcher watches the crates.io registry and reacts to new releases. docs_rs_build is the standalone build CLI, and docs_rs_admin is the administrative binary. The workspace also has a crates/lib/ directory for the shared libraries, including docs_rs_rustwide which wraps the rustwide harness. The members are declared with a glob over crates/bin and crates/lib, with integration_tests as a member, and the exclude list is specific about the rustwide workspaces and the ignored directory, which are build scratch areas rather than source. The workspace version is 0.6.0, the edition is 2024, and the authors are Onur Aslan and The Rust Project Developers.

## The local development workflow, from clone to a running site

The setup is a specific sequence and the order matters. You clone the repository, copy .env.sample to .env, create the ignored/cratesfyi-prefix/crates.io-index directory, run mise install and then build with SQLX_OFFLINE=1 cargo build. The mise install step installs the project's just, Node, Deno and Cargo development tools, and you enable mise in your shell or run commands through mise x with the command. Then you start PostgreSQL and the local S3 service and initialise the database with just compose-up-resources followed by just sqlx-migrate-run. The just recipes load .env and start PostgreSQL and S3 when needed, and most recipes start those resources automatically. You then run just web and the site is on localhost:3000, or just web-watch to restart it when web, template, asset, shared-library or workspace configuration files change. The initial setup downloads roughly 10 GB of data, which is the one number worth knowing before you start.

```console
$ git clone https://github.com/rust-lang/docs.rs.git docs.rs
$ cd docs.rs
$ cp .env.sample .env
$ mkdir -p ignored/cratesfyi-prefix/crates.io-index
$ mise install
$ SQLX_OFFLINE=1 cargo build

$ just compose-up-resources
$ just sqlx-migrate-run
$ just web
```

## Building documentation for one crate, and the local mode default

The builder recipe is how you produce the documentation for a specific crate, and there are two commands. The first sets up or updates the docs.rs nightly toolchain with just builder build update-toolchain, and the second builds the crate with just builder build crate regex 1.3.1, naming a crate and a version. The builder recipe uses DOCSRS_BUILDER_CLI_MODE, which defaults to local on amd64 Linux and to docker on other platforms, and you set the variable explicitly to override the choice. The cli and watcher recipes use DOCSRS_CLI_MODE with a similar default of local everywhere. The rationale is that a developer on an Apple Silicon Mac cannot run the build servers natively and needs the container path, while on Linux the local path is faster. There is also a standalone build CLI for testing a local package, cargo run --locked -p docs_rs_build with the path to the package, which packages the crate automatically and does not require a docs.rs database.

```console
$ just builder build update-toolchain
$ just builder build crate regex 1.3.1

$ cargo run --locked -p docs_rs_build -- /path/to/package
```

## Running everything in Compose, and the caveat about build server architecture

If running the Rust binaries on the host is impractical, the just recipes keep the same interface while running them through Docker Compose, and you set DOCSRS_CLI_MODE and DOCSRS_BUILDER_CLI_MODE to docker in .env. The compose file then gives you the same recipes, just cli-db-migrate and just compose-up-web, with compose-up-builder and compose-up-watcher for the other services, and a full profile that starts all of it. The compose file's comments are unusually informative about the shape of the system. The db service is Postgres and the s3 service is rustfs, and the optional web profile runs the web server, the builder profile runs two parallel build servers sharing nothing apart from the build queue they access, and the watcher profile runs the crates.io registry watcher, the repo-stats updater, the CDN invalidator and the release-rebuild-enqueuer, and there is a metrics profile with a debug OpenTelemetry receiver. The caveat is that the build servers have to run on the linux/amd64 platform, so on a Mac the layers are cached separately for amd64 and arm64, which increases build time.

## Testing, and the schema isolation trick that makes it work

The test suite is run with just run-tests, which starts PostgreSQL and S3, builds tests for every workspace member and runs cargo nextest with the workspace, the locked flag and no-fail-fast, and plain cargo test only tests the default members. The interesting part is the setup. At the start of each nextest run, the test setup rebuilds a template schema by applying every migration, captures its DDL with pg_dump, then uses that DDL to create an isolated schema for each test. So every test gets a fresh database built from the migrations, which is why a migration that does not apply cleanly breaks the suite rather than only breaking one test. PostgreSQL 18 client tools are required when tests run on the host, and there is a flag, DOCSRS_TEST_PG_DUMP_FROM_COMPOSE set to true, to use the pg_dump bundled in the local Compose database container instead when installing them is impractical. The builder tests run separately with just run-builder-tests, and migrations are tested through both the SQLx CLI and docs_rs_admin with just test-database-migrations.

```console
$ just run-tests
$ DOCSRS_TEST_PG_DUMP_FROM_COMPOSE=true just run-tests
$ just run-builder-tests
$ just test-database-migrations
```

## Linting, formatting and the committed SQLx metadata

A few maintenance commands complete the picture, and one of them is a trap for anyone who changes a query. After changing queries or migrations you apply migrations and regenerate the committed SQLx offline metadata with just sqlx-update, because the repository checks in the .sqlx/ directory so that the build can run with SQLX_OFFLINE=1 and without a database. That is why the setup sequence above sets SQLX_OFFLINE=1 for the initial cargo build, and why a contributor who edits a query and does not regenerate the metadata will find the build failing on a stale query hash. Linting is just lint, which runs the complete lint suite and uses the actionlint provided by mise install to validate the GitHub Actions workflows, and formatting is just format, which runs all the formatters and exits with an error if files are not formatted correctly, so it rewrites them and tells you. The rest of the tooling configuration is in the tree as well, with clippy.toml, tomlfmt.toml, an _typos.toml for spell checking, eslint.config.js, a triagebot.toml for issue triage and a docker-bake.hcl for the container builds.

## Conclusion

docs.rs suits a Rust developer who wants to reproduce the documentation build for their own crate locally, or who wants to work on the service that documents the whole ecosystem, and the build CLI is genuinely useful on its own. It is the wrong tool for generating documentation as part of your own release pipeline, because the service exists to build every published crate, and a poor fit for a team that does not want Docker on a build machine, since building crates still requires Docker because of rustwide. Before running it, read the developer guide rather than the README, since the README says it contains the commands needed to develop and test docs.rs while the user-facing documentation lives on the about page.

## FAQ

### What does docs.rs do?

It hosts documentation for crates published on crates.io, formerly known as cratesfyi. It builds the documentation with rustdoc and the nightly Rust toolchain, and it uses rustwide to run crate builds in isolated containers. The licence is MIT.

### How do I build documentation for a single crate locally?

Run just builder build update-toolchain to set up the nightly toolchain, then just builder build crate with a crate name and version, for example just builder build crate regex 1.3.1. For a local package you can instead use the standalone CLI with cargo run --locked -p docs_rs_build and the path to the package, which packages the crate automatically and needs no docs.rs database.

### Why does developing docs.rs require Docker even for a local crate build?

Because building crates still requires Docker, since docs.rs uses rustwide to run crate builds in isolated containers. The recommended setup runs the Rust binaries on the host with external services in Docker Compose for fast incremental builds, but the crate build itself is always containerised.

### What do the just recipes do in the docs.rs repository?

They drive the whole local workflow: just compose-up-resources starts PostgreSQL and S3, just sqlx-migrate-run initialises the database, just web runs the site on localhost:3000 and just web-watch restarts it on changes. The cli and builder recipes also respect DOCSRS_CLI_MODE and DOCSRS_BUILDER_CLI_MODE, which switch them between running on the host and running through Compose.

### Why do I need to run just sqlx-update after changing a query?

Because the repository commits the SQLx offline metadata in the .sqlx directory, so the build can run with SQLX_OFFLINE=1 and without a database. After changing queries or migrations you apply the migrations and regenerate that metadata, otherwise the build fails on a stale query hash.

## Sources

- [Issues](https://github.com/rust-lang/docs.rs/issues)
- [License: MIT](https://github.com/rust-lang/docs.rs/blob/main/LICENSE)
- [Project website](https://docs.rs)
- [README](https://github.com/rust-lang/docs.rs/blob/main/README.md)
- [rust-lang/docs.rs on GitHub](https://github.com/rust-lang/docs.rs)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/rust-lang-docs-rs
