Setting Up Rust Securely (Without the Blind `curl | sh`)
1. Introduction
Rust is one of the most thoughtfully designed languages of our time — but setting it up on macOS can feel oddly opaque. The standard advice is to run a one-liner like curl https://sh.rustup.rs | sh
, which works beautifully but hides a lot of what’s happening behind the scenes. For developers who are more security-conscious or just prefer to know what’s being installed and where, this default approach can feel like a black box.
This post explores the different ways to install and manage Rust on macOS — from the convenience of Homebrew to the flexibility of rustup, and the transparency of manual or containerized setups. The goal is simple: give you control and understanding without sacrificing practicality.
2. TL;DR — Installation Methods Compared
Method | Pros | Cons | Best For |
---|---|---|---|
Homebrew (brew install rust ) |
Managed by system package manager, simple | Single version only, slower updates | Users who just need stable Rust globally |
rustup (official script) | Multi-toolchain, per-project pinning, reproducible | Involves piping a script to shell | Most Rust developers; convenience with trust |
rustup via Homebrew (brew install rustup-init ) |
Same power as rustup but auditable via Brew | Slightly more manual setup | Balanced approach: flexibility + safety |
Manual install | Full transparency, verifiable signatures | Complex, more maintenance | Power users or high-security environments |
Docker / Vagrant | Fully isolated, reproducible environments | Slower I/O on macOS, can’t build macOS/iOS binaries | Reproducible Linux builds or CI-like dev setups |
If you’re unsure: use brew install rustup-init
— it gives you the full power of rustup while keeping installation auditable through Homebrew.
3. The Typical Rust Installation Path
Running:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
downloads the official rustup installer and executes it in your shell. The script detects your platform, downloads a precompiled rustup-init
binary, and installs it to ~/.cargo/bin
, along with the default toolchain (usually stable-x86_64-apple-darwin
).
This is the official and supported way — the Rust team maintains the infrastructure, uses HTTPS, and distributes signed manifests. However, you don’t see what’s happening under the hood, and for some, that’s reason enough to pause.
4. The Brew Alternative
Installing Rust via Homebrew is simple:
brew install rust
Homebrew handles the download, checksum verification, and installation into /usr/local/Cellar/rust
(or /opt/homebrew
on Apple Silicon). You get rustc
, cargo
, and standard tools, all linked into /usr/local/bin
.
Pros: managed updates, clear ownership, integrates with system package management.
Cons: one version only, updates lag behind official releases, and you can’t easily install nightly or beta toolchains. If you use multiple Rust projects that depend on different versions, this can get painful quickly.
5. My Preferred Way
If you dislike running a shell script from the web, but also want to have all the convenience of managed versions, components, targets etc...
Install rustup
via Homebrew directly:
brew install rustup
This installs the same tool but through a trusted, auditable channel.
Understanding rustup Deeply
rustup
isn’t the compiler — it’s a toolchain manager. It manages versions, components, and targets under ~/.rustup
, while the compilers and tools themselves live under ~/.cargo/bin
.
With rustup, you can:
rustup install stable
It installs cargo, clippy, rust-docs, rust-std, rustc, rustfmt, and sets default toolchain to 'stable-aarch64-apple-darwin'.
You can even pin versions per project via rust-toolchain.toml
or rustup override set
. Updates (rustup update
) fetch signed manifests from Rust’s CDN. It’s safe and reproducible.
6. Containerized Rust: Docker & Vagrant
If you want complete isolation from your host system, Rust runs well inside Docker or Vagrant-managed containers:
docker run --rm -it \
-v "$PWD":/workspace \
-w /workspace rust:1-bookworm bash
You can build projects, cache dependencies in named volumes, and keep your host system clean.
Pros: clean reproducibility, no system pollution, easy CI parity.
Cons: macOS filesystem performance under Docker can be slow, and you can’t cross-compile macOS/iOS binaries from Linux containers.
If you want extra reproducibility, Vagrant can wrap Docker for defined dev boxes — though that’s often overkill unless you’re building a large multi-developer setup.
7. Security & Trust Considerations
Ultimately, installing Rust is about who you trust:
- Homebrew: trusts Homebrew maintainers and their bottles.
- rustup: trusts Rust project’s official distribution infrastructure.
- Docker: trusts the Rust Docker image maintainers, depends on which one you choose.
For extra peace of mind:
- Install
rustup
via Brew, not viacurl | sh
. - Use a non-root user inside Docker containers.
- Verify signatures when downloading manually.
- Audit your dependencies with
cargo-vet
orcargo-crev
.
8. Practical Recommendations
- If you want simplicity:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
- If you want control & convenience:
brew install rustup
. - If you want isolation: Docker with named cargo volumes.
A good middle ground for most macOS developers is:
brew install rustup
rustup default stable
# Make sure to add the `rustup/bin` path to your shell's PATH env variable
This setup is auditable, reproducible, and works with all major tooling (VS Code, Rust Analyzer, etc.).
9. Closing Thoughts
Setting up Rust securely doesn’t have to mean sacrificing convenience. The key is awareness — understanding what’s installed, where it lives, and who you’re trusting.
Rust’s ecosystem is designed around reproducibility and safety, and that extends to its tooling. Whether you prefer a fully containerized setup, a manually verified binary, or just the standard rustup workflow, what matters is that you make an informed choice.
After all, security isn’t just about saying no to scripts — it’s about knowing exactly what they do.