How to Install Rust and Compile System Tools on RHEL 7
Rust is a systems programming language that offers memory safety without a garbage collector, making it an ideal choice for building high-performance command-line utilities, daemons, and system-level tools. On RHEL 7, the system compiler and C runtime are older, but Rust’s installer rustup sidesteps this limitation by managing its own toolchain independently of the system packages. In this tutorial, you will install Rust via rustup, build a CLI tool with argument parsing using the Clap library, produce portable static binaries using the musl target, strip binaries for minimal size, compile popular community tools from source, and install utilities directly from crates.io.
Prerequisites
- RHEL 7 with sudo or root access
curlinstalled (sudo yum install -y curl)- Development tools for native compilation:
sudo yum groupinstall -y "Development Tools" - At least 2 GB of free disk space for the Rust toolchain
- Internet access to reach
sh.rustup.rsandcrates.io
Step 1: Install Rust with rustup
The canonical way to install Rust on any Linux distribution is via rustup, the official Rust toolchain installer. It installs rustc, cargo, and associated tools in your home directory under ~/.cargo, completely separate from the system packages.
# Download and run the rustup installer (non-interactive)
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
# Load the cargo environment into the current shell
source "$HOME/.cargo/env"
# Verify the installation
rustc --version
cargo --version
By default, rustup installs the stable toolchain. You should see output like rustc 1.78.0 (9b00956e5 2024-04-29). The installation also adds the necessary lines to your ~/.bash_profile and ~/.profile so that cargo is available in future sessions.
Step 2: Create a New Cargo Project
Cargo is Rust’s build system and package manager. Use it to scaffold a new binary project.
cd ~/projects 2>/dev/null || mkdir -p ~/projects && cd ~/projects
cargo new cli-tool
cd cli-tool
# Inspect the project structure
ls -R
cat src/main.rs
Cargo creates a src/main.rs with a “Hello, world!” program, a Cargo.toml manifest, and a .gitignore. The Cargo.toml is where you declare dependencies, metadata, and build profiles.
Step 3: Add Clap for Argument Parsing
Clap (Command Line Argument Parser) is the most widely used argument parsing library in the Rust ecosystem. The derive feature enables a macro-driven API where you define arguments as struct fields, dramatically reducing boilerplate.
# Add clap with the derive feature to Cargo.toml
cat > Cargo.toml <<'EOF'
[package]
name = "cli-tool"
version = "0.1.0"
edition = "2021"
[dependencies]
clap = { version = "4", features = ["derive"] }
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
EOF
Now replace the default main.rs with a Clap-powered CLI:
cat > src/main.rs <<'EOF'
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(name = "cli-tool", version = "0.1.0", about = "A sample RHEL 7 CLI tool")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Greet a user by name
Greet {
/// The name to greet
name: String,
/// Enable verbose output
#[arg(short, long)]
verbose: bool,
},
/// Count lines in a file
Count {
/// Path to the file
path: String,
},
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Greet { name, verbose } => {
if verbose {
println!("Executing greet command for: {}", name);
}
println!("Hello, {}!", name);
}
Commands::Count { path } => {
let content = std::fs::read_to_string(&path)
.unwrap_or_else(|e| { eprintln!("Error reading {}: {}", path, e); std::process::exit(1); });
println!("{} lines in {}", content.lines().count(), path);
}
}
}
EOF
# Build in debug mode and test
cargo build
./target/debug/cli-tool greet "RHEL Admin"
./target/debug/cli-tool greet --verbose "RHEL Admin"
./target/debug/cli-tool count /etc/hosts
./target/debug/cli-tool --help
Step 4: Build an Optimized Release Binary
Debug builds include full symbols and no optimizations. For deployment, always use the release profile, which enables full compiler optimizations as configured in your Cargo.toml.
cargo build --release
# Check the binary size
ls -lh target/release/cli-tool
# Run the release binary
./target/release/cli-tool greet "Production"
With lto = true (link-time optimization) and codegen-units = 1, Rust performs whole-program optimization, often reducing binary size by 40–60% compared to default release builds.
Step 5: Static Linking with the musl Target
By default, Rust binaries on Linux link dynamically against glibc. This means a binary built on RHEL 7 may not run on RHEL 8 or CentOS Stream if the glibc version differs. The solution is to compile against the musl C library, producing a fully static binary with zero shared library dependencies.
# Install the musl-gcc cross compiler
sudo yum install -y musl-libc-static musl-gcc 2>/dev/null ||
sudo yum install -y musl-tools 2>/dev/null ||
echo "musl-gcc not in standard repos; install from EPEL or compile manually"
# Add the musl target to rustup
rustup target add x86_64-unknown-linux-musl
# Create a cargo config for the musl linker
mkdir -p .cargo
cat > .cargo/config.toml <<'EOF'
[target.x86_64-unknown-linux-musl]
linker = "musl-gcc"
EOF
# Build with the musl target
cargo build --release --target x86_64-unknown-linux-musl
# Verify it is fully static (no dynamic deps)
ldd target/x86_64-unknown-linux-musl/release/cli-tool
file target/x86_64-unknown-linux-musl/release/cli-tool
The output of ldd should read not a dynamic executable, and file should show statically linked. This binary can be copied to any x86_64 Linux system regardless of the installed glibc version.
Step 6: Strip the Binary
Even with the strip = true profile option, you can further reduce binary size by stripping the binary manually using the system strip utility, which removes all remaining symbol information.
BINARY=target/x86_64-unknown-linux-musl/release/cli-tool
# Check size before stripping
ls -lh $BINARY
# Strip all symbols
strip --strip-all $BINARY
# Check size after stripping
ls -lh $BINARY
# Confirm it still works
$BINARY greet "Stripped Binary"
Stripping typically reduces binary size by an additional 10–20% beyond the profile-level stripping Cargo performs.
Step 7: Compile Popular Tools from Source
Many beloved Unix utilities have been rewritten in Rust and are available as Cargo packages. Compiling them from source on RHEL 7 gives you the latest versions without waiting for distribution packages.
# ripgrep (rg) — faster replacement for grep
cargo install ripgrep
# fd — faster replacement for find
cargo install fd-find
# bat — cat with syntax highlighting
cargo install bat
# Verify installations
rg --version
fd --version
bat --version
# These are installed to ~/.cargo/bin/
ls -lh ~/.cargo/bin/
All installed binaries are placed in ~/.cargo/bin/, which is added to your PATH by the rustup installer. To make them available system-wide on RHEL 7, copy them to /usr/local/bin/:
sudo cp ~/.cargo/bin/rg /usr/local/bin/
sudo cp ~/.cargo/bin/fd /usr/local/bin/
sudo cp ~/.cargo/bin/bat /usr/local/bin/
Step 8: Install Packages from crates.io
cargo install downloads, compiles, and installs any binary crate published to crates.io in a single command. No pre-built packages are required.
# Install a specific version
cargo install ripgrep --version 14.1.0
# Force reinstall (overwrite existing)
cargo install --force bat
# List all installed cargo binaries
cargo install --list
# Uninstall a package
cargo uninstall bat
Unlike system packages, cargo install compiles specifically for your machine’s architecture and available CPU features, often producing faster binaries than pre-built distributions.
Conclusion
You have installed Rust via rustup on RHEL 7, scaffolded a CLI project with Cargo, implemented argument parsing using Clap’s derive API, and built both debug and optimized release binaries. You compiled fully portable static binaries using the x86_64-unknown-linux-musl target, stripped them to minimize size, compiled popular system tools like ripgrep, fd, and bat from source, and used cargo install to fetch packages directly from crates.io. Rust’s combination of zero-cost abstractions, memory safety guarantees, and excellent tooling makes it a powerful addition to any RHEL 7 systems administrator’s toolkit.