A reproducible, config-driven Snakemake pipeline that takes paired-end RNA-seq FASTQ files from raw reads to a statistically rigorous differential expression report: fastp (QC/trimming) → salmon (alignment-free quantification) → tximport + DESeq2 (R) → an automated HTML report with PCA, volcano, MA, and heatmap plots.
It is built to be general-purpose: point config/samples.tsv and config/config.yaml at any two-condition, paired-end dataset and reference transcriptome, and the same pipeline runs end-to-end — nothing in the Snakefile or scripts is hard-coded to the demo dataset described below.
Most portfolio RNA-seq projects stop at "I ran DESeq2 in an R script." This one is built the way a production pipeline is expected to look:
- Config-driven, not hard-coded — swap datasets by editing a TSV and a YAML file
- Reproducible environments — every rule's tools are pinned in
envs/*.yaml;environment.ymlgives a one-command local setup - Statistically defensible DE calls —
apeglmshrinkage on log2 fold changes (not raw MLE estimates), explicit multiple-testing correction, and a documented significance rule - Continuous integration — every push runs the full pipeline on real (subsampled) sequencing data in GitHub Actions, so a broken environment or rule fails immediately, not months later when someone else tries to reuse it
- A real biological question, not synthetic data (see below)
FASTQ (paired-end)
│
▼
fastp — adapter/quality trimming, per-sample QC report
│
▼
salmon index — build a k-mer index from the reference transcriptome
│
▼
salmon quant — alignment-free transcript-level quantification
│
▼
MultiQC — aggregate fastp + salmon QC into one report
│
▼
tximport + DESeq2 — gene-level count aggregation, size-factor normalization,
│ dispersion estimation, Wald test, apeglm LFC shrinkage
▼
R Markdown report — PCA, volcano, MA plot, top-gene heatmap, results table
Each stage is a Snakemake rule (workflow/rules/*.smk); Snakefile wires them together and config/config.yaml controls all dataset-specific parameters (which condition is the reference level, significance thresholds, k-mer size, etc.).
To validate the pipeline on real biology rather than synthetic counts, the included demo compares:
- 3 wild-type replicates vs.
- 3 replicates after 2 hours of auxin-induced Rap1 depletion
Rap1 is an essential, highly pleiotropic yeast transcription factor (ribosomal protein gene expression, telomere silencing), so acute depletion should produce strong, biologically real differential expression — a good stress test for the whole pipeline, not just a toy example. Data are subsampled FASTQs from nf-core/test-datasets (originally Kubik et al.), and the reference is the S. cerevisiae R64-1-1 transcriptome (Ensembl release 112).
Note on the demo data: the FASTQs are subsampled to ~100K read pairs per sample purely so the whole pipeline runs in a couple of minutes on a laptop or in CI. That's enough to demonstrate that every stage works correctly end-to-end and to recover real signal (see results below), but the gene list and mapping rate (~36%, since this is total RNA-seq mapped against a cDNA-only index) should not be read as a complete or publication-grade Rap1 regulon — that would require the full-depth dataset.
| Metric | Value |
|---|---|
| Genes tested (after low-count filtering) | 97 |
| Significant (padj < 0.05, |log2FC| > 1) | 9 |
| Comparison | RAP1_DEPLETED_2H vs. WT |
Samples separate cleanly by condition on PC1 (78% of variance) — the experimental effect dominates technical noise even in this small, subsampled dataset:
Volcano plot of shrunken log2 fold changes vs. adjusted p-value:
Top significant genes cluster samples by condition, as expected:
Full outputs: docs/example_results/deseq2_results.csv · rendered HTML report
git clone https://github.com/Zach-Girard/rnaseq-diffexp.git
cd rnaseq-diffexp
# 1. Create the environment (Snakemake, salmon, fastp, MultiQC, R/DESeq2 stack)
conda env create -f environment.yml
conda activate rnaseq-diffexp
conda install -c conda-forge pandoc # required for the R Markdown report
# 2. Download the demo dataset + reference transcriptome (~30 MB)
bash workflow/scripts/download_test_data.sh
# 3. Run the full pipeline
snakemake --cores 4
# Outputs land in results/, including results/diffexp/report.html- Replace
config/samples.tsvwith your ownsample,condition,fastq_1,fastq_2rows. - Point
reference.transcriptome/reference.t2ginconfig/config.yamlat your organism's transcriptome FASTA and a transcript→gene mapping. - Set
diffexp.reference_level/diffexp.treatment_levelto match yourconditioncolumn. snakemake --cores N.
Snakefile Pipeline entry point / rule "all"
config/
config.yaml All tunable parameters (paths, thresholds, k-mer size)
samples.tsv Sample sheet (generated by the download script for the demo)
workflow/
rules/ One .smk file per pipeline stage
scripts/
download_test_data.sh Fetches demo FASTQs + yeast reference (not committed to git)
run_deseq2.R tximport + DESeq2 + apeglm shrinkage
make_readme_figures.R Regenerates the PNGs used in this README
envs/ Per-rule conda environments (for `snakemake --use-conda`)
environment.yml One-shot environment for local/CI use
report/
diffexp_report.Rmd Parameterized R Markdown report template
docs/
figures/ PNGs embedded in this README
example_results/ Committed example output (small CSV + HTML) from the demo run
.github/workflows/ci.yml Runs the full pipeline on every push
Snakemake · salmon · fastp · MultiQC · tximport · DESeq2 · apeglm (shrinkage estimation) · EnhancedVolcano · R Markdown (parameterized, reproducible reporting) · conda environment management · GitHub Actions CI · config-driven pipeline design (dataset-agnostic by construction)
MIT — see LICENSE.


