Back to Blog
RNA-SeqRDifferential Expression

Install DESeq2 and edgeR via conda (R/Bioconductor, Apple Silicon)

Build a pinned R and Bioconductor stack with DESeq2 and edgeR inside one isolated conda environment on Apple Silicon, never touching system R.

SSSudipta SardarJuly 20, 20269 min read
Install DESeq2 and edgeR via conda (R/Bioconductor, Apple Silicon)

DESeq2 and edgeR are the two workhorse R/Bioconductor packages for calling differentially expressed genes from RNA-seq count data, and nearly every published differential-expression pipeline leans on one of them. Both ship as prebuilt bioconda packages, so you can get a working Bioconductor stack without compiling anything from source or wrestling with install.packages() and missing system libraries. This guide builds a single conda environment with r-base, DESeq2, and edgeR pinned together, completely isolated from whatever R you already have on your Mac.

Why install through conda instead of install.packages()

The usual route to DESeq2 is BiocManager::install("DESeq2") from inside R. On macOS that often means Xcode command line tools, a matching gfortran, and compiling a long chain of C/C++ and Fortran dependencies from source — well over an hour, breaking in creative ways whenever your compiler toolchain drifts from the R build.

Conda sidesteps all of it. The bioconda packages bioconductor-deseq2 and bioconductor-edger are prebuilt binaries that pull in a matching r-base plus every Bioconductor and CRAN dependency, also prebuilt. Nothing compiles locally, and the whole stack lives inside one environment you can delete and rebuild at will.

Prerequisites

You need a working conda install first. If you do not have one yet, follow our Miniconda on Apple Silicon guide and come back here. This assumes an Apple Silicon Mac (osx-arm64) with a recent conda defaulting to the fast libmamba solver (conda 23.10 and later).

As with every bioconda install on this site, we install only from conda-forge and bioconda and pass --override-channels so conda never touches the Anaconda defaults channel, which is gated behind a Terms-of-Service prompt on recent conda releases.

TL;DR: copy-paste install

bash
conda create -n bu-deseq2 --override-channels -c conda-forge -c bioconda bioconductor-deseq2 bioconductor-edger
conda activate bu-deseq2
R -e 'library(DESeq2); library(edgeR)'

That is the whole install. Expect it to take a while to solve — more on that below — and the rest of this guide walks through each step in detail.

Step 1: Create the environment

Ask for both packages in the same conda create call rather than installing them separately. Solving them together lets conda pick one consistent r-base version and one consistent set of shared dependencies for both packages at once.

bash
conda create -n bu-deseq2 --override-channels -c conda-forge -c bioconda bioconductor-deseq2 bioconductor-edger

This solve is genuinely slow, often several minutes even on a fast connection — that is expected, not a sign something is broken. DESeq2 and edgeR each pull in dozens of Bioconductor and CRAN packages (S4Vectors, IRanges, GenomicRanges, SummarizedExperiment, BiocParallel, limma, locfit, RcppArmadillo, and more) plus r-base itself, and the solver has to find a mutually compatible set of versions across all of them. Let it run; do not kill and retry.

You should see something like this shape of output, trimmed down: conda resolves a large "NEW packages will be INSTALLED" list headed by r-base and dozens of r-* and bioconductor-* packages, downloads several hundred megabytes, and finishes with Executing transaction: done. Do not worry about exact package counts or version numbers — bioconda updates these builds regularly.

text
Solving environment: done

The following NEW packages will be INSTALLED:

  r-base                bioconda/osx-arm64::r-base-...
  bioconductor-deseq2   bioconda/osx-arm64::bioconductor-deseq2-...
  bioconductor-edger    bioconda/osx-arm64::bioconductor-edger-...
  ... (dozens more r-* and bioconductor-* dependencies)

Preparing transaction: done
Verifying transaction: done
Executing transaction: done

Step 2: Verify the install in R

Activate the environment — you need to do this in every new shell — and load both libraries directly:

bash
conda activate bu-deseq2
R -e 'library(DESeq2); library(edgeR); sessionInfo()'

You should see something like this: each library() call prints a few "Loading required package" messages as dependencies attach, no errors, and sessionInfo() lists both packages under "other attached packages" with version strings such as DESeq2_x.y and edgeR_x.y (exact numbers depend on when you installed).

text
Loading required package: S4Vectors
Loading required package: BiocGenerics
...

other attached packages:
[1] edgeR_x.y          limma_x.y          DESeq2_x.y
[4] SummarizedExperiment_x.y   GenomicRanges_x.y   ...

Also confirm this R is the one inside your environment, not a system Homebrew or CRAN install:

bash
which R

It should resolve inside .../envs/bu-deseq2/bin/R. If you get a different path, or R: command not found, activate the environment again.

Step 3: A tiny real usage example

A minimal, runnable example that exercises both packages end to end on a toy count matrix — two genes, three control and three treatment replicates. Far too small to draw any biological conclusion from, but it confirms the install works and shows each package's API.

r
counts <- matrix(
  c(20, 25, 18, 200, 210, 195,
    30, 28, 32,  15,  12,  18),
  nrow = 2, byrow = TRUE,
  dimnames = list(
    c("geneA", "geneB"),
    c("ctrl1", "ctrl2", "ctrl3", "trt1", "trt2", "trt3")
  )
)
condition <- factor(c("ctrl", "ctrl", "ctrl", "trt", "trt", "trt"))

DESeq2 wraps the counts and metadata in a DESeqDataSet, then runs its full normalization, dispersion-estimation, and Wald-test pipeline with a single DESeq() call:

r
library(DESeq2)
coldata <- data.frame(condition = condition, row.names = colnames(counts))
dds <- DESeqDataSetFromMatrix(countData = counts, colData = coldata, design = ~ condition)
dds <- DESeq(dds)
results(dds)

edgeR takes a slightly more manual route: build a DGEList, normalize with calcNormFactors(), estimate dispersion, then test:

r
library(edgeR)
y <- DGEList(counts = counts, group = condition)
y <- calcNormFactors(y)
y <- estimateDisp(y)
et <- exactTest(y)
topTags(et)

Both should run without error and print a results table with a log2 fold change and a p-value for geneA and geneB. For a real experiment, feed in a full count matrix from featureCounts or Salmon plus tximport, with thousands of genes so dispersion estimates have enough data to shrink sensibly. See our RNA-seq differential expression walkthrough for the full statistical picture.

DESeq2 vs edgeR: which one to reach for

Both model counts with a negative binomial distribution and both are well validated, but they differ in defaults and workflow feel:

DESeq2edgeR
NormalizationMedian-of-ratios size factorsTMM (trimmed mean of M-values)
Dispersion estimateEmpirical Bayes shrinkage toward a fitted trendPer-gene, tagwise, or trended, several estimators available
Typical entry pointDESeqDataSetFromMatrix() then DESeq()DGEList() then estimateDisp()
Fold-change shrinkageBuilt in via lfcShrink()Available via glmTreat() / effect-size options
FeelFewer manual steps, more "batteries included"More explicit control over each modeling step

In practice both give similar gene lists on the same dataset. Many labs run both as a cross-check, especially in a course setting.

Apple Silicon and long-solve gotchas

A few things specific to this stack, beyond the generic bioconda advice:

  • The solve really is slow — that is normal. R/Bioconductor environments have one of the largest dependency graphs in bioconda. A Solving environment step hanging for minutes is real work, not a stall — avoid interrupting it.
  • Install both packages in one conda create, not two. Installing them separately can force the solver to downgrade r-base or shared dependencies later, triggering a bigger, slower re-solve.
  • Native osx-arm64 builds exist for this stack. Both packages carry compiled C/C++ code (DESeq2 via Rcpp, edgeR via its own C routines), so bioconda ships genuine osx-arm64 builds rather than relying on Rosetta 2. Confirm with conda list -n bu-deseq2 r-base.
  • If a stray dependency ever lacks an arm64 build, fall back to CONDA_SUBDIR=osx-64 conda create -n bu-deseq2-x86 --override-channels -c conda-forge -c bioconda bioconductor-deseq2 bioconductor-edger, then conda config --env --set subdir osx-64. Rarely needed for this stack today.
  • Do not mix this with system R or RStudio's CRAN R. Keep any BiocManager-installed system R separate, and point RStudio at .../envs/bu-deseq2/bin/R if you want to use this environment there.

Common errors and fixes

ErrorFix
CondaToSNonInteractiveError / Terms of Service not accepted for defaultsDo not use the defaults channel. Install with --override-channels -c conda-forge -c bioconda as shown.
PackagesNotFoundError: ... bioconductor-deseq2The bioconda channel is missing or listed in the wrong order. Pass both channels with conda-forge first: -c conda-forge -c bioconda.
Solve appears stuck on Solving environment for a very long timeExpected for this stack. Give it several minutes; make sure you are on a conda version with the libmamba solver (23.10+), or install mamba and run mamba create instead.
Error: package or namespace load failed for 'DESeq2' after activatingUsually means the environment was only partially installed (interrupted solve). Remove it and recreate: conda remove -n bu-deseq2 --all, then rerun the conda create command.
library(DESeq2) works, but R resolves to a Homebrew or CRAN installYou forgot to activate the environment, or another R is earlier on your PATH. Run conda activate bu-deseq2 and re-check with which R.

Managing the environment

Update both packages in place:

bash
conda update -n bu-deseq2 --override-channels -c conda-forge -c bioconda bioconductor-deseq2 bioconductor-edger

Snapshot the exact environment so a collaborator (or a future you) can reproduce it precisely:

bash
conda env export -n bu-deseq2 > deseq2-edger-env.yml

And when you no longer need it, remove it cleanly. Because everything — r-base included — lived inside the environment, this leaves nothing behind on your system:

bash
conda remove -n bu-deseq2 --all

Next steps

With a working DESeq2/edgeR environment in hand, a couple of natural follow-ons: