Builtins.fetchurl vs nixpkgs.fetchurl

When trying to fetch a dependency from a private S3 bucket with fetchurl, it seemed like s3:// URLs were supposed to be supported, but I was seeing the error message

curl: (1) Protocol "s3" not supported or disabled in libcurl

After a lot of confusion, discovered that (import <nixpkgs> {}).fetchurl is a fixed-output derivation invoking curl, while builtins.fetchurl is an entirely different implementation built in to nix. Only the builtins one supports S3.

Other differences seem to be:

  • nixpkgs.fetchurl supports SRI hash, while builtins requires sha256
  • nixpkgs.fetchurl uses .netrc via curl, while builtins does not

I could find very little documentation on these, so I wanted to open a thread to clarify, and to put some keywords on a page for the next person to find when googling this.

Why do both exist, and besides the feature discrepancies, when should one or the other be used?

The most important difference I know of is that builtins.fetchurl is not a derivation. To be specific, the download happens during evaluation, not during building, so that means importing from it is not IFD. builtins.toFile vs pkgs.writeText is similar.