How to Install Python 3 and pip on Fedora

Install Python 3 and pip on Fedora by using the `dnf` package manager to pull the `python3` and `python3-pip` packages from the default repositories.

You cloned a repository and the first command fails

You open a terminal to run a Python script you just cloned from GitHub. The script fails immediately with a missing module error. You run pip install requests and get a permission denied message. You try sudo pip install requests and suddenly your system package manager complains about broken dependencies. This happens to almost everyone switching to Fedora. The system protects itself aggressively, and the default Python setup enforces strict boundaries between system tools and user projects.

What's actually happening

Fedora ships with Python 3 as a core system component. NetworkManager, GNOME, and dozens of background services depend on it. Because of that dependency, the system Python installation lives in /usr/lib/python3 and is managed exclusively by dnf. The pip package manager is not installed by default. It sits in a separate repository package. When you install pip alongside the system Python, you get a tool that can pull third-party libraries from PyPI. Those libraries go into /usr/lib/python3/site-packages by default. Writing to that directory requires root privileges. Modifying it breaks system tools when Fedora pushes an update. The package manager expects a clean, predictable state. User-installed packages drift away from that state.

The solution is not to fight the package manager. The solution is to install the base tools correctly, then isolate your work. Fedora provides the building blocks. You just need to arrange them in the right order. Config files in /etc/ are user-modified. Files in /usr/lib/ ship with the package. Edit /etc/. Never edit /usr/lib/. The same rule applies to Python packages. System packages stay in /usr/lib/. Your project packages stay in a virtual environment.

The fix or how-to

Start by installing the core interpreter, the package manager, and the development headers. The headers are required for any Python package that compiles C extensions. Many popular libraries like numpy, psycopg2, and cryptography need them.

Here is how to install the base environment without touching the protected system directories.

sudo dnf install python3 python3-pip python3-devel -y
# python3 provides the interpreter and standard library
# python3-pip gives you the package installer for third-party libraries
# python3-devel supplies header files and config scripts for compiling C extensions
# -y skips the confirmation prompt to keep the transaction automatic

Fedora separates runtime libraries from development files. The python3 package contains the executable and the standard library. The python3-devel package contains the .h files and python3-config script. Compilers need those headers to link against the Python runtime. Without them, pip install fails halfway through with a missing header error.

If your workflow involves building packages from source, install the full compiler toolchain. This group installs gcc, make, autoconf, and the standard C/C++ libraries.

sudo dnf groupinstall "Development Tools" -y
# groupinstall pulls a curated set of packages instead of listing them individually
# Development Tools covers compilers, linkers, and build automation utilities
# -y accepts the package list without prompting

Now set up an isolated environment for your actual work. Virtual environments keep your project dependencies separate from the system Python and from each other. They also let you install packages without root privileges.

python3 -m venv ~/projects/myapp-env
# -m venv invokes the built-in virtual environment module
# ~/projects/myapp-env creates the isolated directory structure
# The module copies the interpreter and sets up a private site-packages folder

Activate the environment before you install anything. The shell prompt changes to show the active environment name. All subsequent pip commands target the isolated directory.

source ~/projects/myapp-env/bin/activate
# source runs the activation script in the current shell session
# The script prepends the venv bin directory to your PATH variable
# pip and python now point to the isolated copies instead of the system binaries

Install your dependencies inside the active environment. You no longer need sudo. The packages go into ~/projects/myapp-env/lib/python3.x/site-packages.

pip install requests flask
# pip downloads wheels or source distributions from PyPI
# The packages install into the active venv site-packages directory
# No root privileges are required because the directory belongs to your user

Run journalctl -xe if a service fails to start after you modify Python paths. The x flag adds explanatory context to log lines. The e flag jumps to the end of the journal. Most sysadmins type journalctl -xeu <unit> to isolate service failures quickly.

Verify it worked

Check that the interpreter and package manager point to the correct locations. Run these commands inside the activated environment.

which python3
which pip
python3 --version
pip --version

The output should show paths inside ~/projects/myapp-env/bin. The version strings should match. If which pip returns /usr/bin/pip3, your environment did not activate correctly. Run the source command again and check your shell profile for conflicting aliases.

Test a package that requires compilation. This confirms the development headers and toolchain are working.

pip install cryptography
# cryptography includes C extensions that require gcc and python3-devel
# A successful build proves the compiler toolchain and headers are linked correctly
# The wheel installs into the venv without touching system directories

Verify the environment isolates packages properly. Deactivate it and check the available modules.

deactivate
# deactivate restores the original PATH and removes venv-specific environment variables
# The shell prompt returns to its default state
python3 -c "import requests; print(requests.__version__)"
# This command should fail with ModuleNotFoundError
# The system Python does not contain packages installed in the virtual environment
# This confirms the isolation boundary is working as intended

Manage your project dependencies explicitly. Export the installed packages to a requirements file so you can recreate the environment on another machine.

source ~/projects/myapp-env/bin/activate
pip freeze > requirements.txt
# freeze lists all installed packages with exact version pins
# The output writes to a text file in your current directory
# Other developers can recreate the exact same environment with pip install -r requirements.txt

Common pitfalls and what the error looks like

The most common mistake is running sudo pip3 install to bypass permission errors. That command writes packages directly into /usr/lib/python3/site-packages. Fedora's package manager tracks those directories. When dnf upgrade runs, it expects the directory to match the installed RPM state. Third-party packages break that expectation. The next system update will fail with a file conflict error.

You will see this exact error when the package manager detects the drift:

Error: Transaction test error:
file /usr/lib64/python3.12/site-packages/requests/__init__.py from install of python3-requests-2.31.0-1.fc40.noarch conflicts with file from package python3-requests-2.31.0-1.fc40.noarch

Never force the package manager past this error. Remove the conflicting package from the system directory first, or better yet, switch to a virtual environment and never touch the system site-packages again. If you already broke the state, run sudo dnf remove python3-requests to let RPM clean up the conflicting files, then reinstall the system package with sudo dnf install python3-requests.

SELinux blocks execution of Python scripts in unexpected locations. Fedora enforces mandatory access controls on all files. If you drop a script into /home/user/scripts and run it, SELinux might deny execution if the context is wrong. You will see a denial in the journal.

type=AVC msg=audit(1700000000.000:100): avc:  denied  { execute } for  pid=1234 comm="python3" name="app.py" dev="sda1" ino=54321 scontext=system_u:system_r:unconfined_t:s0 tcontext=unconfined_u:object_r:home_root_t:s0 tclass=file permissive=0

Fix the context by restoring the default labels for your home directory. The restorecon command reads the policy database and applies the correct labels.

restorecon -Rv ~/projects
# -R applies the fix recursively to all files and subdirectories
# -v prints each file as the context is updated
# This aligns your project directory with the SELinux policy expectations

Network binding fails when you run a development server. Python web frameworks listen on 127.0.0.1 by default. If you change the host to 0.0.0.0 to test from another machine, the firewall blocks the connection. Fedora's firewall drops incoming traffic unless you explicitly allow it.

sudo firewall-cmd --permanent --add-port=8000/tcp
# --permanent writes the rule to the persistent configuration file
# --add-port=8000/tcp allows TCP traffic on the standard dev server port
# The rule does not take effect until the firewall reloads
sudo firewall-cmd --reload
# --reload applies the persistent rules to the running firewall
# Always run this after every permanent rule change
# The runtime and persistent configurations stay synchronized

Trust the package manager. Manual file edits drift, snapshots stay.

When to use this vs alternatives

Use the system python3 package when you need a stable, security-patched interpreter that integrates with Fedora updates. Use python3 -m venv when you are working on a single project with specific dependency versions. Use pyenv when you need to switch between multiple Python major versions for different legacy codebases. Use Docker or Podman when you want to package the entire runtime and dependencies into a reproducible container. Stay on the default Fedora Python if you only deviate from the system packages occasionally.

Where to go next