This workflow processes single-end or paired-end bulk RNA-seq FASTQ files
from raw reads through to transcript-level quantification and a
consolidated QC report.
The workflow is built in a modular fashion, where most of the base
functionality is provided by components frombiobox, supplemented
by the bgzip component fromtoolbox and the
workflow logic in this package.
Processing happens in the following stages:
FastQC - raw read quality control
TrimGalore - adapter trimming and quality filtering (skippable via --skip_trim)
FastQC - trimmed read quality control
Kallisto - builds a transcriptome index once (shared across samples), then pseudo-aligns each sample for preliminary quantification
STAR genome index - builds a genome index once (shared across all samples); gzipped genome FASTA/GTF inputs are decompressed automatically since STAR requires plain-text references
STAR alignment - aligns trimmed reads to the genome, producing both genome and transcriptome BAMs
samtools sort - coordinate-sorts the genome BAM (required for Qualimap)
Qualimap - coordinate-based QC (exon coverage, 5'/3' bias)
Salmon quant - transcript-level quantification in alignment mode using the transcriptome BAM
MultiQC - aggregates QC metrics from all tools and samples into a single report
The Kallisto and STAR index-build steps each fan in all samples (run
once), then fan back out so every sample is processed independently.
Kallisto quantification, STAR alignment, Qualimap, and Salmon all run
concurrently where their inputs allow; a failed QC step (FastQC,
Qualimap) leaves the sample in the output rather than dropping it
entirely.
Viash >= 0.9.7
Nextflow — pin NXF_VER=25.10.2; Nextflow 26.x's strict parser breaks -entry and some viash-hub component configs
Docker (enabled in nextflow.config)
Component tools are resolved from biobox and toolbox and run inside
Docker containers — no manual tool installation is required.
Build all components and workflows with Viash:
viash ns build --setup cachedbuild --parallel This resolves dependencies from the biobox and toolbox repositories, sets up their Docker images, and generates Nextflow modules under target/nextflow/.
bulk-rnaseq/
├── _viash.yaml # Project config (name, version, source/target dirs, repositories)
├── nextflow.config # Nextflow config (Docker enabled)
├── src/
│ ├── config/
│ │ └── labels.config # Shared resource labels (cpus/memory) included in every built workflow
│ └── workflows/
│ └── bulk_rnaseq/ # Main workflow
│ ├── config.vsh.yaml # Workflow inputs, outputs, dependencies, and test_resources
│ ├── main.nf # Workflow logic (VDSL3/Nextflow)
│ ├── test.nf # Test workflows (test_wf, test_plain_gtf, test_provided_index, multiple_lane_wf)
│ └── integration_test.sh # Runs every test.nf entrypoint against Docker
├── resources_test_scripts/
│ └── bulk_rna_test.sh # Fetches/generates all files under resources_test/
├── resources_test/ # Test fixtures (FASTQs, references, indices) — generated, not committed
│ ├── samplesheet_test.csv # Example samplesheet with nf-core test data URLs
│ └── param_list_test.yaml # Same, as a --param_list YAML file
└── target/ # Generated output from viash build (not committed) Test data is the nf-core RNA-seq
GSE110004
dataset: a small paired-end subset plus a matching genome FASTA,
transcriptome FASTA, and GTF. Fetch it (and derive the plain/gzipped
variants and prebuilt indices the test suite needs) with:
bash resources_test_scripts/bulk_rna_test.sh Fixtures land in resources_test/.
The easiest way to see all available parameters is the integrated help:
nextflow run target/nextflow/workflows/bulk_rnaseq/main.nf --help Viash builds this in: every built workflow accepts individual CLI arguments
(single sample) or a --param_list file (multiple samples). Column
names in the param_list map directly to argument names.
Pass -profile docker,local — docker runs each component in its container,local scales CPU/memory requests down to laptop size (see Resource usage
tuning to adjust).
Single sample:
nextflow run target/nextflow/workflows/bulk_rnaseq/main.nf \
--id sample1 \
--input_fastq_1 reads_R1.fastq.gz \
--input_fastq_2 reads_R2.fastq.gz \
--genome_fasta genome.fasta \
--gtf annotation.gtf.gz \
--transcript_fasta transcriptome.fasta \
-profile docker,local Multiple samples via param_list — put per-sample columns in a CSV, pass shared reference files on the CLI:
nextflow run target/nextflow/workflows/bulk_rnaseq/main.nf \
--param_list samples.csv \
--genome_fasta genome.fasta \
--gtf annotation.gtf.gz \
--transcript_fasta transcriptome.fasta \
-profile docker,local samples.csv:
id,input_fastq_1,input_fastq_2,strandedness
SRR6357070,path/to/SRR6357070_R1.fastq.gz,path/to/SRR6357070_R2.fastq.gz,reverse
SRR6357071,path/to/SRR6357071_R1.fastq.gz,,reverse The id column becomes the sample identifier. Leave input_fastq_2
empty for single-end samples. Relative paths in the CSV are resolved
relative to the CSV file's own location, not the directory nextflow
is launched from. Any argument can appear in the CSV; CLI values act
as defaults for columns not present. --param_list accepts CSV, JSON,
or YAML — equivalently, in YAML:
param_list:
- id: SRR6357070
input_fastq_1: path/to/SRR6357070_R1.fastq.gz
input_fastq_2: path/to/SRR6357070_R2.fastq.gz
strandedness: reverse
genome_fasta: genome.fasta
gtf: annotation.gtf.gz
transcript_fasta: transcriptome.fasta For a sample sequenced across multiple lanes, separate the per-lane files with ; in the same cell:
id,input_fastq_1,input_fastq_2,strandedness
SRR6357070,SRR6357070_L001_R1.fastq.gz;SRR6357070_L002_R1.fastq.gz,SRR6357070_L001_R2.fastq.gz;SRR6357070_L002_R2.fastq.gz,reverse | Parameter | Required | Description |
--input_fastq_1 | yes | R1 FASTQ (or single-end FASTQ). Pass multiple files if the sample was sequenced across multiple lanes — they are concatenated automatically before any downstream processing. |
--input_fastq_2 | no | R2 FASTQ for paired-end runs. Same multi-lane behaviour as --input_fastq_1. |
--strandedness | no | forward, reverse (default), or unstranded |
--genome_fasta | yes | Reference genome FASTA (plain or gzipped) |
--gtf | yes | Gene annotation GTF (plain or gzipped) |
--transcript_fasta | yes | Transcript FASTA for Kallisto and Salmon (alignment mode) |
--genome_sa_index_nbases | no | STAR index parameter; use 7 for small/test genomes (default: 14) |
--star_index | no | Pre-built STAR genome index directory; skips index generation if provided |
--kallisto_index | no | Pre-built Kallisto index file; skips index generation if provided |
--skip_trim | no | Skip TrimGalore adapter trimming; raw reads go straight to alignment |
--fragment_length | no | Estimated average fragment length (required by Kallisto for single-end samples) |
--sd | no | Estimated fragment length standard deviation (required by Kallisto for single-end samples) |
Key outputs:
| Parameter | Description |
--genome_bam / --transcriptome_bam | Coordinate-sorted genome BAM and transcriptome-aligned BAM from STAR |
--kallisto_output | Kallisto pseudo-alignment output directory (abundance.tsv, abundance.h5, run_info.json) |
--salmon_output / --salmon_quant_sf | Salmon quantification output directory and per-transcript counts file |
--qualimap_output | Qualimap rnaseq_qc_results.txt (exon coverage, 5'/3' bias) |
--multiqc_report | Run-level MultiQC HTML report aggregating QC from all tools and samples |
See config.vsh.yaml for the full list of output arguments (per-step FastQC/TrimGalore reports, logs, etc.).
Nextflow's labels can be used to specify the amount of resources a
process can use. This workflow uses the following labels for CPU and
memory:
verylowmem, lowmem, midmem, highmem
verylowcpu, lowcpu, midcpu, highcpu
The defaults for these labels can be found at src/config/labels.config.
Nextflow checks that the specified resources for a process do not exceed
what is available on the machine and will not start if it does. Create
your own config file to tune the labels to your needs, for example:
withLabel: verylowcpu { cpus = 2 }
withLabel: lowcpu { cpus = 8 }
withLabel: midcpu { cpus = 16 }
withLabel: highcpu { cpus = 32 }
withLabel: verylowmem { memory = { get_memory( 4.GB * task.attempt ) } }
withLabel: lowmem { memory = { get_memory( 8.GB * task.attempt ) } }
withLabel: midmem { memory = { get_memory( 16.GB * task.attempt ) } }
withLabel: highmem { memory = { get_memory( 64.GB * task.attempt ) } } When starting Nextflow using the CLI, you can use -c to provide the
file to Nextflow and overwrite the defaults.
Fetch/generate the test fixtures first (FASTQs, reference files, and pre-built STAR/Kallisto indices — including the lane-split FASTQs used by multiple_lane_wf):
bash resources_test_scripts/bulk_rna_test.sh test.nf has several entrypoints, each exercising a different scenario:
| Entrypoint | Covers |
test_wf | 2 paired-end + 1 single-end sample; gzipped GTF, plain genome FASTA |
test_plain_gtf | Plain GTF + gzipped genome FASTA — exercises both bgzip decompress/skip branches |
test_provided_index | Pre-built --star_index / --kallisto_index skip index generation |
multiple_lane_wf | Multi-lane paired-end + single-end; exercises the concat_text concatenation branch (runIf: true) |
Run all four against Docker in one go:
export NXF_VER=25.10.2
bash src/workflows/bulk_rnaseq/integration_test.sh Or run a single entrypoint directly:
export NXF_VER=25.10.2
nextflow run src/workflows/bulk_rnaseq/test.nf \
-c src/config/labels.config \
-entry test_wf \
-profile docker,local The pipeline uses TrimGalore for adapter trimming. Cutadapt is also available in biobox and could replace it, but TrimGalore is the better default here for three reasons:
Auto-detects adapters — no need to specify adapter sequences explicitly
Produces trimming reports that MultiQC parses out of the box
Handles paired-end read synchronisation automatically
Cutadapt is worth considering if adapter sequences are known upfront and precise control over trimming parameters is needed. Note that cutadapt's output argument is multiple: true in biobox, which requires an explicit output glob pattern in fromState — see the vdsl3 skill for details.
--genome_fasta and --gtf both accept plain or gzip-compressed files. FastQC, TrimGalore, Kallisto, and Salmon all read .gz natively, so no special handling is needed for them. STAR is the exception — star_genome_generate requires plain-text FASTA/GTF — so main.nf runs the bgzip component conditionally (runIf checking the file's .gz suffix) right before the STAR genome index step, decompressing only when needed. Raw and trimmed FASTQ inputs need no such handling either: FastQC/TrimGalore read .gz directly, and the vendored star_align_reads component auto-detects a .gz/.bz2 suffix on its input and sets STAR's --readFilesCommand accordingly.
Contributions are welcome — please open an issue or pull request on GitHub.
Select version
Components
4
Released at
Sep 4, 2026 (1 hour ago)
Dependencies