No module named 'tkinter'" and "headless" Backend ErrorsCode that plots fine on a laptop can break the moment it runs inside a Docker container, a CI job, or an SSH session on a headless box. The exact wording of the failure isn't consistent either — it depends on what happens to be installed on that particular machine. Here's both variants reproduced (Matplotlib 3.11.1, Python 3.11 and 3.12), why the wording differs, and what actually fixes it.
tkinter is a separate OS-level package with a C extension underneath it, not something pip install matplotlib pulls in. Slim Docker base images routinely skip it. On a container built that way, this is what a stray matplotlib.use("TkAgg") — leftover from a tutorial, or an old notebook cell someone copied into a script — does:
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 2, 3])
plt.show()
# Traceback (most recent call last):
# ...
# File ".../matplotlib/backends/backend_tkagg.py", line 1, in <module>
# from . import _backend_tk
# File ".../matplotlib/backends/_backend_tk.py", line 9, in <module>
# import tkinter as tk
# ModuleNotFoundError: No module named 'tkinter'
Reproduced this exact way on a bare container with no tkinter anywhere on disk. The failure happens the instant matplotlib.use("TkAgg") forces the backend module to actually load — plt.show() never gets reached.
Adding the missing package (apt-get install python3-tk) doesn't make a headless machine stop being headless — it just moves the failure somewhere more specific. Same code, run afterward on Python 3.12 with python3-tk present but still no display attached:
import matplotlib
matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 2, 3])
plt.show()
# Traceback (most recent call last):
# ...
# File ".../matplotlib/pyplot.py", line 453, in switch_backend
# raise ImportError(
# ImportError: Cannot load backend 'TkAgg' which requires the 'tk' interactive
# framework, as 'headless' is currently running
Current Matplotlib checks for a headless environment up front and names the problem directly, instead of letting the GUI toolkit underneath fail on its own terms. Older releases instead let Tk raise _tkinter.TclError: no display name and no $DISPLAY environment variable at the same spot — worth knowing if a Stack Overflow answer someone's following mentions that message and it doesn't match what's on screen now.
So two people staring at what looks like the same bug, one seeing ModuleNotFoundError and the other an ImportError about a headless framework, usually just have different things installed — not two different bugs.
Neither error shows up unless something explicitly demands an interactive backend. Left alone, Matplotlib figures out on its own that there's no display and quietly picks a backend that doesn't need one:
import matplotlib
print(matplotlib.get_backend()) # 'agg', with no display and nothing forced
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 2, 3])
plt.savefig("chart.png") # writes the file, no complaints
plt.show() # a no-op under Agg
Confirmed on the same tkinter-less container from the first example above — no install, no config, nothing extra needed. The two errors earlier only happen because something overrides that default: an explicit matplotlib.use("TkAgg") somewhere in the code, or an MPLBACKEND environment variable set globally on a shared machine.
Server-side and CI plotting almost never needs an on-screen window — it needs a PNG on disk. So stop asking for an interactive backend:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
plt.plot([1, 2, 3], [1, 2, 3])
plt.savefig("chart.png")
ModuleNotFoundError above. If nothing in the codebase actually needs TkAgg, deleting the stray matplotlib.use(...) line entirely is usually enough — automatic detection already lands on Agg without it, per the previous section.There's a real case for installing the interactive framework: a notebook or script run locally where plt.show() is supposed to pop up a window, or a small desktop tool built around Matplotlib's Tk integration. That's not what a container or CI runner is doing, which is exactly where both errors above tend to show up in the first place.
# Debian/Ubuntu
apt-get install python3-tk
# RHEL/CentOS/Fedora
yum install python3-tkinter
apt-get install python3-tk only wires up whichever python3 the package manager targets. Confirmed the hard way: installing it on a box running both Python 3.11 (a separate build at /usr/local/bin/python3) and Python 3.12 (the OS's /usr/bin/python3.12) fixed the import for 3.12 only — the 3.11 build kept raising ModuleNotFoundError right after. Check where which python3 points before assuming the install did anything.