What really decides how smoothly a LaTeX installation ages is not memorising tlmgr’s option list but knowing where to put a file. Print the TEXMF search path and you will see a !! marker in front of some trees and not others. It means “do not scan the disk; consult only the ls-R database,” and those two characters decide whether a file you drop in by hand is found immediately or stays invisible until you run mktexlsr. This page covers adding and updating packages with tlmgr, when to use the personal tree TEXMFHOME versus the system tree TEXMFLOCAL, how to install from CTAN by hand, and how font maps (updmap) are managed — with the values measured on a real TeX Live 2024 install.
Adding a package: how tlmgr install works
The standard way to add a LaTeX package is a single line, tlmgr install <name>, and its dependencies come along by default. You can name packages one at a time or pull in a whole collection such as collection-fontsrecommended. Installing into a system-wide tree requires administrator rights (sudo tlmgr install … on Unix, an elevated shell on Windows). Add --no-depends when you deliberately do not want the dependencies, and --reinstall when you want to overwrite files that have gone wrong.
# Install a package; dependencies come along by default
tlmgr install siunitx
tlmgr install collection-fontsrecommended
# Skip dependencies, or overwrite a broken install
tlmgr install --no-depends siunitx
tlmgr install --reinstall siunitx
# A system-wide tree needs admin rights
sudo tlmgr install siunitxWhat matters more in practice is the route from an error message to a package name. When you hit something like LaTeX Error: File siunitx.sty not found, dropping a .sty you found in a search engine into the manuscript folder is the worst possible move. Separate the file name from the package name. The file is siunitx.sty, the package is siunitx — usually the same, but often not: tikz.sty lives in the pgf package. tlmgr search --global --file answers “which package ships this file,” and that is the bridge. If you only half-remember a name, tlmgr search --global <word>; if you want to know what a package is, tlmgr info <name> prints its description, license, size and file list.
# From "File siunitx.sty not found" to an installed package
kpsewhich siunitx.sty # is it already here?
tlmgr search --global --file siunitx.sty # which package ships it?
tlmgr install siunitx # install it properly
# Half-remembered name, or curious about a package
tlmgr search --global siunit
tlmgr info siunitxOne behaviour that is easy to miss: neither tlmgr install nor tlmgr remove refreshes the symlinks in system directories such as /usr/local/bin. If your PATH points straight at TeX Live’s own bin directory this never matters, but if you use tlmgr path add to wire up links for executables, man pages and info pages, you must rerun tlmgr path add yourself after installing a package that ships a new executable (and tlmgr path remove after removing one). If the command line is not your preference, TeX Live also ships tlshell, a Tcl/Tk GUI that searches, installs, removes, bulk-updates and switches repositories from a window — and shows the underlying command log, which makes it a gentle way to learn tlmgr.
Updating: --self and --all go together
The standard move is one line, tlmgr update --self --all: --self brings tlmgr itself — the infrastructure — up to date, and --all brings everything else. The order is not a matter of taste. The tlmgr manual says of --all that it updates every installed package except tlmgr itself, and that if updates to tlmgr are present, this gives an error unless --force or --self is also given. An out-of-date tlmgr stops and insists on replacing itself first. Give it both flags and it does exactly that: it updates itself, and on success restarts under the new version to finish the rest.
# The standard move: infrastructure first, then everything else
tlmgr update --self --all
# See what would change before committing to it
tlmgr update --list
# Update one package only
tlmgr update siunitxIt helps to know what else that update does. Packages dropped from a collection on the server are auto-removed, and packages newly added to a collection you have are auto-installed; --list shows both before you commit. Where it fetches from is the repository setting, by default CTAN’s automatic mirror redirection. If the connection is slow or you want to pin one mirror, switch permanently with tlmgr option repository; to use a different mirror for one run only, add --repository <url>. Defaults such as paper size also live in tlmgr, so tlmgr paper a4 sets A4 for every tool at once (or per program, tlmgr dvips paper a4). tlmgr option show prints the current settings together.
# Where tlmgr fetches from, and other stored defaults
tlmgr option repository ctan # automatic CTAN mirror redirection
tlmgr option repository https://example.org/CTAN/systems/texlive/tlnet
tlmgr option show
# Use a different mirror for one run only
tlmgr install --repository https://example.org/CTAN/systems/texlive/tlnet siunitx
# Paper size defaults are stored here too
tlmgr paper a4
tlmgr dvips paper a4TEXMFHOME versus TEXMFLOCAL
Files that tlmgr does not manage — a conference class file, a style you wrote, a package you downloaded from CTAN by hand — go in one of two places: TEXMFHOME for you alone, TEXMFLOCAL for the whole machine. Both are searched before the distribution’s own tree (TEXMFDIST), so a same-named file there wins. The actual locations vary, so print them rather than assume. On this Mac, kpsewhich -var-value=TEXMFHOME returns /Users/wedd/Library/texmf — not ~/texmf. On Linux the default is ~/texmf, which is why following Linux instructions verbatim on a Mac leaves your file permanently invisible. TEXMFLOCAL here is /usr/local/texlive/texmf-local, and the striking thing is that it sits outside the year directory — so whatever you put there survives the upgrade to next year’s release.
# Never guess these paths - print them
kpsewhich -var-value=TEXMFHOME # your own tree (differs on macOS and Linux)
kpsewhich -var-value=TEXMFLOCAL # machine-wide tree, outside the year directory
kpsewhich -var-value=TEXMFDIST # the distribution itself - do not edit by hand
# The whole search order, including the !! markers
kpsewhich -expand-path='$TEXMF'Choosing between them is simple: files for your own manuscripts go in TEXMFHOME; things everyone on a shared machine should have go in TEXMFLOCAL. TEXMFHOME needs no administrator rights and travels with your home directory across a reinstall. Inside, follow the same layout the distribution uses (the TDS): a LaTeX style goes under tex/latex/. The full picture of that layout belongs to “TeX directory structure & paths”; in practice the hierarchy in the example below covers most of what you need. One warning: do not leave an old copy of a .sty in the manuscript folder. TeX searches the current directory first, so the stale copy keeps winning even after tlmgr updates the real one — a failure that is very hard to diagnose.
# A personal class file, in the tree that belongs to you
HOME_TREE="$(kpsewhich -var-value=TEXMFHOME)"
mkdir -p "$HOME_TREE/tex/latex/local"
cp mythesis.cls "$HOME_TREE/tex/latex/local/"
kpsewhich mythesis.cls # should print the path immediately
# Let tlmgr manage that tree too, with no root required
tlmgr init-usertree
tlmgr --usermode install siunitxWhen mktexlsr and texhash are actually needed
The answer is clean: you need it when you have placed a file by hand in TEXMFLOCAL or a system tree, and you do not when you placed it in TEXMFHOME. The reason is the !! from the opening. Expand TEXMF on this machine and you find !! in front of /usr/local/texlive/texmf-local and texmf-dist, but not in front of ~/Library/texmf. !! tells kpathsea “for this tree, do not look at the disk; trust only the ls-R database,” so until ls-R is refreshed, a new file may as well not exist. The corroboration is that TEXMFDBS, the list of trees that carry an ls-R, does not include TEXMFHOME at all. And the index earns its keep: texmf-dist/ls-R on this machine is 276,953 lines long, which is how TeX avoids walking more than 220,000 files on every run.
The command that rebuilds the index is mktexlsr, which regenerates ls-R for every tree listed in TEXMFDBS. texhash is not a different command — checked on this machine, texhash is a symlink to mktexlsr, so it is literally the same program under two names. Either will do. Because it writes into system trees, it needs sudo. Note also that tlmgr and getnonfreefonts take care of the refresh themselves, so in practice the only time you type it is after copying files in by hand.
# The same file in the system tree DOES need the index rebuilt
LOCAL_TREE="$(kpsewhich -var-value=TEXMFLOCAL)"
sudo mkdir -p "$LOCAL_TREE/tex/latex/local"
sudo cp mythesis.cls "$LOCAL_TREE/tex/latex/local/"
sudo mktexlsr # texhash is a symlink to this same program
kpsewhich mythesis.cls # now it resolves
kpsewhich -all mythesis.cls # and shows every copy, if several collideInstalling from CTAN by hand: .dtx and .ins
You only do this by hand for things tlmgr does not have: a class file a journal distributes itself, a package published last week, a version that exists only on the author’s site. Some CTAN packages ship a plain .sty, but many arrive as a pair — .dtx, which holds the source and its documentation together, and .ins, the instruction file that extracts it. Run tex foo.ins and it generates foo.sty out of the .dtx; then copy that into the right place under TEXMFHOME. The .dtx format is literate programming in its purest form: run pdflatex foo.dtx and you get a PDF manual with the annotated source inside it.
# A package that ships as .dtx + .ins: extract, then place
tex foo.ins # writes foo.sty (and foo.cls, if any)
pdflatex foo.dtx # optional: build the annotated manual
HOME_TREE="$(kpsewhich -var-value=TEXMFHOME)"
mkdir -p "$HOME_TREE/tex/latex/foo"
cp foo.sty "$HOME_TREE/tex/latex/foo/"
kpsewhich foo.styOn MiKTeX: mpm and automatic installation
MiKTeX has no tlmgr. Its command line is mpm (the MiKTeX Package Manager) and its GUI is the MiKTeX Console, and both updates and package additions go through those. The headline difference is on-the-fly installation: a package the document asks for but you do not have is downloaded mid-run and the compile continues (the Console lets you set always install, ask each time, or never). That automation comes with a condition, though: even when MiKTeX is installed for all users, packages fetched on the fly land in the running user’s own AppData\Roaming tree. On a shared machine, install what you need up front in administrator mode instead.
Managing font maps: updmap and getnonfreefonts
A font map is a line-by-line table saying “the font TeX calls by this name is really that file, and this is how to embed it in the PDF.” There is pdftex.map for pdftex and dvipdfmx, psfonts.map for dvips, kanjix.map for Japanese, and others. You never write them by hand — updmap generates them. For a sense of scale, count the lines: pdftex.map on this machine is 45,443 lines long. Installing a font package and letting updmap run is what updates this table, and only then can the font be embedded in a PDF.
updmap has two faces — updmap-sys for the whole system and updmap-user for you alone — and here lies the nastiest trap in TeX Live’s configuration. updmap --help warns about it itself: once updmap-user has been run, even a single time, running updmap-sys no longer has any effect. A personal configuration file gets created and shadows the system one from then on. updmap-sys does print a warning when this happens, but if you do not know the cause you can spend a very long time wondering why running it as administrator changes nothing. The safe rule is use updmap-sys only and never touch updmap-user.
Embedding Japanese, Chinese and Korean fonts has its own front door: kanji-config-updmap-sys. It calls updmap underneath and switches which CJK font family gets embedded in your PDFs. Pass it status and it reports the current state — on this machine the current Japanese family is haranoaji, with haranoaji, ipa and ipaex offered as the families it can switch to. In other words, the Japanese fonts that ship inside TeX Live are Harano Aji and IPA / IPAex, and Noto CJK and Source Han are not bundled. To use those you point at the fonts installed on your operating system, or install them separately.
# Ask, without changing anything: which CJK family is embedded?
kanji-config-updmap-sys status
# Switch the embedded Japanese family, machine-wide
sudo kanji-config-updmap-sys haranoaji
# Rebuild the map files after a manual font install
sudo updmap-sys # never updmap-user: it permanently shadows updmap-sysFinally, the fonts that cannot ride along in the distribution. A font licensed “free to use but not to sell” cannot be bundled into TeX Live, which is also distributed on a DVD that is sold. TUG’s getnonfreefonts script exists to fetch exactly these: it pulls the font and its TeX support files from CTAN and configures everything so TeX can find them. The key is --user (into TEXMFHOME) versus --sys (into the system tree). The separate getnonfreefonts-sys command you will see in older write-ups no longer exists — for everyone on the machine, run getnonfreefonts --sys … with administrator rights. --lsfonts lists what is on offer.
# See what is on offer, then install into your own tree
getnonfreefonts --user --lsfonts
getnonfreefonts --user luximono
# Or system-wide, for everyone on the machine
sudo getnonfreefonts --sys luximono- Install general-purpose packages with
tlmgr. A stale copy left in the manuscript folder keeps winning after an update and produces failures nobody can explain. - Files that belong only to a conference template may live in the manuscript repository, because they are part of that submission bundle rather than of the distribution.
- On shared machines and in CI, record the TeX Live year, the extra package names, and any pinned repository or Docker tag in the README or
.latexmkrc. “I installed it once on my laptop” is not reproducibility. - Do not run
tlmgr update --self --allright before a submission. You would be building the PDF with different packages from yesterday’s. Try updates on another day. - Never use
updmap-user. Running it even once stopsupdmap-sysfrom having any effect, and the cause is extremely hard to spot afterwards.