Master Python dependency management and eliminate "it works on my machine" problems forever. Learn to create, maintain, and optimize requirements.txt files for consistent Python environments across teams and deployments. If you've ever tried to share your Python project with a colleague or deploy it on a new machine, you may have run into the dreaded "It works on my machine" problem. This frustrating situation occurs when your code works perfectly in your environment but throws errors elsewhere due to missing or mismatched dependencies. Thankfully, Python provides a simple yet powerful solution: the requirements.txt file. By using this file, you can list all the dependencies your project needs, including their specific versions. This ensures that anyone working with your project can replicate your environment exactly, whether they're on a different computer, operating system, or deploying to a production server.
Try our free requirements.txt Generator to build and validate a requirements.txt file instantly in your browser.
What Is a requirements.txt File?
At its core, a requirements.txt file specifies the Python packages required to run your project. It's a simple text file that lists the dependencies your project needs, often with specific version numbers to ensure compatibility.
Why Is It Important?
The requirements.txt file serves as a blueprint for recreating your project's environment. Here's why it's essential:
- Consistency Across Environments: Ensures everyone working on your project has the same setup, regardless of their system or operating system
- Prevents Bugs: By locking in specific versions of packages, you avoid issues caused by breaking changes or updates in dependencies
- Simplifies Collaboration: Instead of manually sharing which libraries are needed, you can share the requirements.txt file for quick project setup
Basic Format
A typical requirements.txt file contains one line for each package, followed by its version number. Here's an example:
pyOpenSSL==0.13.1
pyparsing==2.0.1
python-dateutil==1.5
pytz==2013.7
scipy==0.13.0b1
six==1.4.1
virtualenv==16.3.0
Each line specifies a package name and its version using ==. This ensures that the exact version you've tested your project with will be installed, helping to maintain stability and reliability.
Version Specifier Comparison Table
The version specifiers legal in a requirements.txt file are defined by the Python packaging version specifier specification (originally PEP 440). There are exactly eight of them:
| Specifier | Name | Example | Meaning |
|---|---|---|---|
== | Version matching | flask==2.0.1 | Only version 2.0.1 |
!= | Version exclusion | flask!=2.0.0 | Any version except 2.0.0 |
>= | Inclusive minimum | flask>=2.0.0 | Version 2.0.0 or higher |
<= | Inclusive maximum | flask<=3.0.0 | Version 3.0.0 or lower |
> | Exclusive minimum | flask>2.0.0 | Any version above 2.0.0 |
< | Exclusive maximum | flask<3.0.0 | Any version below 3.0.0 |
~= | Compatible release | flask~=2.0.1 | >=2.0.1 and ==2.0.* |
=== | Arbitrary equality | flask===2.0.1+local | Literal string match, escape hatch only |
Two of these trip people up constantly, so be precise about them:
~= does not mean "patch updates only". It means "the last component may increase". The rule is that ~=V.N expands to >=V.N, ==V.* — it drops the final segment and allows everything above:
flask~=2.0.1 # >=2.0.1, ==2.0.* → 2.0.2 and 2.0.9 OK, 2.1.0 NOT OK
flask~=2.2 # >=2.2, ==2.* → 2.3, 2.9 OK, 3.0 NOT OK
So ~=2.0.1 pins the minor version and ~=2.2 pins only the major version. They behave very differently, and ~=1 is invalid — the operator requires at least two segments.
== supports prefix matching with a trailing .*. flask==2.0.* matches any 2.0.x. Note that plain flask==2.0 also matches 2.0.0, because versions are zero-padded before comparison.
⚠️ There is no caret (^) operator in requirements.txt. flask^2.0.1 is a Poetry/Cargo-style specifier and pip will reject the line. If you are copying a dependency out of a pyproject.toml managed by Poetry, translate ^2.0.1 to >=2.0.1,<3.0.0 by hand. The same applies to Poetry's ~2.0.1 (single tilde) — the requirements.txt equivalent is ~=2.0.1.
You can also combine specifiers with commas, which are ANDed together:
# Allow versions 2.0.0 through 2.9.x, but not 3.0+
flask>=2.0.0,<3.0.0
# Require at least 1.0, exclude known buggy version
requests>=1.0,!=1.2.3
# Pin major version but allow patches
django>=4.2,<4.3
💡 Best Practice: For applications you deploy, pin exact versions with == (ideally generated by a lock step — see below). For libraries you publish, prefer a floor (>=) and add an upper bound only when you have a concrete reason, because premature upper bounds in libraries are a well-known source of unresolvable dependency conflicts for your users.
Every Line Type a requirements.txt File Accepts
Most guides only show package==version. pip's requirements file format accepts considerably more, and knowing the full surface is what lets you read a real-world file:
# 1. A comment. Blank lines are ignored too.
# 2. A plain requirement
requests
# 3. A requirement with a version specifier
requests==2.31.0
# 4. Extras — installs the package's optional dependency group
requests[socks]==2.31.0
celery[redis,auth]>=5.3
# 5. An environment marker — only install when the condition is true
pywin32==306 ; sys_platform == "win32"
# 6. A direct URL reference (PEP 508 "name @ url" form)
mypkg @ https://example.com/mypkg-1.0-py3-none-any.whl
# 7. A VCS checkout
mypkg @ git+https://github.com/org/mypkg.git@v1.2.3
# 8. An editable local install
-e ./libs/shared
# 9. Include another requirements file
-r requirements-base.txt
# 10. Apply a constraints file
-c constraints.txt
# 11. Global options
--index-url https://pypi.org/simple
--extra-index-url https://internal.example.com/simple
# 12. Line continuation with a trailing backslash
requests==2.31.0 \
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
Two parsing rules worth committing to memory: a # anywhere on a line starts a comment and truncates the rest of it, and paths in -r/-c are resolved relative to the file that contains the line, not to your current working directory. That second rule is why pip install -r deploy/requirements.txt works from the repo root even when the file says -r base.txt.
Specifying a Python Version in requirements.txt
This is one of the most common questions about the format, and the answer is a flat no — a requirements.txt file cannot declare which Python version your project needs. There is no python==3.11 line. If you write one, pip will try to install a PyPI package literally named python and fail (or, worse, install an unrelated squatted package).
What you can do is use an environment marker to make individual dependencies conditional on the running interpreter:
# Only install the backport on older interpreters
importlib-metadata==6.8.0 ; python_version < "3.10"
# Only install on 64-bit Linux
nvidia-cublas-cu12==12.1.3.1 ; sys_platform == "linux" and platform_machine == "x86_64"
The marker variables available are defined by the dependency specifiers spec: python_version, python_full_version, os_name, sys_platform, platform_machine, platform_system, platform_release, platform_version, platform_python_implementation, implementation_name, implementation_version, and extra. Values are compared as strings, which is why python_version < "3.10" is correct but a numeric comparison is not — and why python_version == "3.1" does not match 3.10.
To actually require a Python version, declare requires-python in pyproject.toml, or pin the interpreter outside Python entirely (a .python-version file for pyenv/uv, or the base image tag in your Dockerfile). requirements.txt only ever describes packages.
Why You Need a requirements.txt File
Using a requirements.txt file is not just a best practice in Python development—it's a game changer when it comes to managing dependencies and ensuring your projects run smoothly. Here are the key benefits:
1. Consistency
A requirements.txt file ensures that all team members and environments use the same versions of dependencies. Whether someone is working on a Mac, Windows, or Linux system, they can replicate your development environment with a single command. This eliminates the guesswork and avoids the dreaded "It works on my machine" scenario.
2. Ease of Collaboration
When sharing your project with others, the requirements.txt file makes it incredibly simple for them to set it up. Instead of manually installing dependencies, they can use the file to quickly install everything your project needs. This is especially useful for onboarding new team members or sharing your work in open-source projects.
3. Version Control
Dependencies often evolve, and package updates can sometimes introduce breaking changes. By specifying exact versions in the requirements.txt file, you lock your project to a stable, tested setup. This reduces the risk of bugs caused by updates and ensures your project remains functional over time.
4. Automation
The requirements.txt file is a critical component in deployment pipelines and continuous integration/continuous delivery (CI/CD) workflows. It automates dependency installation, allowing for seamless deployment of your project to production environments or testing frameworks without manual intervention.
How to Create a requirements.txt File
Creating a requirements.txt file is simple and can be done in two main ways: manually or automatically using the pip freeze command. Both approaches ensure you can document and manage the dependencies for your project effectively.
Manual Creation
If you already know the packages and versions your project requires, you can create a requirements.txt file manually. Here's how:
- Open a text editor of your choice (e.g., Notepad, VS Code, or PyCharm)
- List each package and its version on a new line in the following format:
package_name==version_number
- Save the file as requirements.txt in the root directory of your project For example, your requirements.txt file might look like this:
tensorflow==2.3.1
flask==2.0.1
numpy==1.21.0
Using pip freeze
For a more automated approach, you can generate a requirements.txt file using the pip freeze command (or use our requirements.txt Generator for a guided, browser-based option). This method lists all installed packages in your current Python environment, along with their version numbers. Here's how to do it:
- Open a terminal or command prompt
- Navigate to your project directory
- Run the following command:
pip freeze > requirements.txt
This command creates a requirements.txt file containing a list of all installed packages. 💡 Pro Tip: While pip freeze is convenient, it often includes packages that your project doesn't actually use, especially if your environment has been used for multiple projects. Consider using virtual environments for cleaner dependency management.
pip freeze vs Hand-Authored vs Compiled: Choosing a Pinning Strategy
The single biggest decision about a requirements.txt file is not what syntax to use but who writes it. There are three strategies, and mixing them up is the root cause of most dependency pain.
| Strategy | What the file contains | Strengths | Weaknesses |
|---|---|---|---|
| Hand-authored | Only your direct dependencies, usually with loose ranges | Readable; you can see intent | Not reproducible — transitive deps float, so two installs differ |
pip freeze | Everything installed, fully pinned | Reproducible | No record of why a package is there; can't tell direct from transitive; captures junk from a dirty environment |
| Compiled (two-file) | A hand-written requirements.in plus a generated, fully pinned requirements.txt | Reproducible and intent-preserving | One extra tool and one extra step |
The compiled approach is what most production teams converge on, because it gets both properties. You hand-write your intent:
# requirements.in — direct dependencies only
flask>=3.0
requests
gunicorn
Then generate the pinned, complete file:
pip install pip-tools
pip-compile requirements.in -o requirements.txt
The generated requirements.txt pins every package including transitive ones, and pip-tools annotates each line with what pulled it in:
flask==3.0.3
# via -r requirements.in
werkzeug==3.0.3
# via flask
Now pip install -r requirements.txt is reproducible, and six months later you can still tell that werkzeug is there because Flask needs it — not because someone once tried it out. To upgrade, you edit the .in file (or run pip-compile --upgrade-package flask) and recompile, rather than hand-editing pins. The same pattern is available from uv pip compile if you want a faster drop-in.
The specific breakage pip freeze causes: freeze records the state of your environment, including packages installed by editors, debuggers, or an earlier experiment. It also writes local/editable installs in forms that do not reinstall cleanly elsewhere (you will see lines like mypkg @ file:///Users/you/src/mypkg, which break on every other machine). Always freeze from a clean virtual environment, and read the diff before committing it.
Constraints Files: Pinning Without Installing
A constraints file (-c) is the least-known and most useful part of the format. It looks like a requirements file, but it does not install anything — it only says "if something ends up installing this package, it must be this version."
# constraints.txt
urllib3==1.26.18
pip install -r requirements.txt -c constraints.txt
This is the correct tool when a transitive dependency you do not directly use is causing a problem. Adding urllib3==1.26.18 to requirements.txt would falsely claim it as a direct dependency of your project; putting it in constraints.txt pins it without lying about your dependency graph. Constraints files may not contain extras, editable installs, or environment markers that would make them ambiguous.
Hash-Checking Mode: Verifying What You Install
For supply-chain security, pip supports pinning to a file hash with the per-requirement --hash option:
flask==3.0.3 \
--hash=sha256:34e815dfaa43340d1d15a5c3a02b8476004037eb4840b34910c6e21679d288f3 \
--hash=sha256:ceb27b0af3823ea2737928a4d99d125a06175b8512c445cbd9a9ce200ef76842
Multiple --hash entries for one package are ORed — you list one per acceptable distribution file (the wheel and the sdist, or several platform wheels).
Three rules from pip's secure installs documentation that surprise people:
- Hash-checking is global and contagious. Using
--hashon any single requirement turns the mode on for the entire install. - Every requirement must then be pinned, to
==, a URL, or a filesystem path. Ranges like>=are rejected in this mode, because a new release could otherwise appear and fail the hash unexpectedly. - Every transitive dependency must also be listed and hashed. pip will not silently install an unhashed sub-dependency, so hash-checking effectively requires a fully compiled file.
That last point is why hashes are generated, never hand-written. Use pip-compile --generate-hashes requirements.in -o requirements.txt, and add --require-hashes to your install command in CI so a file that has quietly lost its hashes fails the build instead of installing unverified code.
Install Packages from a requirements.txt File
Once you have a requirements.txt file, installing all the dependencies listed in it is straightforward. This process saves you time and ensures your project's environment is consistent across different systems.
Step-by-Step Guide
- Open a Terminal or Command Prompt: Navigate to the directory where your requirements.txt file is located
- Run the Installation Command: Use the following command to install all the dependencies listed in the file:
pip install -r requirements.txt
- This command reads the file line by line and installs the specified packages and versions
- Wait for the Installation to Complete: Once finished, you'll see output indicating that each package has been successfully installed
Troubleshooting Common Issues
While installing packages from a requirements.txt file is usually seamless, you might encounter issues. Here are some common problems and how to resolve them: ⚠️ Common Issue: Permission errors during installation can be resolved by using the --user flag: pip install -r requirements.txt --user
Using Virtual Environments
Virtual environments are a powerful tool in Python development. They allow you to isolate your project's dependencies from your system's global environment, ensuring that your project runs consistently and avoids conflicts with other projects.
What Is a Virtual Environment?
A Python virtual environment is a self-contained directory that contains a copy of the Python interpreter and its own independent set of installed packages. When you activate a virtual environment, any Python packages you install or modify are confined to that environment and won't interfere with other projects.
How to Use a Virtual Environment
Here's a step-by-step guide to creating and using a virtual environment:
- Create a Virtual Environment:
# Windows/Linux
python -m venv .venv
# macOS (using python3)
python3 -m venv .venv
- Activate the Virtual Environment:
# Windows
.venv\\Scripts\\activate
# macOS/Linux
source .venv/bin/activate
- Install Dependencies and Generate requirements.txt:
# Install packages
pip install flask numpy requests
# Generate requirements.txt
pip freeze > requirements.txt
# Deactivate when done
deactivate
💡 Best Practice: Always use virtual environments to keep your project dependencies isolated. This ensures your requirements.txt file contains only the packages your project actually needs, making it cleaner and more maintainable.
Best Practices for Using requirements.txt
To maximize the effectiveness of your requirements.txt file and maintain a smooth development process, follow these essential best practices:
- Use Virtual Environments: Always generate your requirements.txt file from a virtual environment to ensure only necessary packages are included
- Pin Package Versions: Specify exact versions using == operator to prevent unexpected behavior from updates
- Keep in Version Control: Add requirements.txt to your Git repository for team collaboration
- Regular Updates: Periodically update dependencies for security patches and bug fixes
- Test After Updates: Always test your project after updating dependencies
Advanced Tools for Dependency Management
Consider these advanced tools for more sophisticated dependency management:
- pip-tools: Automatically resolves dependency conflicts and generates requirements.txt from requirements.in files
- Poetry: Modern Python dependency manager with automatic virtual environment handling
- pipreqs: Generates requirements.txt by scanning your project's imports
Migrating to pyproject.toml
Python's packaging ecosystem is evolving toward pyproject.toml as the standard configuration file (PEP 621). While requirements.txt remains widely used, understanding when and how to migrate can future-proof your projects.
For a complete guide to pyproject.toml, see our pyproject.toml Complete Guide. For help deciding which file to use, see pyproject.toml vs requirements.txt vs setup.py.
When to Use Each Approach
| Use Case | Recommended | Reason |
|---|---|---|
| Simple scripts | requirements.txt | Minimal setup, widely understood |
| Applications (web apps, APIs) | requirements.txt | Deployment tools expect it |
| Reusable libraries/packages | pyproject.toml | Modern standard for distribution |
| Projects using Poetry/PDM | pyproject.toml | Native format for these tools |
| Legacy projects | requirements.txt | Don't fix what isn't broken |
Converting requirements.txt to pyproject.toml
If you're building a distributable package, here's how to migrate:
Original requirements.txt:
flask>=2.0.0
requests==2.28.1
python-dotenv~=1.0.0
Equivalent pyproject.toml:
[project]
name = "my-project"
version = "1.0.0"
description = "My Python project"
requires-python = ">=3.8"
dependencies = [
"flask>=2.0.0",
"requests==2.28.1",
"python-dotenv~=1.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0",
"black>=23.0",
]
Generating requirements.txt from pyproject.toml
Many deployment platforms still require requirements.txt. You can generate one from pyproject.toml:
# Using pip-tools (recommended)
pip install pip-tools
pip-compile pyproject.toml -o requirements.txt
# Using Poetry
poetry export -f requirements.txt --output requirements.txt
# Using PDM
pdm export -o requirements.txt
💡 Key Insight: You don't have to choose one over the other. Many projects use pyproject.toml as the source of truth and generate requirements.txt for deployment compatibility.
Security Considerations
When managing Python dependencies, security should be a top priority. Outdated packages can introduce vulnerabilities that put your application and data at risk. Here's how to maintain secure dependency management:
Automated Security Scanning
GitHub automatically scans requirements.txt files for known vulnerabilities and sends alerts when security issues are detected. This automated monitoring helps you stay ahead of potential threats without manual oversight.
Regular Dependency Audits
Use tools like pip-audit or safety to regularly scan your dependencies for known security vulnerabilities. These tools can be integrated into your CI/CD pipeline for continuous security monitoring. 🔒 Security Alert: Never ignore dependency security warnings. Outdated packages with known vulnerabilities can be entry points for cyberattacks. Regular updates and security audits are essential for maintaining a secure Python environment.
Troubleshooting Common Errors
Here are solutions to the most frequent issues when working with requirements.txt files:
ERROR: Could not find a version that satisfies the requirement
ERROR: Could not find a version that satisfies the requirement package-name==1.0.0
ERROR: No matching distribution found for package-name==1.0.0
Causes and solutions:
- Package doesn't exist: Check for typos in the package name (
pip searchis deprecated, use PyPI.org to verify) - Version doesn't exist: Run
pip index versions package-nameto see available versions - Python version incompatible: Some packages require specific Python versions. Check with
python --version - Platform incompatible: Some packages are Windows/Linux/macOS only
ERROR: pip's dependency resolver does not currently take into account all the packages
ERROR: pip's dependency resolver does not currently take into account all packages
Solution: This warning appears when packages have conflicting dependencies. Use pip-tools for better resolution:
pip install pip-tools
pip-compile requirements.in --resolver=backtracking
Permission denied errors
ERROR: Could not install packages due to an EnvironmentError: [Errno 13] Permission denied
Solutions:
# Option 1: Install for current user only (recommended)
pip install -r requirements.txt --user
# Option 2: Use a virtual environment (best practice)
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install -r requirements.txt
Hash verification failed
ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE
Cause: The distribution file pip downloaded does not match any hash in the file. Usually this is benign — a different wheel was selected for your platform or Python version than the one that was hashed — but it can also mean the artifact was tampered with or your index is serving something unexpected.
Solutions:
# Regenerate hashes for all platforms you deploy to (if you trust the packages)
pip-compile --generate-hashes requirements.in -o requirements.txt
Check why it mismatched before regenerating. pip prints both the expected and the actual hash; if the package version did not change but the hash did, stop and investigate rather than re-pinning to whatever you just downloaded.
⚠️ Do not reach for --no-deps here — it controls dependency resolution, not hash verification, and will not disable the check. The flag that actually turns hash-checking off is --no-require-hashes, and using it discards the supply-chain guarantee the hashes exist to provide. Treat it as a local debugging step only, never as a fix committed to CI.
Conflict between package versions
ERROR: Cannot install package-a and package-b because these package versions have conflicting dependencies.
Solution: Create a constraints file to force compatible versions:
# constraints.txt
problematic-package==1.2.3
# Install with constraints
pip install -r requirements.txt -c constraints.txt
SSL Certificate errors
ERROR: Could not fetch URL https://pypi.org/simple/: There was a problem confirming the ssl certificate
Solutions:
# Update certificates
pip install --upgrade certifi
# Temporary workaround (not recommended for production)
pip install -r requirements.txt --trusted-host pypi.org --trusted-host files.pythonhosted.org
Summary
The requirements.txt file is a simple yet powerful tool for managing Python dependencies. By listing the packages your project needs and their specific versions, you can ensure consistency, simplify collaboration, and streamline deployment. It's an essential practice for any Python developer, whether you're working solo or in a team. Key takeaways from this guide:
- The format accepts far more than
package==version— extras, environment markers, direct URLs,-rincludes,-cconstraints, and--hashare all part of it - There is no caret (
^) operator and no way to declare a Python version; use>=x,<yandrequires-pythonrespectively ~=V.Nmeans>=V.N, ==V.*, so~=2.0.1and~=2.2pin at different levels- Virtual environments are essential for creating clean, isolated dependency lists
- Compiling a pinned
requirements.txtfrom a hand-writtenrequirements.ingives you reproducibility without losing intent - Hash-checking mode is global, requires
==pins, and requires every transitive dependency to be listed
Now that you've learned how to create, use, and maintain a requirements.txt file, put it into action: set up a virtual environment, write a short requirements.in with just your direct dependencies, and compile it. You can also build and validate a file in the browser with our requirements.txt Generator.