You can install Homebrew packages offline, but it’s not a built-in feature you just flip a switch for.

Let’s say you’re on a plane, or your office network is locked down tighter than a drum, and you need to brew install some-obscure-library. Normally, brew install fetches the source code or pre-compiled binaries from the internet. But what if there’s no internet?

Homebrew stores downloaded package archives (usually .tar.gz or .zip files) in a specific cache directory. If you’ve installed a package before, even on a different machine, its archive might already be sitting in your cache. The trick is to get that archive onto the offline machine and tell Homebrew to use it.

Here’s how you’d typically set this up before you go offline:

First, find your Homebrew cache. It’s usually located at ~/.cache/Homebrew/. If it’s not there, you can find its location by running:

brew --cache

This command will output the path to your Homebrew cache directory.

Now, let’s simulate needing to install a package, say wget. First, install it normally while online:

brew install wget

This downloads the wget archive and places it in your cache. To see it, cd into your Homebrew cache directory and list the contents. You’ll see a directory named after the formula, and inside that, the archive file. For wget, it might look something like:

~/.cache/Homebrew/wget--1.21.3.catalina.bottle.tar.gz

(The exact filename will vary based on the formula, version, and your macOS version).

To transfer this to your offline machine, you can simply copy this archive file (and any other archives you might need) to a USB drive or any other portable media.

On the offline machine, you’ll need to place the downloaded archive file into the same relative path within its Homebrew cache. So, if your cache is at ~/.cache/Homebrew/ on the offline machine, you’d create a wget subdirectory and place the wget--1.21.3.catalina.bottle.tar.gz file inside it:

mkdir -p ~/.cache/Homebrew/wget
cp /path/to/your/usb/wget--1.21.3.catalina.bottle.tar.gz ~/.cache/Homebrew/wget/

Now, when you try to install wget on the offline machine, Homebrew will first check its cache. Since it finds the archive, it will use that instead of trying to download it from the internet.

brew install wget

This works for any Homebrew package. The key is that the archive file must be present in the exact expected location within the ~/.cache/Homebrew/ directory. If you’re installing a formula that has dependencies, you’ll need to have the archives for all those dependencies cached as well.

You can pre-populate your cache by running brew fetch <formula> for all the packages you anticipate needing. This downloads the archives without installing the packages.

brew fetch wget
brew fetch node
brew fetch python@3.9

Then, copy the entire ~/.cache/Homebrew/ directory (or specific subdirectories containing the archives) to your offline machine’s cache location. This is often more convenient than manually copying individual .tar.gz files.

The most surprising thing about this process is how Homebrew’s caching mechanism is designed. It’s not just a flat list of downloaded files; it maintains a directory structure that mirrors the formula name and version, which is crucial for unambiguous retrieval.

Let’s see this in action. Suppose we have a clean cache on an offline system.

# Verify cache is empty for a specific formula
ls ~/.cache/Homebrew/wget/
# Output: (empty or no such file/directory)

# Attempt to install wget (this will fail if truly offline and no cache)
# brew install wget
# Output would indicate it's trying to download and failing.

# Now, let's simulate having the cache file available
# Assume wget--1.21.3.catalina.bottle.tar.gz is on a USB drive at /Volumes/USB/brew_cache/wget/
mkdir -p ~/.cache/Homebrew/wget
cp /Volumes/USB/brew_cache/wget/wget--1.21.3.catalina.bottle.tar.gz ~/.cache/Homebrew/wget/

# Now, try installing again. Homebrew should find the cached file.
brew install wget

The output of brew install wget should now indicate it’s unpacking the archive from the local cache, rather than attempting an internet download. You’ll see lines like:

==> Fetching wget
==> Downloading https://ghcr.io/v2/homebrew/core/wget/manifests/1.21.3
# ... and then it will find the local file ...
==> Using existing downloaded file: /Users/youruser/.cache/Homebrew/wget--1.21.3.catalina.bottle.tar.gz
==> Installing wget

This works because Homebrew’s installation logic checks for the existence of the archive file in its cache before it initiates any network requests. If the file is present and has the correct naming convention (including the formula name and version), Homebrew assumes it’s ready to go.

A common pitfall is not having the correct directory structure within the cache. Homebrew expects ~/.cache/Homebrew/<formula-name>/<archive-file>. If you just dump all .tar.gz files into the root of ~/.cache/Homebrew/, it won’t find them.

If you’re managing multiple macOS versions or architectures, you might notice different .bottle.tar.gz files. These are pre-compiled binaries specific to certain macOS versions and architectures (like arm64 for Apple Silicon). If you download an archive for a different architecture than your offline machine, the installation will fail because Homebrew won’t be able to use that specific binary. You’d ideally want to fetch the correct bottle for your target machine’s architecture.

The next hurdle you’ll likely encounter is managing dependencies. If wget required libbar, and libbar wasn’t in your cached downloads, brew install wget would fail during the dependency installation phase, prompting you to fetch libbar’s archive as well.

Want structured learning?

Take the full Homebrew course →