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.
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:
fastqc *.fastq.gzStarted 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:
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 module | What a healthy sample looks like | Common red flag |
|---|---|---|
| Per-base sequence quality | Median Phred score above 30 for most of the read | Sharp quality drop well before the read ends |
| Adapter content | Flat, near zero across the read | Rising curve toward the 3' end |
| Sequence duplication levels | Low for DNA, expected to be higher for RNA-seq | Very high duplication in DNA/exome data |
| Per-sequence GC content | Roughly 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:
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 4Here is what each flag is doing:
-i/-Iare the input mate 1 and mate 2 FASTQ files.-o/-Oare the trimmed output mate 1 and mate 2 files.--detect_adapter_for_pelets 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_sequenceexplicitly.--json/--htmlwrite fastp's own report — the HTML is human-readable, and the JSON is what MultiQC will parse later.-wsets 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:
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.
fastp -i sample.fastq.gz -o sample.trimmed.fastq.gz \
--json sample.fastp.json --html sample.fastp.htmlRun 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:
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:
multiqc qc_raw/ qc_trimmed/ fastp_reports/ -n multiqc_report -o multiqc/Common Pitfalls
| Symptom | Likely cause | Fix |
|---|---|---|
| MultiQC report is empty or missing a tool's section | Ran from the wrong directory, or FastQC/fastp output files were deleted | Run 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 it | Pass the adapter explicitly with --adapter_sequence (and --adapter_sequence_r2 for the second mate) |
| Aligner still reports high adapter-derived soft-clipping after trimming | Trimmed the wrong files, or aligned reads before trimming finished | Double check the aligner command points at the .trimmed.fastq.gz outputs, not the originals |
| FastQC "fail" on GC content or duplication for RNA-seq | Often expected biology, not a defect | Cross-check against the actual plot and your library type before treating it as an error |
| Very slow FastQC run on many large files | Default is single-threaded per file | Add -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.