Back to Blog
QCTrimmingNGS

NGS Read Quality Control and Trimming with FastQC, fastp, and MultiQC

Learn to read FastQC quality reports, trim adapters and low-quality bases with fastp, and roll up per-sample results with MultiQC.

SSSudipta SardarJuly 20, 20269 min read
NGS Read Quality Control and Trimming with FastQC, fastp, and MultiQC

Before you align a single read or count a single transcript, every NGS pipeline should pass through quality control. Raw FASTQ files from an Illumina run almost always carry some mix of adapter contamination, quality decay toward the read end, and PCR duplication, none of it visible until you look. This guide walks through the standard three-tool loop: fastqc to inspect raw reads, fastp to trim and filter them, and multiqc to aggregate every sample's reports into one dashboard.

Why QC Comes Before Everything Else

Skipping QC does not make bad reads disappear — it just moves the damage downstream, where it is harder to diagnose. Adapter sequences left in reads can:

  • Inflate false variant calls where an adapter mismatches the reference in a short alignment.
  • Bias transcript quantification by mapping partially to the wrong isoform or gene.
  • Inflate duplication and GC-content estimates in a way that looks like biology but is really a library-prep artifact.

The fix is a short, repeatable loop run on every batch before reads touch an aligner: inspect, trim, re-check. fastqc handles the inspection, fastp does the trimming in one fast pass, and multiqc turns a folder of individual reports into one page you can scan across dozens of samples at once.

Step 1: Inspect Raw Reads with FastQC

FastQC reads each FASTQ file, computes a set of per-file quality metrics, and writes an HTML report plus a zipped data file. Point it at every raw file in a directory at once:

bash
fastqc *.fastq.gz
Started analysis of sample_R1.fastq.gz
Approx 5% complete for sample_R1.fastq.gz
Approx 10% complete for sample_R1.fastq.gz
...
Analysis complete for sample_R1.fastq.gz

For a lane with paired-end reads, run it on both mates:

bash
fastqc sample_R1.fastq.gz sample_R2.fastq.gz -o qc_raw/ -t 4

-o sends output to a dedicated directory instead of cluttering the FASTQ folder, and -t sets the thread count so FastQC processes several files in parallel. Each run produces sample_R1_fastqc.html (open in a browser) and sample_R1_fastqc.zip (the machine-readable data MultiQC parses later).

Step 2: Read the FastQC Modules That Matter

FastQC packs a dozen modules into one report, but for a routine NGS QC pass, three of them do most of the work.

Per-Base Sequence Quality

This box-and-whisker plot shows the Phred quality distribution at every read position. Healthy Illumina data holds a median quality above 30 (an error rate below 1e-3) across most of the read, with a gradual drop only in the last 10 to 20 cycles — a normal side effect of sequencing-by-synthesis chemistry. A steep cliff earlier than that, or whiskers spanning the whole scale, is your cue to trim.

Adapter Content

This module tracks the fraction of reads where a known adapter (Illumina TruSeq, Nextera, or similar) is detected at each position. It stays flat near zero for most of the read, then curves upward toward the 3' end on short-insert libraries, where the sequencer reads past the DNA fragment and into the adapter. Adapter content above a few percent is worth trimming, since untrimmed adapter reads fail to map or map with soft-clipped, ambiguous ends.

Sequence Duplication Levels

This module reports what fraction of reads are exact or near-exact duplicates. Some duplication is expected in RNA-seq, where highly expressed transcripts naturally produce many identical reads — a high percentage there is not automatically a problem. In DNA sequencing (WGS, exome, ChIP-seq), high duplication more often signals PCR over-amplification from too little starting material, and is worth flagging before variant calling.

FastQC moduleWhat a healthy sample looks likeCommon red flag
Per-base sequence qualityMedian Phred score above 30 for most of the readSharp quality drop well before the read ends
Adapter contentFlat, near zero across the readRising curve toward the 3' end
Sequence duplication levelsLow for DNA, expected to be higher for RNA-seqVery high duplication in DNA/exome data
Per-sequence GC contentRoughly matches the expected genome/transcriptome GC%A second peak (contamination)

FastQC flags modules with a green check, an amber warning, or a red fail icon, but those thresholds are generic defaults, not a verdict on your experiment. Always look at the actual plot — a "fail" on GC content or duplication is often expected biology (rRNA-depleted RNA-seq, amplicon panels) rather than a technical problem.

Step 3: Trim and Filter with fastp

Once you know what is wrong, fastp fixes most of it in a single fast pass. It combines adapter trimming, quality filtering, length filtering, and its own before/after QC report into one binary, which is why it has largely replaced the older Trimmomatic-plus-FastQC combo.

For paired-end reads, the basic call looks like this:

bash
fastp -i sample_R1.fastq.gz -I sample_R2.fastq.gz \
      -o sample_R1.trimmed.fastq.gz -O sample_R2.trimmed.fastq.gz \
      --detect_adapter_for_pe \
      --json sample.fastp.json --html sample.fastp.html \
      -w 4

Here is what each flag is doing:

  • -i / -I are the input mate 1 and mate 2 FASTQ files.
  • -o / -O are the trimmed output mate 1 and mate 2 files.
  • --detect_adapter_for_pe lets fastp auto-detect the adapter sequence per pair by overlap analysis, rather than requiring you to specify it. For single-end reads, fastp falls back to matching against its built-in adapter list unless you pass --adapter_sequence explicitly.
  • --json / --html write fastp's own report — the HTML is human-readable, and the JSON is what MultiQC will parse later.
  • -w sets worker threads.

fastp's defaults are sensible for most Illumina data out of the box: it drops bases below Phred quality 15 from read ends, discards reads shorter than 15 bp after trimming, and trims poly-G tails common on NovaSeq/NextSeq two-color chemistry. You can tighten any of these explicitly:

bash
fastp -i sample_R1.fastq.gz -I sample_R2.fastq.gz \
      -o sample_R1.trimmed.fastq.gz -O sample_R2.trimmed.fastq.gz \
      --detect_adapter_for_pe \
      --qualified_quality_phred 20 \
      --length_required 36 \
      --json sample.fastp.json --html sample.fastp.html

--qualified_quality_phred 20 raises the minimum "qualified" base quality to Q20 (from the Q15 default), and --length_required 36 discards reads shorter than 36 bp after trimming — a common floor for short-read RNA-seq alignment. For single-end data, drop -I/-O and pass only -i/-o.

bash
fastp -i sample.fastq.gz -o sample.trimmed.fastq.gz \
      --json sample.fastp.json --html sample.fastp.html

Run fastqc again on the trimmed output before you move on. Comparing the raw and trimmed reports side by side in MultiQC is the fastest way to confirm the trim actually removed the adapter curve and cleaned up the quality tail, rather than just trusting that the command finished without errors.

Step 4: Aggregate Everything with MultiQC

Opening a dozen FastQC HTML files one at a time does not scale. MultiQC scans a directory tree for known report formats — FastQC zips, fastp JSON files, aligner logs, and dozens of other tools — and merges them into one interactive HTML report with a row per sample.

Run it from the parent directory containing your qc_raw/, qc_trimmed/, and fastp output:

bash
multiqc .
[INFO   ]         multiqc : This is MultiQC v1.21
[INFO   ]         multiqc : Search path : /home/user/project
Searching   100%  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 48/48
[INFO   ]          fastqc : Found 24 reports
[INFO   ]           fastp : Found 12 reports
[INFO   ]         multiqc : Report      : multiqc_report.html
[INFO   ]         multiqc : Data        : multiqc_data
[INFO   ]         multiqc : MultiQC complete

MultiQC writes multiqc_report.html, with summary tables and plots stacking every sample's per-base quality, adapter content, and duplication curves onto the same axes. This is where problems easy to miss in a single-sample FastQC report jump out — one lane with a quality dip, one sample with an adapter spike the others lack, or a batch effect across a whole run.

You can scope the search to specific subfolders instead of the whole project, and name the output explicitly:

bash
multiqc qc_raw/ qc_trimmed/ fastp_reports/ -n multiqc_report -o multiqc/

Common Pitfalls

SymptomLikely causeFix
MultiQC report is empty or missing a tool's sectionRan from the wrong directory, or FastQC/fastp output files were deletedRun multiqc . from the parent directory holding all report files; keep the .zip/.json outputs, not just the HTML
fastp reports almost no adapter trimming on data you know has adapters--detect_adapter_for_pe needs enough read overlap to detect itPass the adapter explicitly with --adapter_sequence (and --adapter_sequence_r2 for the second mate)
Aligner still reports high adapter-derived soft-clipping after trimmingTrimmed the wrong files, or aligned reads before trimming finishedDouble check the aligner command points at the .trimmed.fastq.gz outputs, not the originals
FastQC "fail" on GC content or duplication for RNA-seqOften expected biology, not a defectCross-check against the actual plot and your library type before treating it as an error
Very slow FastQC run on many large filesDefault is single-threaded per fileAdd -t N to process N files in parallel

Wrap-Up

FastQC, fastp, and MultiQC cover the full loop from raw reads to a clean, trimmed FASTQ file you can trust downstream: FastQC tells you what is wrong, fastp fixes it in one fast pass, and MultiQC turns dozens of reports into one dashboard worth reading. Treat this as the mandatory first stage of any pipeline — before alignment with BWA or Bowtie2, and before the rest of an RNA-seq differential expression workflow. All three tools install from bioconda into an isolated environment; see Conda environments and Bioconda channels for the channel setup that keeps them reproducible.