No module named 'yaml' is almost always one of three things: the package PyYAML is not installed (it imports as yaml, not pyyaml, which trips up the muscle memory), the C extension failed to build because libyaml is missing on the system, or the wrong Python is on the PATH — same cause as the requests error. The reason this question gets asked specifically about YAML is the package-name mismatch. The package on PyPI is PyYAML. The module it installs is yaml. People type pip install yaml (which installs a different, lesser-known package) or import pyyaml (which fails because the import name is yaml). The error then points at the wrong thing.
This post walks through the three real causes, the libyaml trap that catches Alpine and slim Docker users, and the one case where the answer is “use a different package.” The requests post covers the framework; this one covers the YAML-specific failure modes that framework does not address.
The interesting thing about this error is that it has more failure modes than the average ModuleNotFoundError. Most Python packages are one-stop: install, import, use. PyYAML is two: install the Python package, and make sure the optional C extension compiles against the system’s libyaml. The second is where most of the time is spent.
Table of contents
- The direct answer
- The package-name trap
- Cause 1: the package is not installed
- Cause 2: libyaml is missing (the C extension trap)
- Cause 3: the wrong Python is running
- When the answer is ruamel.yaml instead
- The deploy-side version: slim Docker images
- The opinion this post is built on
- FAQ
The direct answer
# The package is PyYAML, not yaml
python -m pip install PyYAML
# Verify
python -c "import yaml; print(yaml.__version__)"
If the install succeeds and the import fails, the problem is either the wrong Python (the same diagnostic as the requests post covers) or a failed C extension build. For the C extension:
# Debian/Ubuntu
sudo apt install libyaml-dev
# Alpine
apk add yaml-dev
# macOS
brew install libyaml
# Then reinstall
python -m pip install --force-reinstall --no-cache-dir PyYAML
The --no-cache-dir is important. Pip caches the previous failed build, and a rebuild with the system library present may pick up the old cached artifact. The --force-reinstall makes pip redo the build cleanly.
That is the working baseline. The rest of this post is the cases where the baseline is not enough.
The package-name trap
The PyPI package is PyYAML. The Python module it installs is yaml. The import statement is import yaml. The install command is pip install PyYAML. People who type pip install yaml install a different package, one that has been mostly unmaintained for a decade, and the error they hit later is “yaml.safe_load” does not behave the way the docs say, because the API is subtly different. People who type import pyyaml get a ModuleNotFoundError even when the install is correct, because the module name has no “py” in it.
The reason for the mismatch is that PyYAML predates the modern Python packaging conventions. The package was named with the “Py” prefix to match the era’s naming style; the module was named to be short to type. Both decisions stuck. The result is a small piece of friction that catches people who learned Python after 2015 and have not hit PyYAML before.
A useful test:
python -c "import yaml; print(yaml.__file__)"
If the output ends with something like site-packages/yaml/__init__.py, the install is correct. If the error is ModuleNotFoundError, the install is wrong, the import is wrong, or the Python is wrong. The same three-cause framework as the requests post applies, with the package-name trap as the YAML-specific addition.
Cause 1: the package is not installed
The diagnostic:
python -c "import yaml; print(yaml.__version__)"
If the answer is ModuleNotFoundError, the package is not installed for this Python. The fix:
python -m pip install PyYAML
If the install completes and the import still fails, the problem is the Python — go to cause 3. If the install fails with a compilation error, go to cause 2. If the install succeeds and the import succeeds, the problem is solved and the rest of the post is for the next incident.
A useful pattern: pin the version in requirements.txt:
PyYAML==6.0.1
Pinned versions survive a surprise pip install --upgrade months later, and they make the deploy reproducible. The 6.x line is the current stable, and the API has been stable across it.
Cause 2: libyaml is missing (the C extension trap)
This is the YAML-specific failure mode. PyYAML ships a Python implementation of the YAML parser and an optional C extension that wraps the system’s libyaml library. The C extension is faster — measurably so for large documents — but it has to be compiled against libyaml at install time. If libyaml is not on the system, the C extension fails to build, and the install either fails outright or succeeds with a Python-only fallback that is much slower.
The error when the build fails outright:
__main__.LibraryNotFoundError: ('/usr/lib/x86_64-linux-gnu/libyaml.so.0', 'libyaml.so.0: cannot open shared object file: No such file or directory')
The error when the build silently falls back to the Python implementation is the harder one to catch: the install succeeds, the import works, the YAML parsing is just slow, and the developer does not realize they are running the slow path. The C extension is the default; the Python fallback is the path you get when the C extension fails to build.
The fix is the system package:
# Debian / Ubuntu
sudo apt install libyaml-dev
# Alpine
apk add yaml-dev
# Fedora / RHEL
sudo dnf install libyaml-devel
# macOS (Homebrew)
brew install libyaml
After installing the system library, reinstall PyYAML to pick up the C extension:
python -m pip install --force-reinstall --no-cache-dir PyYAML
Verify the C extension is in use:
python -c "import yaml; print(yaml.__with_libyaml__)"
# True
If the output is False, the C extension is not active, and the YAML parsing will be slow on any non-trivial document. If the output is True, the C extension is active, and the parser is running at the speed the package was designed for.
This is the failure mode that hits the hardest in Docker. The python:3.12-slim image does not ship libyaml, and a pip install PyYAML in the Dockerfile silently drops to the Python fallback. The application works. The performance does not. The next deploy profiling session finds the cause, and the fix is a one-line apt install in the Dockerfile.
Cause 3: the wrong Python is running
Same as the requests post. which python and python -c "import sys; print(sys.executable)" should agree. If they do not, the install is going to one Python and the import is coming from another. Use python -m pip install PyYAML to install for the Python the script is using.
The diagnostic:
which python
which pip
python -c "import sys; print(sys.executable)"
python -m pip --version
If python -m pip --version points at a different python than which python, that is the cause. The fix is to use python -m pip exclusively, and to alias pip to python -m pip in the shell so the wrong-pip problem stops being a problem.
When the answer is ruamel.yaml instead
PyYAML is the right default, but it is not the only YAML library, and there are cases where ruamel.yaml is the better answer.
ruamel.yaml is a YAML 1.2 parser that preserves round-trip comments and formatting. PyYAML is a YAML 1.1 parser that loads and dumps without preserving comments. For most applications — config files, API payloads, data interchange — PyYAML is fine. For applications that edit YAML in place (config-file editors, IaC tools, document generation), ruamel.yaml is the right tool.
The install is the same pattern:
python -m pip install ruamel.yaml
The import is from ruamel.yaml import YAML. The API is slightly different but the basic load and dump work the same way.
The reason to mention ruamel.yaml in a No module named 'yaml' post is that the failure mode is sometimes not the install but the semantics. The developer has a YAML file with comments, edits a value, dumps it, and the comments are gone. That is not a missing module — that is PyYAML doing exactly what it was designed to do. The fix is to switch to ruamel.yaml and use its round-trip API.
The deploy-side version: slim Docker images
The python:3.12-slim and python:3.12-alpine images are the most common deploy bases for Python apps, and both are the most common sources of the libyaml failure.
For python:3.12-slim (Debian-based):
FROM python:3.12-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
libyaml-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
For python:3.12-alpine:
FROM python:3.12-alpine
RUN apk add --no-cache yaml-dev
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
Without the libyaml-dev or yaml-dev line, the C extension silently fails to build, the install succeeds, and the parser runs in the slow Python path. With the line, the C extension builds, the parser runs at full speed, and the YAML-heavy part of the application is no longer the bottleneck.
A platform that pre-installs libyaml in the build image, surfaces the runtime image’s libraries, and rebuilds the image on dependency changes is the one that turns this class of bug from a Friday-morning profiling session into a non-event. The RunxBuild platform is built around that pattern.
For a sanity check on the deploy cost, the hosting cost calculator gives a real number to compare against. The cheapest deploy is rarely the one that runs YAML at full speed.
The opinion this post is built on
PyYAML is the canonical example of a package that works locally and breaks in production for reasons that have nothing to do with the package. The package-name mismatch trips up muscle memory. The C extension requires a system library that is not in the standard Python image. The C extension’s failure mode is silent, so the developer does not know the parser is running slow until they profile.
The fix is small and boring:
- Use
PyYAML, notyaml, inrequirements.txt. - Use
import yaml, notimport pyyaml, in the code. - Install
libyaml-dev(or the platform equivalent) in the build image. - Verify
yaml.__with_libyaml__isTrueafter install.
None of that is glamorous. All of it is the price of admission for using a parser that has both a Python and a C implementation and makes the choice between them invisible to the developer. The discipline is the same as the rest of Python: pin the version, isolate the environment, and verify the install actually did what the install message claimed.
A platform that ships a Python image with libyaml preinstalled, surfaces the runtime’s library versions, and rebuilds the image on dependency changes is the platform that turns this from a Friday-afternoon fire into a non-event. The platform is the only honest answer to “why is YAML slow in production” that does not involve a profiler and a Stack Overflow thread.
FAQ
Why does pip install yaml not work?
yaml is a different, mostly unmaintained package on PyPI. The package you want is PyYAML. The install command is python -m pip install PyYAML, and the import is import yaml. The package name and the module name differ, which is a long-standing quirk of this particular library.
What is the difference between yaml and PyYAML?
The PyPI package is PyYAML, and the Python module it installs is yaml. There is no pyyaml module; the import is always import yaml. The package was named with the “Py” prefix when it was first published, and the module was named to be short to type. Both names stuck.
Why is import yaml slow on slim Docker images?
Because libyaml is not installed in the build image, the C extension fails to build, and PyYAML silently falls back to the pure-Python implementation. The install succeeds, the import works, and the parsing is just much slower than it should be. The fix is to install libyaml-dev (Debian) or yaml-dev (Alpine) in the build image, then reinstall PyYAML.
How do I know if the C extension is in use?
python -c "import yaml; print(yaml.__with_libyaml__)"
If the output is True, the C extension is active. If False, the Python fallback is in use, and the parser is slow on non-trivial YAML. The fix is to install the system libyaml and reinstall PyYAML with --force-reinstall --no-cache-dir.
Should I use ruamel.yaml instead?
Use ruamel.yaml if you need YAML 1.2 round-trip support, comment preservation, or stricter YAML 1.2 compliance. For most applications — config loading, API payload parsing, data interchange — PyYAML is fine. The two libraries can coexist in the same project if different parts need different semantics.
Does PyYAML work on Python 3.12+?
Yes. PyYAML 6.x supports Python 3.8 through 3.13. The 5.x line is the last to support Python 2; the 6.x line is the current stable. If you are on Python 3.12 and seeing install failures, the cause is almost always the libyaml system library, not the Python version.