Cargo `--offline` ✈️

Rust 1.36 is released on the 4th July and includes a bunch of new stuff. This blog post is about one newly stable feature in Cargo: --offline.

Cargo seamlessly uses dependencies whether they are local or from a remote registry. But sometimes it is not possible or desirable to access the network: not everyone has reliable (or any) internet access, sometimes access is expensive or strictly controlled, and sometimes you just don't want to connect. The --offline flag makes Cargo try harder not to need the network.

What does 'try harder' mean? Even without --offline, Cargo often works without network access, so what does --offline change? To explain exactly what --offline does, I'll need to go over some of the steps in a cargo build.

Cargo build

When Cargo builds your project, it starts by gathering information about each dependent crate. Using this information, it creates a dependency graph. Using that graph and version constraints for each dependency, Cargo resolves an exact version for each crate (this version information is then stored in Cargo.lock). Cargo then ensures it has source code for the resolved version of each crate. Finally it builds all those crates (using rustc) in an order determined from the dependency graph.

The phase separation is not really that clean: for Git and path dependencies, the 'gathering info' phase and the 'ensuring source code phase' overlap. For these dependencies, the only way to get dependency information is to inspect the Cargo.toml in the source code. For dependencies in a registry (e.g., crates.io), Cargo only needs access to the registry's index.

I'll summarise what each phase means for each kind of dependency:

Type of dep Gather dep info Ensure source
registry Read index Download source code
path Read Cargo.toml No action
git Clone repo and read Cargo.toml Clone repo

Cargo caches everything it downloads in a directory shared by all projects (.cargo). If the source for the correct version of a crate is cached, it won't need to be downloaded again. A registry's index can be incrementally updated.

If a lock file (Cargo.lock) exists for a project, then Cargo will use versions from that in preference to resolving new versions.

Building without the network

Alright, so what do we need the network for? To update a registry's index or to download sources. If everything is cached and Cargo doesn't need to update an index, then a build without network access should work (whether you use --offline or not). If you have already built your project and don't touch Cargo.toml, then Cargo doesn't need the network to do a build.

It is when you modify Cargo.toml that things get interesting. Changing the version of a dependency which is obtained from a registry usually causes Cargo to update its index for that registry, even if the new version of the dependency has already been cached. A build without --offline will fail with a network error.

Using --offline means Cargo will not update its index. Cargo will re-resolve the dependency graph using the cached index and will prefer to use versions of crates which it has cached. If it is possible to just use cached crates, then you can build without touching the network.

Similarly, when adding a new dependency or starting a new project, without --offline, cargo build would update the index, but using --offline means Cargo will use the cached index and try to use crates which have been cached locally.

In other words, you probably shouldn't expect to git clone a non-trivial project, get on a plane, and have a working build. If you have built a crate before, then cargo build --offline should continue to work once you're offline, sometimes even if you change your Cargo.toml. If you start a new project on the plane and only use crates you've used before, you should be fine too.

Going further

If you know in advance that you'll be offline, then there are a few things you can do beforehand to improve your chances of being able to build. If you only care about one project (and don't plan to update dependencies), you can run cargo fetch - that resolves the dependency graph and downloads any required crates. After running cargo fetch, you are guaranteed that cargo build without a network will succeed.

If you want a bit more flexibility, you can use cargo-prefetch to fetch a collection of popular crates into Cargo's cache. This is useful if you are likely to add/update dependencies or if you're starting a new project. Alternatively you can use the prefetch facility in cargo-cacher to cache the entire crates.io registry or individual crates.

Some details

You can use the --offline flag with all Cargo subcommands (but some which require internet access (e.g., publish) will always fail). For example, cargo build --offline. You can also set the net.offline key in your config file in order always do --offline builds.

How are --offline and --frozen related?

cargo build --frozen works in exactly the same way as cargo build, except that network access is prevented. --offline actually changes how cargo build works, as described above.

Can I just always use --offline?

You should not. You are more likely to get an error from Cargo if you use --offline because there is a smaller set of crates for Cargo to use. You will also not be able to update any dependencies and thus will miss out on security and performance improvements, etc.