What is Snakemake & Why It Matters

Understand the pipeline problem in bioinformatics, discover how Snakemake solves it, install it via conda, and run your very first rule.

📐 Module 13 Lesson 1 of 10 · ⏱ ~50 min · 🐍 Snakemake FREE

01 The Pipeline Problem

Imagine you are processing 48 sorghum RNA-seq samples. Your pipeline has six steps: quality control with FastQC, trimming with Trimmomatic, alignment with HISAT2, sorting with samtools, counting with featureCounts, and differential expression with DESeq2. In a Bash script, you might write a for loop and chain every command together.

This works — until something breaks at step 4 on sample 23. Now you must re-run everything from the beginning, or carefully track exactly where it failed. If you add a new sample later, you run the whole script again even though most outputs already exist. If a collaborator wants to reproduce your results on a different cluster, they need your exact Bash script, your exact sample names, and hope nothing has changed.

💡 This is not a hypothetical problem. It is the reason nearly every published bioinformatics pipeline in Nature Methods, Genome Biology, and Bioinformatics journals now uses a workflow manager — Snakemake, Nextflow, or WDL.

The core issues with raw Bash pipelines are:

  • No dependency tracking — Bash does not know that step 5 depends on step 4. If step 4 fails silently, step 5 runs on incomplete data.
  • No resumability — If the pipeline crashes, you restart from zero unless you write complex checkpoint logic yourself.
  • No parallelism — Running 48 samples sequentially wastes most of your compute time. Parallelising them with & and wait is fragile and hard to control.
  • No reproducibility — The pipeline is tied to your exact machine, your exact paths, your exact software versions.

Snakemake was designed to solve all four of these problems at once.

02 What is Snakemake?

Snakemake is a Python-based workflow management system created by Johannes Köster at the University of Duisburg-Essen in 2012. It is now maintained by a large open-source community and is one of the two dominant workflow systems in bioinformatics (alongside Nextflow).

The key insight behind Snakemake is borrowed from GNU Make: instead of telling the computer how to run things step by step, you tell it what outputs you want and how to produce them from inputs. Snakemake figures out the rest — what order to run things in, which rules are already done, and how many rules can run simultaneously.

Why "Snakemake"?

The name comes from Python (the snake) plus Make (the classic Unix build tool). Snakemake uses Python syntax for rules and logic, but the dependency resolution philosophy comes directly from Make. If you have ever used make to compile C code, you already understand the core concept.

Snakemake is written in Python and its workflow files (Snakefile) use Python syntax. This means you can use any Python expression, import any Python library, and write arbitrarily complex logic inside a Snakemake pipeline — without leaving the Snakemake paradigm.

Why Snakemake for RNA-seq?

An RNA-seq pipeline is a perfect fit for Snakemake because it is inherently a directed acyclic graph (DAG) of file transformations. Each step takes files in and produces files out. Snakemake sees these file relationships and constructs the DAG automatically:

  • FASTQ files → FastQC HTML reports (QC, no file transformation needed)
  • FASTQ files → trimmed FASTQ files (Trimmomatic)
  • Trimmed FASTQ → BAM files (HISAT2 alignment)
  • BAM files → sorted BAM files (samtools sort)
  • Sorted BAM → count matrix (featureCounts)
  • Count matrix → differential expression results (DESeq2 in R)

03 How Snakemake Thinks

Understanding how Snakemake resolves rules is essential before writing any code. Snakemake works backwards from the target output.

When you ask Snakemake to produce results/counts.txt, it asks: "Which rule produces results/counts.txt?" It finds that rule, then looks at that rule's inputs. It asks: "Do these inputs already exist?" If not, it finds the rules that produce those inputs, and so on — building a dependency tree (DAG) from target back to source files.

Once the DAG is complete, Snakemake executes it in the correct order, running independent rules in parallel. If a rule has already produced its output and the output file is newer than the input, Snakemake skips that rule. This is called up-to-date checking — you get free resumability.

The DAG in plain English

A Directed Acyclic Graph (DAG) is a map of which steps depend on which other steps, where the arrows only go forward (acyclic = no loops). Snakemake draws this map for your pipeline automatically based on which files each rule reads and writes. You never have to specify the order manually — Snakemake infers it from the file relationships.

🌱 In your sorghum BPV project, if you re-run the pipeline after fixing a single alignment parameter, Snakemake will only re-run alignment and everything downstream of it — not the trimming step, which was already correct. This alone saves hours of compute time on real datasets.

04 Installing Snakemake

Snakemake is best installed inside a dedicated conda environment. This keeps it isolated from other tools and makes it easy to recreate the environment on another machine.

Run from: any location — we are creating a new conda environment
Bash
# Create a dedicated environment for Snakemake
conda create -n snakemake-env -c conda-forge -c bioconda snakemake -y

# Activate the environment
conda activate snakemake-env

# Verify the installation
snakemake --version
Why -c conda-forge -c bioconda?

Snakemake has many dependencies — Python packages, graphing libraries, and optional cluster-submission tools. The conda-forge channel provides current versions of most Python packages. The bioconda channel provides bioinformatics tools including Snakemake itself. Specifying both channels ensures conda can find and resolve all dependencies correctly. Without bioconda, conda would not find Snakemake at all.

Expected output from --version:

Output
8.x.x
⚠️ If you see PackagesNotFoundError, you may need to add the channels first: conda config --add channels bioconda and conda config --add channels conda-forge, then retry.

Create the project directory

Let's set up a clean project directory for this entire Snakemake module. We will build on this directory across all 10 lessons.

Run from: ~/
Bash
# Create the project tree for the entire module
mkdir -p ~/rnaseq-snakemake-pipeline/{data/raw,data/trimmed,results,logs,envs}

# Move into the project root
cd ~/rnaseq-snakemake-pipeline

# Confirm structure
tree -L 2
Why this directory structure?

Snakemake does not enforce a directory structure, but every major bioinformatics group uses a convention: data/raw/ for unprocessed FASTQ files (never modified), data/trimmed/ for adapter-trimmed reads, results/ for final outputs, logs/ for per-rule log files, and envs/ for per-rule conda environment YAML files. This layout keeps raw data protected, separates intermediate files from final results, and makes the pipeline self-documenting. In your sorghum project this structure would contain your actual FASTQ files from JLU Gießen's sequencing core.

Expected output:

Output
.
├── data
│   ├── raw
│   └── trimmed
├── envs
├── logs
└── results

05 Your First Rule

Every Snakemake workflow lives in a file called Snakefile (capital S, no extension). Let's write the simplest possible rule — one that copies a FASTQ file from data/raw/ to results/. This is intentionally trivial: the goal is to understand the anatomy of a rule before adding real bioinformatics commands.

Run from: ~/rnaseq-snakemake-pipeline/

First, create a small test FASTQ file to work with:

Bash
# Create a small fake FASTQ for testing (4-line FASTQ format)
cat > data/raw/sorghum_sample1.fastq << 'EOF'
@SRR1234567.1 sorghum read 1
ATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCGATCG
+
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
@SRR1234567.2 sorghum read 2
GCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTAGCTA
+
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
EOF

Now create the Snakefile:

Snakemake (Python)
# Snakefile — Lesson 1: your first rule
# Save this as: ~/rnaseq-snakemake-pipeline/Snakefile

# The 'rule all' tells Snakemake what the final target output is.
# It is always listed first and drives the entire pipeline.
rule all:
    input:
        "results/sorghum_sample1.fastq"


# This rule copies a FASTQ file from data/raw/ to results/
rule copy_fastq:
    input:
        "data/raw/sorghum_sample1.fastq"
    output:
        "results/sorghum_sample1.fastq"
    shell:
        "cp {input} {output}"
Anatomy of a Snakemake rule

Every rule has a name (e.g. copy_fastq), an input block listing the files the rule needs, an output block listing the files it will produce, and an action block — either shell:, script:, or run:. Inside the shell: string, {input} and {output} are automatically replaced with the actual file paths at runtime. The rule all: is a special convention: its input block lists the final targets of the entire pipeline. Snakemake traces backwards from these targets to figure out which other rules to run.

⚠️ Indentation is mandatory. Snakemake uses Python-style indentation — 4 spaces per level. If you mix tabs and spaces, or forget to indent the input:, output:, and shell: directives, Snakemake will throw a syntax error. Always use 4 spaces, never tabs.

06 Dry Run & Execution

Before running any rule for real, always do a dry run first. A dry run tells you exactly what Snakemake would do — which rules it would execute and in what order — without actually executing any shell commands. This is one of Snakemake's most valuable features.

Run from: ~/rnaseq-snakemake-pipeline/
Bash
# Dry run — shows what would happen without running anything
snakemake --dry-run --printshellcmds
Why dry run before every real run?

On a 48-sample RNA-seq dataset, a real run could use 20 CPU cores and run for 6 hours. If your Snakefile has a typo, you might not discover it until hour 5. A dry run catches syntax errors, missing input files, and rule dependency problems in seconds — before wasting any compute time. The --printshellcmds flag additionally prints the exact shell commands that would be executed, so you can verify them.

Expected dry-run output:

Output
Building DAG of jobs...
Job stats:
job          count
-----------  -------
all          1
copy_fastq   1
total        2

Reasons:
    (check individual jobs above for details)
    output files have to be generated:
        copy_fastq: results/sorghum_sample1.fastq

rule copy_fastq:
    input: data/raw/sorghum_sample1.fastq
    output: results/sorghum_sample1.fastq
    jobid: 1
    reason: Missing output files: results/sorghum_sample1.fastq
    resources: tmpdir=/tmp

cp data/raw/sorghum_sample1.fastq results/sorghum_sample1.fastq

This was a dry-run (flag --dry-run). The order of jobs does not reflect the order of execution.

Now run it for real:

Bash
# Real run — executes all rules needed to produce the target
snakemake --cores 1

# Verify the output was created
ls -lh results/
Why --cores?

Snakemake requires you to specify how many CPU cores it can use. This is deliberate — Snakemake can run many rules in parallel, and it needs to know how many parallel jobs are allowed. On your laptop, --cores 4 is a safe choice (uses 4 threads). On the JLU Gießen HPC cluster, you might specify --cores 32 or hand control to SLURM (covered in Lesson 9). Using --cores 1 here ensures sequential execution, which is easiest to follow for learning.

Expected output from ls -lh results/:

Output
total 4.0K
-rw-r--r-- 1 shajedur shajedur 200 Aug 27 10:43 sorghum_sample1.fastq

Run it again — watch Snakemake skip

Now run the exact same command a second time, without changing anything:

Bash
snakemake --cores 1

Expected output:

Output
Building DAG of jobs...
Nothing to be done (all requested files are present and up to date).
This is the core superpower

"Nothing to be done" is Snakemake telling you: "All outputs already exist and are newer than their inputs — there is nothing to recompute." In a real 48-sample pipeline, this means you can safely re-run snakemake --cores 16 at any time and it will only redo work that is actually necessary. You do not need to track which samples are done and which are not — Snakemake handles that entirely via file timestamps.

Advertisement Supports free bioinformatics education

07 Quick Reference

Command / Concept What it does When to use it
snakemake --dry-run Shows which rules would run without executing them Before every real run
snakemake --cores N Runs the pipeline using N CPU cores (enables parallelism) Every real run
snakemake --printshellcmds Prints each shell command before executing it Debugging
snakemake --forcerun RULE Forces a specific rule to re-run even if output exists After changing a rule's logic
snakemake --dag | dot -Tsvg > dag.svg Generates a visual graph of the pipeline DAG Understanding complex pipelines
rule all: Special rule that defines the final targets of the pipeline Always first in every Snakefile
input: / output: Declare files a rule reads (input) and writes (output) Every rule
shell: The shell command to execute; {input} and {output} are placeholders Most bioinformatics rules

08 Exercises

1 Verify your Snakemake installation

Activate your snakemake-env conda environment and run snakemake --version. What version is installed? Then run snakemake --help | head -40 and find the flag that limits the maximum number of jobs. Write it down.

Show answer
Bash
conda activate snakemake-env
snakemake --version
# Should print something like: 8.x.x

snakemake --help | head -40
# Look for: --jobs N or -j N (maximum number of parallel jobs)
2 Add a second rule that compresses the output

Modify your Snakefile to add a second rule called compress_fastq that uses gzip to compress results/sorghum_sample1.fastq into results/sorghum_sample1.fastq.gz. Update rule all to request the compressed file. Run a dry run first, then run for real.

Show answer
Snakemake
rule all:
    input:
        "results/sorghum_sample1.fastq.gz"

rule copy_fastq:
    input:  "data/raw/sorghum_sample1.fastq"
    output: "results/sorghum_sample1.fastq"
    shell:  "cp {input} {output}"

rule compress_fastq:
    input:  "results/sorghum_sample1.fastq"
    output: "results/sorghum_sample1.fastq.gz"
    shell:  "gzip -k {input}"

The -k flag tells gzip to keep the original file. Snakemake automatically figures out that compress_fastq must run after copy_fastq because its input is copy_fastq's output.

3 Visualise the DAG

Install graphviz in your environment (conda install graphviz), then run the DAG command below and open the resulting SVG in your browser. How many nodes and edges does the graph have?

Show answer
Bash
conda install -c conda-forge graphviz -y
snakemake --dag | dot -Tsvg > dag.svg
xdg-open dag.svg   # Linux viewer

With the two-rule pipeline, the DAG has 3 nodes (all → compress_fastq → copy_fastq) and 2 directed edges. Each box represents one rule execution; the arrows show which rule depends on which.

Ready to move on?

Advertisement Supports free bioinformatics education