# mosdepth: per-base depth for BAM and CRAM, fast enough for a 30x genome

> mosdepth is a Nim program that computes sequencing depth per base, per window or per region from a BAM or CRAM file, and it writes summary, distribution, quantized and threshold outputs. It suits a sequencing pipeline that needs coverage quickly and in a form other tools can read.

**brentp/mosdepth** — fast BAM/CRAM depth calculation for WGS, exome, or targeted sequencing

- Repository: https://github.com/brentp/mosdepth
- Stars: 879 · Forks: 108
- Language: Nim
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/brentp-mosdepth

## Depth calculation, and the build flag that matters

The scope is sequencing coverage, computed from a BAM or CRAM alignment file, for whole genome sequencing, exome or targeted sequencing. There is one instruction that sits above everything else in the readme, marked as a note, and it is the kind of thing that causes confusion when missed. mosdepth must be built with the --mm:refc flag for optimal performance and correct operation, and The program itself is written in Nim, and the source layout is small, with the main module, a depth statistics module, an integer to string helper, a functional test script, a tests directory and a scripts directory, plus a nimble file and a changelog. The installation is through Bioconda, which is the usual route for a bioinformatics tool on a cluster.

## What it can output, and why the formats matter

The output list is what makes the tool useful to a pipeline rather than merely fast. It can produce per-base depth at about twice the speed of the samtools depth command, with the note that this is about 25 minutes of CPU time for a 30 times genome. It gives mean per-window depth for a window size, which is what you would use for copy number variant calling, and the mean per region given a BED file. There is a mean or median per-region cumulative coverage histogram for a window size, and a distribution of the proportion of bases covered at or above a given threshold for each chromosome and genome-wide. The quantized output merges adjacent bases as long as they fall in the same coverage bin, so a run of bases at depth 10 to 20 becomes one region rather than a million rows. The threshold output says how many bases in each region are covered at the given thresholds. There is a summary of mean depths per chromosome and within specified regions per chromosome, and a d4 file, which the documentation describes as better than bigwig. When appropriate, the output files are bgzipped and indexed so they can be queried with tabix, and that indexing is what makes the outputs usable in a browser or with a region query rather than only as bulk files.

## The flags that change your numbers

The option list is where the real behaviour lives, and several of the defaults are opinionated. Reads with any of the bits in the exclusion flag set are dropped, and the default mask is 1796, which is worth understanding before you trust a number. The include flag lets you keep only reads with certain bits set. The fast mode does not look at internal cigar operations and does not correct mate overlaps, and the documentation calls it recommended for most use cases, which is a good default and a good thing to know you are using. The fragment mode counts the coverage of the full fragment including the full insert for proper pairs, and the minimum and maximum fragment length options filter on insert size. The mapping quality threshold drops reads below a value. The read group option restricts the calculation to a set of read group identifiers, which matters when you have a multi-sample alignment. The use-median option switches a region from the mean to the median. And the chromosome option restricts the calculation to one chromosome, which is the fastest way to get a quick answer while you work out the rest of the command.

## The three worked examples, and what they tell you about the tool

Three examples in the readme cover the common cases and each one teaches a different thing. The exome example computes coverage in each capture region from a BED file:

```bash
mosdepth --by capture.bed sample-output sample.exome.bam
```

The reported figure is that for a 5.5GB exome BAM and all 1,195,764 Ensembl exons as the regions it completes in 1 minute 38 seconds on a single CPU, with the per-base output going to one file, the mean for each region to another, both written with a CSI index for tabix queries, and the depth distribution to a text file. The whole genome example uses 500-base windows with the no-per-base and fast-mode flags, and the note on the no-per-base flag says it makes the tool faster because there is a cost to writing that much text, and the note on fast mode says it avoids the mate pair overlap and cigar calculations and lets the extraction library read less data from CRAM. The callable regions example is the most instructive, because it shows how the environment variables can label the quantized output the way a coverage calling tool expects, with a zero depth labelled as no coverage, depths of one to four as low coverage, five to 149 as callable and 150 upwards as high coverage.

## The environment variables that shape the output

The callable regions example relies on environment variables, and that is a second configuration surface beyond the command line flags. The variables named for the quantize levels set the label written into the fourth column of the output, one per boundary, and the result is a BED file where adjacent bases with depths in the same bin are merged into a single region carrying the label. There is a separate variable for the precision used in the distribution output, and setting it to 5 gives the distribution value with a reduced precision, without the depth file or the per-base output, using three threads. That is a useful combination for a quick look at a sample's coverage profile during a run, since the distribution is the one output that does not scale with genome size in the same way. The naming convention is consistent, with the tool name as the prefix, which is worth knowing because it means these variables can be set in a pipeline environment without colliding with anything else.

## Conclusion

mosdepth is a good fit for a sequencing pipeline that needs coverage over a whole genome or an exome and cannot afford to wait, since it is written for speed and outputs in formats a downstream tool can read directly. It is the wrong tool if you need anything beyond depth, because coverage is the only quantity it computes, and and the documentation notes that skipping the per-base output speeds execution substantially, suggesting the quantized or thresholded values where they suffice. It is also the wrong tool if you cannot build it with the right flag, because the note at the top of the readme says it must be built with the --mm:refc option for optimal performance and correct operation. Before adopting it, read the flag list rather than assuming defaults, since the read exclusion mask and the fast mode both change the numbers you get. The project is MIT licensed, the last push was on 2026-05-02, and the current release is v0.3.14 from 2026-04-24.

## FAQ

### What does mosdepth do and what input does it take?

It computes sequencing depth from a BAM or CRAM alignment file, for whole genome sequencing, exome or targeted sequencing. It can output per-base depth, mean depth per window for a given window size, mean depth per region from a BED file, a per-region cumulative coverage histogram, a distribution of the proportion of bases covered at or above a threshold, quantized output merging adjacent bases in the same bin, threshold counts, a per-chromosome summary and a d4 file.

### How does mosdepth need to be built?

The note at the top of the documentation says it must be built with the --mm:refc flag for optimal performance and correct operation, and the option list shows the fasta option used for CRAM files. Installation is through Bioconda, and the source is written in Nim.

### Which mosdepth flags change the coverage numbers?

Reads with any of the bits in the exclusion flag set are dropped, with a default mask of 1796, and the include flag keeps only reads with certain bits set. The fast mode skips internal cigar operations and mate overlap correction and is recommended for most use cases, the fragment mode counts the full insert for proper pairs, the minimum and maximum fragment length options filter on insert size, the mapping quality threshold drops reads below a value and the read group option restricts to specific read groups.

### How do I produce callable regions with mosdepth?

Set the environment variables that label the quantize levels, one per boundary, so that a depth of zero is labelled as no coverage, depths of one to four as low coverage, five to 149 as callable and 150 upwards as high coverage, then run with the no-per-base flag and the quantize option. The result is a BED file where adjacent bases in the same bin are merged into one region with the label in the fourth column.

### What is the fastest way to run mosdepth on a whole genome?

Use 500-base windows with the no-per-base flag and the fast mode, as in the whole genome example. The no-per-base flag avoids the cost of writing that much text, and the fast mode avoids the mate pair overlap and cigar calculations and lets the extraction library read less data from CRAM. Per-base output is roughly twice as fast as the samtools depth command, about 25 minutes of CPU for a 30 times genome.

## Sources

- [brentp/mosdepth on GitHub](https://github.com/brentp/mosdepth)
- [Issues](https://github.com/brentp/mosdepth/issues)
- [License: MIT](https://github.com/brentp/mosdepth/blob/master/LICENSE)
- [README](https://github.com/brentp/mosdepth/blob/master/README.md)
- [Releases](https://github.com/brentp/mosdepth/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/brentp-mosdepth
