Fonts and fontconfig on Linux
Font rendering on Linux goes through fontconfig, which decides which font file satisfies a request, and FreeType, which turns outlines into pixels.
Most font problems are fontconfig problems.
Installing
mkdir -p ~/.local/share/fonts
cp MyFont-*.ttf ~/.local/share/fonts/
fc-cache -f
System-wide:
sudo cp MyFont-*.ttf /usr/local/share/fonts/
sudo fc-cache -f
Use /usr/local/share/fonts rather than /usr/share/fonts, which belongs to the package manager and gets overwritten. Our usr directory guide covers why /usr/local exists.
~/.fonts still works on most systems and is deprecated. Do not use it for anything new.
Most fonts are packaged:
sudo apt install fonts-firacode fonts-noto fonts-noto-color-emoji
sudo dnf install fira-code-fonts google-noto-sans-fonts
Inspecting
fc-list | wc -l # how many
fc-list : family | sort -u # families only
fc-list | grep -i fira # find one
fc-match "Helvetica" # what does this resolve to
fc-match --sort "monospace" | head # full preference order
fc-match is the diagnostic command. Ask for a font you do not have:
fc-match "Helvetica"
# DejaVuSans.ttf: "DejaVu Sans" "Book"
fontconfig substituted DejaVu Sans. That substitution is why a document specifying Helvetica renders at all, and also why it renders differently from what the author saw.
Configuration
Files live in ~/.config/fontconfig/fonts.conf or /etc/fonts/local.conf, in XML.
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<!-- defaults for the three generic families -->
<alias>
<family>sans-serif</family>
<prefer><family>Inter</family></prefer>
</alias>
<alias>
<family>serif</family>
<prefer><family>Source Serif 4</family></prefer>
</alias>
<alias>
<family>monospace</family>
<prefer><family>JetBrains Mono</family></prefer>
</alias>
</fontconfig>
sans-serif, serif, and monospace are what most applications actually request, so setting these three covers a great deal.
fc-cache -f
fc-match sans-serif # confirm
Replace a specific font everywhere:
<match target="pattern">
<test name="family"><string>Arial</string></test>
<edit name="family" mode="assign" binding="same">
<string>Liberation Sans</string>
</edit>
</match>
Liberation fonts are metric-compatible with Arial, Times New Roman, and Courier New, meaning identical character widths. A document laid out in Arial keeps its line breaks, which is why they are the right substitution for office documents.
Missing glyphs
Boxes or question marks mean no installed font has that character.
sudo apt install fonts-noto fonts-noto-cjk fonts-noto-color-emoji
Noto stands for “no tofu”, tofu being the name for those empty boxes. Google built it to cover every Unicode script, and installing the full family solves essentially all missing-glyph problems at the cost of considerable disk space.
Emoji need their own configuration to be picked up as a fallback:
<match target="pattern">
<test name="family"><string>sans-serif</string></test>
<edit name="family" mode="append" binding="strong">
<string>Noto Color Emoji</string>
</edit>
</match>
Appending rather than preferring means emoji are used only for characters the primary font lacks.
fc-list :charset=1F600 # which fonts have a grinning face
Rendering settings
<match target="font">
<edit name="antialias" mode="assign"><bool>true</bool></edit>
<edit name="hinting" mode="assign"><bool>true</bool></edit>
<edit name="hintstyle" mode="assign"><const>hintslight</const></edit>
<edit name="rgba" mode="assign"><const>rgb</const></edit>
<edit name="lcdfilter" mode="assign"><const>lcddefault</const></edit>
</match>
Hinting snaps glyph outlines to the pixel grid. hintslight preserves the font’s intended shapes while improving sharpness and is the right default for most displays. hintfull is sharper and distorts shapes noticeably.
Subpixel rendering (rgba) uses the individual red, green, and blue elements of an LCD to triple horizontal resolution. Set it to match your panel’s subpixel layout, which is rgb for most and bgr for some. Set it to none on an OLED or a rotated display, where it produces colour fringing.
On HiDPI displays none of this matters much, because there are enough pixels that the adjustments are unnecessary. Our HiDPI guide covers scaling.
Terminal fonts and ligatures
Programming fonts with ligatures render != as a single glyph and => as an arrow.
Popular choices: JetBrains Mono, Fira Code, Cascadia Code, Iosevka, Victor Mono.
The terminal has to support them. Kitty, Alacritty, WezTerm, and Konsole do. GNOME Terminal’s support has been inconsistent across versions, so a font with ligatures showing none is frequently the terminal rather than the font.
Nerd Fonts are patched versions carrying thousands of icon glyphs, which is what status bars, eza, starship, and similar tools use for their symbols.
mkdir -p ~/.local/share/fonts
cd ~/.local/share/fonts
# download a Nerd Font release archive, extract, then
fc-cache -f
If your prompt or file listing shows boxes where icons should be, you need a Nerd Font and your terminal configured to use it. Our prompt customization guide covers the prompt side.
Applications that ignore all this
Toolkits read their own settings on top of fontconfig.
gsettings set org.gnome.desktop.interface font-name 'Inter 11'
gsettings set org.gnome.desktop.interface monospace-font-name 'JetBrains Mono 11'
Qt reads ~/.config/qt5ct/qt5ct.conf or the Plasma settings.
Java applications historically ignore fontconfig entirely:
export JAVA_FONTS=/usr/share/fonts
export _JAVA_OPTIONS='-Dawt.useSystemAAFontSettings=on -Dswing.aatext=true'
Electron applications use their bundled Chromium’s font handling, which reads fontconfig for family resolution and applies its own rendering.
The general rule: a fontconfig rule reaches more applications than a toolkit setting, so put substitutions there and use toolkit settings for the default UI font.
Troubleshooting
fc-cache -rv # rebuild everything, verbose
fc-match --sort "sans-serif" # what is being preferred and why
fc-list :lang=ja # do I have Japanese coverage
A newly installed font is not found. Run fc-cache -f. Applications already running need restarting.
A font looks different from the same font elsewhere. Check hinting and subpixel settings, and check whether you actually got the font or a substitute with fc-match.
Bold or italic is synthesised badly. The family has only a regular weight and the renderer is faking the rest. Install the complete family.
Variable fonts partly working. Support depends on the FreeType version and the application. LibreOffice 26.8 added it recently, which is an indication of how uneven support still is.
Frequently Asked Questions
Where do I install fonts on Linux?
Put them in ~/.local/share/fonts for your user or /usr/local/share/fonts system-wide, then run fc-cache -f to rebuild the cache. The older ~/.fonts location still works on most systems but is deprecated and should not be used for new installations.
Why do some characters show as boxes or question marks?
No installed font contains a glyph for that character. Install a font covering the script in question, such as Noto Sans CJK for Chinese, Japanese, and Korean, or Noto Color Emoji for emoji. The Noto family exists specifically to cover every Unicode script.
How do I find which font is actually being used?
Run fc-match followed by the font name or pattern, which shows what fontconfig resolves it to. Adding the —sort flag lists every candidate in preference order, which is how you diagnose an unexpected substitution.
Why does my terminal not show programming ligatures?
Either the font has no ligatures, or the terminal does not support them. Fonts such as Fira Code and JetBrains Mono include ligatures, and terminals including Kitty, Alacritty, and WezTerm render them while several others, notably GNOME Terminal in some versions, do not.
What is font hinting and which setting should I use?
Hinting adjusts glyph outlines to align with the pixel grid for sharper rendering at small sizes. Slight hinting with subpixel rendering suits most modern displays, and on a HiDPI screen hinting matters much less because there are enough pixels that the adjustment is unnecessary.
Why does one application use a different font from everything else?
Applications using different toolkits read different configuration. GTK and Qt each have their own font settings, and Java and Electron applications frequently ignore both. fontconfig underlies all of them, so a rule placed there applies more broadly than a toolkit setting.