Andrew Mercer
on this page

Matroska (MKV)

The Matroska Multimedia Container is a free, open-standard container format that can hold an unlimited number of video, audio, picture, or subtitle tracks in one file. It's a universal format for storing common multimedia content like movies or TV shows. It's similar in concept to AVI, MP4, or ASF, but the specification is entirely open and most implementations are open source.

The name comes from the Russian matryoshka nesting dolls — fitting, because a Matroska file is literally elements nested inside elements.

Container vs. codec

The single most common point of confusion: MKV is a container, not a codec.

Layer What it is Examples
Codec How the audio/video is compressed H.264, HEVC, AV1, VP9, AAC, FLAC, Opus, DTS, TrueHD
Container The box that holds encoded streams plus timing, metadata, chapters, etc. MKV, MP4, WebM, AVI, MOV

"Converting" an MP4 to MKV usually means remuxing: copying the exact same compressed streams into a different box. No quality loss, and it takes seconds instead of hours. Re-encoding is only needed if you want to change the codec, bitrate, or resolution.

File extensions

Extension Purpose
.mkv Video (plus any audio/subtitle tracks)
.mka Audio only
.mks Subtitles only
.mk3d Stereoscopic 3D video
.webm WebM — a restricted Matroska profile (VP8/VP9/AV1 video, Vorbis/Opus audio, WebVTT subs) designed for the web

They're all the same underlying format; the extension is a hint to software.

A bit of history

  • 2002 — Matroska started as a fork of an earlier project (MCF), led by Steve Lhomme.
  • 2010 — Google launched WebM, built on a subset of Matroska.
  • 2020 — The underlying binary format, EBML, was standardized as RFC 8794.
  • 2024 — Matroska itself was published as RFC 9559 by the IETF CELLAR working group.

So it's no longer just "the format pirates use" — it's a formal internet standard, and it's also used for archival work (e.g. FFV1 video in MKV is a common preservation format for libraries and archives).

Why MKV?

  • Holds basically anything. Nearly any video, audio, or subtitle codec, including lossless audio (FLAC, TrueHD, DTS-HD MA) and image-based subtitles (PGS from Blu-ray, VobSub from DVD).
  • Unlimited tracks. Multiple audio languages, commentary tracks, multiple subtitle languages, even multiple video angles.
  • Rich subtitles. Native support for styled ASS/SSA subtitles, with the fonts attached inside the file.
  • Chapters, including nested chapters, multiple editions, and ordered chapters.
  • Attachments — any file (cover art, fonts, an NFO, a PDF booklet).
  • Tags — arbitrary structured metadata.
  • Resilience. Data is stored in independent clusters, so a truncated or damaged file is still mostly playable. This is why screen-recording tools like OBS have long recommended recording to MKV: if the app crashes, you keep the recording.
  • Extensible. EBML means new elements can be added without breaking old parsers — unknown elements are simply skipped.

Comparison with other containers

MKV MP4 WebM AVI
Open spec Yes ISO (paywalled) Yes Mostly
Codec flexibility Nearly anything Limited set VP8/VP9/AV1 + Vorbis/Opus Old, hacks needed for modern codecs
Multiple audio/sub tracks Yes Yes (limited sub formats) Yes Awkward
ASS/SSA styled subs Yes No No No
Chapters Rich (editions, ordered) Basic Basic No
Attachments Yes No No No
Native in browsers / Apple devices Poor Excellent Good (browsers) Poor
Crash resilience Good Poor (unless fragmented) Good Moderate

The big downside of MKV is device compatibility: browsers, iOS/tvOS, and some smart TVs don't play it natively. Media servers like Jellyfin and Plex handle that by remuxing or transcoding on the fly.

How an MKV file is structured

Matroska is built on EBML (Extensible Binary Meta Language) — think "binary XML". Every element is an ID, a size, and data, and elements nest inside one another.

EBML Header            → "this is a Matroska file, version X"
Segment                → everything else lives in here
├── SeekHead           → index of where the top-level elements are
├── Info               → title, duration, muxing app, Segment UID
├── Tracks             → one TrackEntry per track (codec, language, flags, name…)
├── Chapters           → editions → chapter atoms
├── Attachments        → embedded files (fonts, cover art…)
├── Tags               → metadata
├── Cluster            → the actual audio/video data, in blocks with timestamps
├── Cluster
├── …
└── Cues               → seek index (keyframe positions) — makes seeking fast

You can see this tree for yourself with mkvinfo (below).

MKVToolNix

MKVToolNix by Moritz Bunkus is the toolset for working with Matroska. It never re-encodes anything — it only muxes, demuxes, and edits.

Tool Purpose
mkvmerge Create MKV files: remux, merge, append, split, add/remove tracks
mkvinfo Dump the element tree of a file
mkvextract Pull tracks, chapters, attachments, tags, etc. out of a file
mkvpropedit Edit headers in place without rewriting the file
mkvtoolnix-gui GUI for all of the above, with a job queue, header editor, and chapter editor

Installation

# Debian / Ubuntu
sudo apt install mkvtoolnix mkvtoolnix-gui

# Fedora / RHEL
sudo dnf install mkvtoolnix mkvtoolnix-gui

# Arch
sudo pacman -S mkvtoolnix-cli mkvtoolnix-gui

# macOS
brew install mkvtoolnix

# Flatpak (GUI)
flatpak install flathub org.bunkus.mkvtoolnix-gui

Distro packages can lag well behind; the project provides its own up-to-date repositories for most distros on the download page.

Tip: In mkvtoolnix-gui, set up a job the way you want, then use Multiplexer → Show command line. It's the fastest way to learn the mkvmerge syntax for something complicated.

The two rules of mkvmerge

  1. Options apply to the file that follows them. --language 1:eng movie.mkv sets the language of track 1 of movie.mkv. Put the option after the file and it applies to the next file (or errors).
  2. Track IDs are per-file and zero-based. Always check them first with mkvmerge -i.

Gotcha: mkvmerge and mkvextract use zero-based track IDs (0, 1, 2…). mkvpropedit uses one-based track numbers or type selectors (track:v1, track:a2, track:s1). mkvinfo shows both.

Inspecting files

# Quick summary: track IDs, types, codecs
mkvmerge -i movie.mkv

# Full details as JSON
mkvmerge -J movie.mkv

# Pretty track table with jq
mkvmerge -J movie.mkv | jq -r '.tracks[] |
  "\(.id)\t\(.type)\t\(.codec)\t\(.properties.language)\t\(.properties.track_name // "")"'

# The raw EBML element tree
mkvinfo movie.mkv

# Alternatives
mediainfo movie.mkv
ffprobe -hide_banner movie.mkv

Merging vs. appending (important!)

mkvmerge has two very different ways of combining files:

  • Merging (muxing): tracks from all inputs play at the same time — e.g. a video file plus an extra audio track plus a subtitle file.
  • Appending (concatenating): inputs play one after another — e.g. disc 1 then disc 2.

Appending requires a + between files (or [ ] around them). Without it, mkvmerge muxes the files in parallel, producing one file with two video tracks and two sets of audio all starting at 0:00.

Append two discs into one file

# Correct: disc 2 is appended after disc 1
mkvmerge -o Pulse.mkv --title 'Pink Floyd - Pulse' PULSE-D1.mkv + PULSE-D2.mkv

# Same thing, bracket syntax (handy with globs)
mkvmerge -o Pulse.mkv --title 'Pink Floyd - Pulse' '[' PULSE-D1.mkv PULSE-D2.mkv ']'

Existing chapters from each part are carried over and their timestamps shifted automatically. If the source files have no chapters, you can get a chapter at each join:

mkvmerge -o Pulse.mkv --title 'Pink Floyd - Pulse' \
  --generate-chapters when-appending \
  PULSE-D1.mkv + PULSE-D2.mkv

Appending only works if the tracks are compatible (same codec, and for video the same resolution and codec parameters; for audio the same sample rate and channel count). If they don't match you'll get an error and will have to re-encode one part first.

Mux extra tracks into a file

# Add an external subtitle and a commentary track
mkvmerge -o movie.new.mkv \
  movie.mkv \
  --language 0:eng --track-name 0:'Director Commentary' commentary.m4a \
  --language 0:eng --track-name 0:'English (SDH)' --forced-display-flag 0:no movie.en.srt

Remux from another container

mkvmerge -o movie.mkv movie.mp4
mkvmerge -o movie.mkv movie.avi
mkvmerge -o movie.mkv movie.ts

mkvmerge can even read Blu-ray playlists directly:

mkvmerge -o movie.mkv /mnt/bluray/BDMV/PLAYLIST/00800.mpls

(For ripping protected discs, MakeMKV is the usual tool — and as the name suggests, it outputs MKV.)

Selecting and removing tracks

-d, --video-tracks      keep only these video tracks
-a, --audio-tracks      keep only these audio tracks
-s, --subtitle-tracks   keep only these subtitle tracks
-D / -A / -S            drop all video / audio / subtitle tracks
-M                      drop all attachments
--no-chapters
--no-global-tags

Tracks can be selected by ID or by language, and prefixed with ! to invert:

# Keep audio tracks 1 and 3, and only English subtitles
mkvmerge -o out.mkv -a 1,3 -s eng in.mkv

# Keep English and Japanese audio, drop all subtitles
mkvmerge -o out.mkv -a eng,jpn -S in.mkv

# Keep everything except German audio
mkvmerge -o out.mkv -a '!ger' in.mkv

# Pull just the audio into an MKA
mkvmerge -o audio.mka -D -S in.mkv

Track properties: language, name, flags

mkvmerge -o out.mkv \
  --language 1:eng \
  --track-name 1:'English 5.1' \
  --default-track-flag 1:yes \
  --language 2:jpn \
  --default-track-flag 2:no \
  in.mkv

Useful flags:

Flag Meaning
--default-track-flag Player should pick this track by default
--forced-display-flag Subtitle should always display (e.g. for foreign-language dialogue in an English film)
--hearing-impaired-flag SDH / closed captions
--visual-impaired-flag Audio description
--commentary-flag Commentary track
--original-flag Original-language track

Language codes can be ISO 639-2 (eng, jpn) or full IETF BCP 47 tags (en-GB, pt-BR, zh-Hant).

Editing in place with mkvpropedit

mkvpropedit changes header data without rewriting the file — it's instant even on a 50 GB file. Use it for anything that doesn't add or remove streams.

# Set the title
mkvpropedit movie.mkv --edit info --set title='The Movie (1999)'

# Fix a language and set default flags
mkvpropedit movie.mkv \
  --edit track:a1 --set language=eng --set flag-default=1 \
  --edit track:a2 --set flag-default=0 \
  --edit track:s1 --set flag-forced=1 --set name='Forced'

# Remove the title entirely
mkvpropedit movie.mkv --edit info --delete title

# Replace or remove chapters
mkvpropedit movie.mkv --chapters chapters.xml
mkvpropedit movie.mkv --chapters ''

# Replace or remove all tags
mkvpropedit movie.mkv --tags all:tags.xml
mkvpropedit movie.mkv --tags all:

# Attachments
mkvpropedit movie.mkv --add-attachment cover.jpg
mkvpropedit movie.mkv --delete-attachment name:cover.jpg

# Refresh track statistics (bitrate, duration, frame count shown by players/mediainfo)
mkvpropedit movie.mkv --add-track-statistics-tags

Track selectors: track:v1 (first video), track:a2 (second audio), track:s1 (first subtitle), track:@3 (track number 3), track:3 (third track overall).

Extracting with mkvextract

# Tracks: mkvmerge IDs (zero-based) → output files
mkvextract movie.mkv tracks 1:audio.flac 2:subs.en.srt 3:subs.ja.ass

# Chapters as XML, or the simple OGM format
mkvextract movie.mkv chapters chapters.xml
mkvextract movie.mkv chapters -s chapters.txt

# Attachments (IDs from `mkvmerge -i`)
mkvextract movie.mkv attachments 1:cover.jpg 2:font.ttf

# Tags, timestamps, cue sheet
mkvextract movie.mkv tags tags.xml
mkvextract movie.mkv timestamps_v2 0:video-timestamps.txt
mkvextract album.mka cuesheet album.cue

# Multiple modes in one pass
mkvextract movie.mkv tracks 2:subs.srt chapters chapters.xml

Splitting

# By size
mkvmerge -o out.mkv --split size:4G in.mkv

# By duration
mkvmerge -o out.mkv --split duration:00:45:00 in.mkv

# One file per chapter, with a numbered filename
mkvmerge -o 'Pulse-%02d.mkv' --split chapters:all Pulse.mkv

# Only certain chapters
mkvmerge -o out.mkv --split chapters:5,9 in.mkv

# Cut out a single range (keep 00:05:00–00:10:00)
mkvmerge -o clip.mkv --split parts:00:05:00-00:10:00 in.mkv

# Keep two ranges and join them into one file ('+' means append to previous part)
mkvmerge -o highlights.mkv --split parts:00:01:00-00:02:30,+00:40:00-00:41:15 in.mkv

Splits on video happen at keyframes, so cut points can be a few seconds off from what you asked for. Frame-accurate cuts require re-encoding at least the edges.

Chapters

Simple (OGM) format

CHAPTER01=00:00:00.000
CHAPTER01NAME=Shine On You Crazy Diamond
CHAPTER02=00:13:32.000
CHAPTER02NAME=Learning to Fly
CHAPTER03=00:18:55.000
CHAPTER03NAME=High Hopes
mkvmerge -o out.mkv --chapters chapters.txt in.mkv
# or in place
mkvpropedit in.mkv --chapters chapters.txt

Auto-generated chapters

# A chapter every 10 minutes
mkvmerge -o out.mkv --generate-chapters interval:10m in.mkv

XML format

The XML format exposes everything: nested chapters, multiple languages, editions, and ordered chapters. mkvmerge fills in missing UIDs for you.

<?xml version="1.0"?>
<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">
<Chapters>
  <EditionEntry>
    <ChapterAtom>
      <ChapterTimeStart>00:00:00.000000000</ChapterTimeStart>
      <ChapterDisplay>
        <ChapterString>Opening</ChapterString>
        <ChapterLanguage>eng</ChapterLanguage>
      </ChapterDisplay>
      <ChapterDisplay>
        <ChapterString>Ouverture</ChapterString>
        <ChapterLanguage>fre</ChapterLanguage>
      </ChapterDisplay>
    </ChapterAtom>
  </EditionEntry>
</Chapters>

Tags (metadata)

<?xml version="1.0"?>
<!DOCTYPE Tags SYSTEM "matroskatags.dtd">
<Tags>
  <Tag>
    <Targets>
      <TargetTypeValue>50</TargetTypeValue>
    </Targets>
    <Simple><Name>ARTIST</Name><String>Pink Floyd</String></Simple>
    <Simple><Name>DATE_RELEASED</Name><String>1995</String></Simple>
    <Simple><Name>COMMENT</Name><String>Live at Earls Court, 1994</String></Simple>
  </Tag>
</Tags>
mkvpropedit Pulse.mkv --tags global:tags.xml

TargetTypeValue sets the scope: 50 is the whole movie/album, 30 a track/song/chapter, 70 a collection. See the official tag list.

Cool and interesting things

1. Multiple editions in one file (theatrical vs. director's cut)

Matroska chapters live inside editions, and a file can have several. Combined with ordered chapters, one file can play as different cuts of the same film without duplicating any video.

Ordered chapters tell the player "play these time ranges, in this order, and skip anything not listed." Store the director's cut once, then define a theatrical edition that skips the extra scenes:

<?xml version="1.0"?>
<!DOCTYPE Chapters SYSTEM "matroskachapters.dtd">
<Chapters>
  <!-- Edition 1: Director's Cut (plays everything) -->
  <EditionEntry>
    <EditionFlagDefault>1</EditionFlagDefault>
    <EditionFlagOrdered>1</EditionFlagOrdered>
    <ChapterAtom>
      <ChapterTimeStart>00:00:00.000000000</ChapterTimeStart>
      <ChapterTimeEnd>01:30:00.000000000</ChapterTimeEnd>
      <ChapterDisplay><ChapterString>Act I</ChapterString></ChapterDisplay>
    </ChapterAtom>
    <ChapterAtom>
      <ChapterTimeStart>01:30:00.000000000</ChapterTimeStart>
      <ChapterTimeEnd>01:42:00.000000000</ChapterTimeEnd>
      <ChapterDisplay><ChapterString>Extended Scene</ChapterString></ChapterDisplay>
    </ChapterAtom>
    <ChapterAtom>
      <ChapterTimeStart>01:42:00.000000000</ChapterTimeStart>
      <ChapterTimeEnd>02:30:00.000000000</ChapterTimeEnd>
      <ChapterDisplay><ChapterString>Act II</ChapterString></ChapterDisplay>
    </ChapterAtom>
  </EditionEntry>

  <!-- Edition 2: Theatrical (skips the extended scene) -->
  <EditionEntry>
    <EditionFlagOrdered>1</EditionFlagOrdered>
    <ChapterAtom>
      <ChapterTimeStart>00:00:00.000000000</ChapterTimeStart>
      <ChapterTimeEnd>01:30:00.000000000</ChapterTimeEnd>
      <ChapterDisplay><ChapterString>Act I</ChapterString></ChapterDisplay>
    </ChapterAtom>
    <ChapterAtom>
      <ChapterTimeStart>01:42:00.000000000</ChapterTimeStart>
      <ChapterTimeEnd>02:30:00.000000000</ChapterTimeEnd>
      <ChapterDisplay><ChapterString>Act II</ChapterString></ChapterDisplay>
    </ChapterAtom>
  </EditionEntry>
</Chapters>

In mpv, switch editions with E (or --edition=1). Player support for ordered chapters and editions is good in mpv and MPC-HC, and spotty elsewhere — test before relying on it.

2. Segment linking (shared intros across episodes)

Ordered chapters can also reference other files by their Segment UID. The anime fansub scene used this heavily: the opening and ending songs live in their own small files, and each episode's chapters say "play the OP from op.mkv, then this episode, then the ED from ed.mkv." Every episode shrinks by the size of the OP/ED. It's niche and player support is limited, but it's a clever demonstration of what the format can do.

3. Styled subtitles with embedded fonts

ASS/SSA subtitles support positioning, colours, karaoke effects, and custom fonts. Attach the fonts so the file renders correctly anywhere:

mkvmerge -o out.mkv in.mkv subs.ass \
  --attachment-mime-type font/ttf --attach-file MyFont.ttf \
  --attachment-mime-type font/otf --attach-file OtherFont.otf

Players like mpv and VLC load attached fonts automatically when rendering the subtitles.

4. Embedded cover art

An attachment named cover.jpg (or cover.png) is picked up as artwork by many players and file managers, and media servers can use embedded images too:

mkvpropedit movie.mkv \
  --attachment-name cover.jpg --attachment-mime-type image/jpeg --add-attachment poster.jpg

There's also a convention for small_cover.jpg, cover_land.jpg (landscape), and small_cover_land.jpg.

5. An entire album as one audio file, with a chapter per song

An .mka with chapters is a modern replacement for "one big FLAC + cue sheet." Append the tracks and let mkvmerge make a chapter at each join, named after each source file:

mkvmerge -o 'Pink Floyd - Pulse.mka' \
  --title 'Pink Floyd - Pulse' \
  --generate-chapters when-appending \
  --generate-chapters-name-template '<FILE_NAME>' \
  '[' *.flac ']'

Players with chapter support (mpv, foobar2000, VLC) let you skip between songs, while the file stays gapless. All inputs need matching sample rate and channel count.

6. Bundle everything in one file

Because any file can be attached, a single MKV can be a complete archive of a release: the film, lossless audio, commentary, every subtitle language, chapters, cover art, and the booklet PDF.

mkvmerge -o 'Concert (1994).mkv' concert.mkv \
  --attachment-mime-type application/pdf --attachment-description 'Booklet' --attach-file booklet.pdf \
  --attachment-mime-type text/plain --attach-file notes.nfo

7. Fix audio sync without re-encoding

# Delay audio track 1 by 250 ms (negative values advance it)
mkvmerge -o fixed.mkv --sync 1:250 in.mkv

8. Change the frame rate without re-encoding

Video encoded at the wrong frame rate (or a source with bogus timing) can be retimed by rewriting the timestamps only:

mkvmerge -o out.mkv --default-duration 0:24000/1001p in.mkv

Note that the audio won't be stretched to match — this is for fixing metadata, not for PAL speed-up conversions. For variable-frame-rate content you can supply exact per-frame timestamps with --timestamps 0:timestamps.txt (the format mkvextract timestamps_v2 produces).

9. Crash-proof recordings

Since a Matroska file is a sequence of independent clusters, recording straight to MKV means a crash or power loss leaves you with everything up to the last cluster. MP4 (non-fragmented) writes its index at the end, so a crash can make the whole file unreadable. Record to MKV, remux to MP4 afterwards if you need it:

ffmpeg -i recording.mkv -c copy -movflags +faststart recording.mp4

10. 3D video

.mk3d files use the StereoMode track property to describe side-by-side, top-bottom, or other stereoscopic layouts, which players can use to render or convert the video:

mkvmerge -o movie.mk3d --stereo-mode 0:side_by_side_left_first movie-sbs.mkv

Batch operations

Inventory a whole library

for f in **/*.mkv; do
  mkvmerge -J "$f" | jq -r --arg f "$f" '.tracks[] |
    [$f, .type, .codec, .properties.language] | @tsv'
done > library-tracks.tsv

(Needs shopt -s globstar in bash.)

Strip foreign audio and subtitles

mkdir -p clean
for f in *.mkv; do
  mkvmerge -o "clean/$f" -a eng -s eng "$f"
done

If a file has no track matching your selection, the output simply won't have that type of track — check the results before deleting originals.

Fix flags across a season (in place, instant)

for f in *.mkv; do
  mkvpropedit "$f" \
    --edit track:a1 --set flag-default=1 \
    --edit track:s1 --set flag-default=0
done

Option files

Long mkvmerge invocations can live in a JSON file — one array element per argument:

[
  "-o", "Pulse.mkv",
  "--title", "Pink Floyd - Pulse",
  "PULSE-D1.mkv", "+", "PULSE-D2.mkv"
]
mkvmerge @pulse.json

Exit codes

mkvmerge returns 0 on success, 1 if it finished with warnings, and 2 on errors. In scripts, don't treat 1 as failure blindly — the output file was still written.

ffmpeg equivalents

MKVToolNix is the better tool for Matroska-specific features, but ffmpeg works fine for everyday remuxing.

# Remux to MKV, keeping ALL streams (without -map 0, ffmpeg keeps only one per type)
ffmpeg -i in.mp4 -map 0 -c copy out.mkv

# Add a subtitle file
ffmpeg -i in.mkv -i subs.srt -map 0 -map 1 -c copy -metadata:s:s:0 language=eng out.mkv

# Concatenate (concat demuxer)
printf "file '%s'\n" PULSE-D1.mkv PULSE-D2.mkv > list.txt
ffmpeg -f concat -safe 0 -i list.txt -map 0 -c copy Pulse.mkv

# Re-encode to AV1 + Opus in MKV
ffmpeg -i in.mkv -map 0 -c:v libsvtav1 -crf 30 -c:a libopus -b:a 128k -c:s copy out.mkv

Playback and compatibility

Software Notes
mpv Best-in-class Matroska support, including editions, ordered chapters, and segment linking
VLC Very good general support
MPC-HC / MPC-BE Excellent on Windows, including ordered chapters
Kodi Very good
Jellyfin / Plex / Emby Index MKV natively; remux or transcode for clients that can't play it
Web browsers Not officially supported (WebM is the browser-friendly subset)
Apple (QuickTime, iOS, tvOS) No native support; use VLC/Infuse or remux to MP4
Smart TVs Varies by model; subtitle formats (especially PGS/ASS) are the usual failure point

Troubleshooting

Appending fails with "track parameters do not match". The parts differ in codec settings (resolution, profile, sample rate, channels). Re-encode the odd one out to match, or check with mkvmerge -J which parameter differs.

Seeking is slow or broken. The file is probably missing Cues (common with files from crashed recordings). A plain remux rebuilds them: mkvmerge -o fixed.mkv broken.mkv.

Wrong track plays by default. Check flag-default and flag-forced with mkvinfo and fix them with mkvpropedit. Players also apply their own language preferences on top.

Subtitles look wrong / wrong font. The ASS file references a font that isn't attached or installed. Attach it (see above).

Merged file has two videos playing at once. You muxed instead of appended — add + between the files.

A/V drift after appending. The parts have slightly different durations or start offsets. Try --append-mode track (the default is file), or remux each part first.

Resources