7  R: rig & renv

In Your First R Project, you ran renv::init(), installed Seurat and half a dozen other packages, and ran renv::snapshot() to lock everything down. It worked — but what actually happened? Where did those packages go? What is renv.lock recording? And why did the installation chapter have you install R with something called “rig” instead of downloading it from the R website?

This chapter answers those questions. Managing R effectively requires two things: controlling which version of R you’re running, and controlling which packages are installed for each project. We use rig for the first and renv for the second. Together, they give each project a self-contained, reproducible R setup — the same idea as conda environments for Python, but with R’s own tools.

7.1 rig: Managing R Versions

7.1.1 Why You’d Need More Than One

Imagine this: you built your Spongilla analysis six months ago with R 4.4. Now you’re starting a new project, and R 4.5 is out with nice improvements. Should you update?

If you just download R 4.5 from the R website, it overwrites R 4.4. Your new project runs fine, but when you go back to the Spongilla analysis, subtle things might break. Packages that worked under R 4.4 might behave differently under R 4.5. Worse, if you’re using Bioconductor packages for genomics work, each Bioconductor release is tied to a specific R version — upgrading R can silently change which Bioconductor packages you get, potentially altering your results.

This is the problem rig solves. When you run rig add release, it installs the new R version alongside the old one, not replacing it. You can have R 4.3, R 4.4, and R 4.5 all installed at the same time, and switch between them depending on which project you’re working on.

[TODO: screenshot of terminal showing rig list output with multiple R versions installed]

7.1.2 Day-to-Day Usage

The Installation chapter covered installing rig and adding your first R version. Here are the commands you’ll use day to day:

# See which R versions are installed
rig list

# Install the latest stable R
rig add release

# Install a specific older version
rig add 4.3

# Set the system default
rig default 4.4

# Check which version is the default
rig default
Noterig add vs. CRAN installers

When you run rig add, it downloads and installs R without removing previous versions. This is different from downloading R directly from CRAN, which typically overwrites your existing installation.

7.1.3 Setting R Version Per Project

For most day-to-day work, the system default R version is fine — all your new projects will use it. But when you need a specific version for a particular project (because a collaborator uses R 4.3, or because the project depends on a specific Bioconductor release), you can lock it per project.

In Positron, open the Command Palette (Cmd+Shift+P on macOS, Ctrl+Shift+P on Windows) and type R: Select R Binary. You’ll see a list of the R versions rig has installed. Pick the one you want, and Positron will remember that choice for this project — every time you open the project, it uses the version you selected.

[TODO: screenshot of Command Palette showing “R: Select R Binary” with a list of installed R versions]

You only need to do this once per project. If you later use the Musser Lab’s /new-project skill in Claude Code (covered in Part 3), it sets the R version automatically when scaffolding a new project. See the Positron chapter for more details.

7.2 renv: Project Package Libraries

7.2.1 What renv Actually Did in Chapter 3

When you ran renv::init() in your first project, renv created several files in your project folder. At the time you probably didn’t pay much attention to them — you were focused on getting Seurat running. Here’s what each one does.

The renv/ folder is your project’s private package library. When you installed Seurat, ggplot2, and the other packages, they went here — not into a shared system library that all your projects use. This is the key idea: each project has its own packages, so installing something for Project A can’t break Project B.

The renv.lock file is a snapshot of every package in your project library — names, exact versions, and where they came from (CRAN, Bioconductor, GitHub). This is what makes your analysis reproducible. When a collaborator clones your project and runs renv::restore(), renv reads this file and installs every package at the exact version you recorded.

The .Rprofile file runs automatically when R starts in this folder. It calls the activation script (renv/activate.R), which tells R to use the project library instead of the system library. That’s why you see Project loaded. [renv 1.0.0] in the console when you open the project in Positron — it’s renv confirming it’s active.

[TODO: screenshot of Positron console showing “Project loaded. [renv 1.0.0]” activation message]

7.2.2 Installing Packages

You already installed packages in Chapter 3 with renv::install(). Here’s what else you need to know.

For most packages, install.packages() still works — renv intercepts the call and installs into your project library. But renv::install() is more versatile because it understands multiple sources:

# Install from CRAN (either method works)
install.packages("ggplot2")
renv::install("ggplot2")

# Install from Bioconductor (must use renv::install with bioc:: prefix)
renv::install("bioc::DESeq2")

# Install from GitHub
renv::install("satijalab/seurat")

# Install a specific version
renv::install("ggplot2@3.4.0")

The bioc:: prefix is important. Bioconductor is a separate repository of R packages for bioinformatics — things like DESeq2 for differential expression, SingleCellExperiment for single-cell data structures, and hundreds of other genomics tools. These packages aren’t on CRAN, so you need the prefix to tell renv where to find them. For a guide to common R packages for genomics and single-cell analysis, see Appendix D: R Packages for Biology.

WarningClaude Code

When you’re starting a new type of analysis and aren’t sure which packages you need, Claude Code can recommend packages and generate the install commands.

I’m starting a bulk RNA-seq analysis in my renv project. I need to import Salmon quantifications and run differential expression. What R packages do I need and how do I install them?

Claude will recommend the right packages (tximport for importing quantifications, DESeq2 for differential expression, etc.), generate the correct renv::install() commands with the bioc:: prefix for Bioconductor packages, and remind you to snapshot afterward.

7.2.3 Snapshot and Restore: The Core Loop

After installing packages, the most important habit is snapshot:

renv::snapshot()

This updates renv.lock with the exact versions of everything in your project library. After snapshotting, commit the updated renv.lock to Git. This is the cycle you’ll repeat throughout a project:

  1. Install or update a package
  2. Run renv::snapshot() to record the change
  3. Commit renv.lock to Git

Why does this matter? Imagine your collaborator clones your project from GitHub six months from now. The R code is all there, but without the right packages at the right versions, nothing runs. When they open the project and run:

renv::restore()

renv reads renv.lock and installs every package at the exact version you recorded. Your collaborator gets the same environment you had, not whatever happens to be current on CRAN that day. This is what makes the analysis reproducible — not just the code, but the exact tools that ran it.

7.2.4 Checking Status

Before committing your work, it’s good practice to check whether your library and lockfile are in sync:

renv::status()

This tells you if any packages are installed but not recorded in the lockfile (you need to snapshot()), missing from your library (you need to restore()), or at different versions than the lockfile expects.

The most common message you’ll see is “The project is out-of-sync.” This sounds alarming but usually just means you installed something and forgot to snapshot, or you pulled changes from Git that updated the lockfile and haven’t restored yet. Run renv::status() to see the details, and it will be clear which direction to go.

[TODO: screenshot of R console showing renv::status() output with out-of-sync packages listed]

WarningClaude Code

Claude Code can interpret renv status messages and tell you whether to restore or snapshot.

I’m getting “The project is out-of-sync” when I start R. Here’s the output of renv::status(): [paste output]. What should I do — restore or snapshot?

Claude will read the status output, explain which packages are out of sync and why, and recommend the right command.

7.3 How rig and renv Work Together

rig and renv solve different problems, but they connect in an important way. When you run renv::snapshot(), the lockfile records not just your packages but also your R version and, if you’re using Bioconductor, the Bioconductor version. This creates a complete picture of your environment.

The connection matters because of Bioconductor. Each Bioconductor release is built and tested against a specific R version — Bioconductor 3.19 works with R 4.4, Bioconductor 3.20 also works with R 4.4, and Bioconductor 3.21 requires R 4.5. If you change your R version, you may get a different Bioconductor release with different package versions. For a project that uses Bioconductor packages, changing R can subtly change your results even if your code doesn’t change at all. You can check which Bioconductor releases work with which R versions on the Bioconductor release history page.

This is why pinning R per project (using the Command Palette method described above) matters for critical analyses — especially anything heading toward publication. renv records the R and Bioconductor versions in the lockfile, and rig makes it easy to have the right R version available. Together, they ensure that renv::restore() on another machine gives you the same environment, not just the same package names.

You can check your current Bioconductor version at any time:

BiocManager::version()

7.4 What to Commit to Git

You’ve been committing renv.lock since Chapter 3, and that’s the most important file. Here’s the full list of what goes into Git and what doesn’t.

Include in Git:

  • renv.lock — the lockfile (exact package versions — the whole point)
  • renv/activate.R — the activation script
  • .Rprofile — auto-activates renv when R starts

Exclude from Git (should be in .gitignore):

  • renv/library/ — the actual installed packages (too large and platform-specific)
  • renv/staging/ — temporary files during installation

The default renv setup handles the .gitignore entries automatically — when you ran renv::init(), it created a .gitignore inside the renv/ folder that excludes library/ and staging/. If you set up your project through the lab’s workflow tools (covered in later chapters), these exclusions are already in place.

7.5 Updating Packages

Packages get updates — bug fixes, new features, performance improvements. To update everything in your project:

renv::update()

Or update a specific package:

renv::update("ggplot2")

After updating, test that your analysis still runs correctly, then snapshot:

renv::snapshot()

Here’s a useful habit: snapshot and commit before updating. That way, if an update breaks something, you can restore the previous state:

renv::restore()

This reinstalls packages at the versions in renv.lock, effectively undoing the update. Because you committed the old lockfile, you can always get back to a known-good state.

7.6 Troubleshooting

7.6.1 “The project is out-of-sync”

This is the most common renv message and it’s usually not a real problem. It means your installed packages don’t match what’s in renv.lock. Run renv::status() to see the details:

renv::status()

If it shows packages installed but not in the lockfile, you probably installed something and forgot to snapshot — run renv::snapshot(). If it shows packages in the lockfile but not installed, you probably pulled changes from Git — run renv::restore().

7.6.2 Package Installation Fails

Sometimes a package won’t install, especially packages with compiled code (C or Fortran). Try rebuilding from source:

renv::install("package-name", rebuild = TRUE)

For Bioconductor packages, make sure BiocManager is installed and your R version is compatible with the Bioconductor release you need. Bioconductor’s website lists which R versions work with each release.

7.6.3 renv is Slow

The first time you run renv::restore() on a new machine, renv downloads and installs every package from scratch. This can take a while for projects with many packages (Seurat alone has dozens of dependencies). Be patient — subsequent operations are faster because renv maintains a global cache. Packages that are shared across projects only download once; renv links them into each project’s library.

7.6.4 Starting Fresh

If things are really broken and you want to start over:

renv::deactivate()

Then remove the renv files in the terminal:

rm -rf renv renv.lock .Rprofile

And reinitialize:

renv::init()
Warning

This removes your lockfile, so you lose the record of which package versions you had. If you need to preserve that information, copy renv.lock somewhere safe before deleting it.

WarningClaude Code

Claude Code is good at diagnosing package installation errors because it can check your R version, Bioconductor version, and system dependencies all at once.

I’m trying to install DESeq2 with renv::install("bioc::DESeq2") but I’m getting errors about missing dependencies. Here’s the error output: [paste error]. I’m on R 4.4 with renv 1.0. Can you help me figure out what’s wrong?

Claude will check whether the error is a Bioconductor version mismatch, a missing system library, or a network issue, and suggest the specific fix.

7.7 Quick Reference

7.7.1 rig Commands

Task Command
List installed R versions rig list
Install latest R rig add release
Install specific version rig add 4.3
Set default R rig default 4.4
Show default R rig default

7.7.2 renv Commands

Task Command
Initialize renv renv::init()
Install from CRAN install.packages("pkg")
Install from Bioconductor renv::install("bioc::pkg")
Install from GitHub renv::install("user/repo")
Snapshot renv::snapshot()
Restore renv::restore()
Check status renv::status()
Update all packages renv::update()
Update one package renv::update("pkg")