MeatButton

Node or Python Version Conflict: Why the Wrong Version Keeps Running

For anyone getting version errors or "command not found" after installing Node or Python

You installed Node 20. You run node --version and it says 16. Or you installed Python 3.12 but your app is somehow running on Python 3.8. Or you just finished installing Node with nvm and the terminal says node: command not found.

This is one of the most confusing things about Linux servers. You clearly installed the right version. You can see it. But something else keeps running instead. What is going on?

Your server has multiple versions installed at the same time

This is the core issue. Unlike your laptop where you probably have one version of Node and one version of Python, a Linux server can have several versions of each installed simultaneously. They live in different folders. Which one actually runs when you type node or python3 depends on something called the PATH.

The PATH is a list of directories your system searches through, in order, when you type a command. If /usr/bin comes before /home/you/.nvm/versions/node/v20.11.0/bin in the PATH, and there's a node in both places, the system uses the one in /usr/bin — even if that's Node 16 from two years ago.

That's it. That's the whole problem. Everything else is just variations of "the wrong directory came first in the PATH."

How to see which version is actually running

Before you fix anything, figure out what's happening. The which command tells you exactly which executable will run:

which node
# /usr/bin/node          ← system Node (old)
# /home/you/.nvm/versions/node/v20.11.0/bin/node  ← nvm Node (new)

which python3
# /usr/bin/python3       ← system Python (old)
# /home/you/.pyenv/shims/python3   ← pyenv Python (new)

node --version
python3 --version

If which node points to /usr/bin/node when you expected the nvm version, now you know the problem. The system version is winning the PATH race.

The version manager trap: nvm, pyenv, and asdf

Here's where most people get burned. Version managers like nvm (for Node), pyenv (for Python), and asdf (for everything) are great tools. They let you install any version you want and switch between them easily. But they have a catch that almost nobody explains clearly:

They only work in your terminal session.

When you install nvm, it adds a few lines to your .bashrc or .zshrc file. Every time you open a new terminal, those lines run and modify your PATH to include nvm's Node versions. Same deal with pyenv and asdf.

This works fine when you're typing commands interactively. The problem shows up when something else tries to run your app:

So you test your app in the terminal and it works perfectly with Node 20. Then you set up a systemd service and it either crashes because it's running Node 16, or fails entirely because it can't find node at all.

System Python vs. your Python

Python makes this especially confusing because Linux itself depends on Python. Your operating system uses Python for internal tools, package management, and system scripts. That system Python is usually an older version — Python 3.8 on Ubuntu 20.04, Python 3.10 on Ubuntu 22.04.

You can't just upgrade system Python. If you replace /usr/bin/python3 with a newer version, you risk breaking your operating system. That's not an exaggeration — people have bricked their servers doing this.

So when you install Python 3.12 through pyenv, it doesn't replace system Python. It sits alongside it. Now you have two Python 3s:

/usr/bin/python3                        ← system Python 3.8 (don't touch this)
/home/you/.pyenv/versions/3.12.0/bin/python3  ← your Python 3.12

And here's the fun part: pip install installs packages to whichever Python runs it. If you run pip install flask and pip belongs to system Python 3.8, Flask gets installed there. Then your app runs under pyenv Python 3.12 and says "ModuleNotFoundError: No module named 'flask'" because that Python has its own separate set of packages.

This is why you see advice to always use python3 -m pip install instead of bare pip — it forces pip to install packages for the specific Python you're pointing at.

How to fix it for systemd services

If your app runs as a systemd service (and it probably should — see our systemd guide), you need to use absolute paths to the exact version you want.

First, find the real path while you're in a terminal where the right version works:

which node
# /home/deploy/.nvm/versions/node/v20.11.0/bin/node

which python3
# /home/deploy/.pyenv/versions/3.12.0/bin/python3

Then use that full path in your service file:

[Service]
# Instead of this (will use system version or fail):
ExecStart=node server.js

# Do this:
ExecStart=/home/deploy/.nvm/versions/node/v20.11.0/bin/node server.js

# For Python:
ExecStart=/home/deploy/.pyenv/versions/3.12.0/bin/python3 app.py

Yes, it's ugly. Yes, it breaks if you update Node and the version number in the path changes. That's the tradeoff with version managers — flexibility in the terminal, rigidity everywhere else.

The wrapper script alternative

If you don't want hardcoded version paths in your service file, you can use a wrapper script that loads nvm first:

#!/bin/bash
export NVM_DIR="/home/deploy/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
nvm use 20
exec node /home/deploy/myapp/server.js

Save that as /home/deploy/myapp/start.sh, make it executable (chmod +x start.sh), and point your service file at it:

ExecStart=/home/deploy/myapp/start.sh

Same idea works for pyenv — just source pyenv's init script instead.

How to fix it system-wide

If you want a version of Node or Python available to all users, all services, and all cron jobs without path gymnastics, you have a few options:

Install from your distro's package manager

# Ubuntu/Debian
sudo apt install nodejs    # gives you whatever version the distro ships
sudo apt install python3   # same

# For newer Node versions, use the NodeSource repository:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install nodejs

This installs to /usr/bin/ where everything can find it. The downside: you get whatever version your distro offers, which might not be what you want. The NodeSource repo helps for Node, but Python is trickier — Ubuntu is notoriously slow to ship new Python versions.

Use the alternatives system

If you've installed multiple versions and want to set a system-wide default:

sudo update-alternatives --install /usr/bin/python3 python3 /usr/local/bin/python3.12 1
sudo update-alternatives --config python3

This is cleaner than symlinking manually, but it's still a Linux-specific tool that most people don't know about.

The Docker solution

Docker sidesteps this entire problem. Instead of managing Node and Python versions on your server, you specify the exact version in a Dockerfile:

FROM node:20-slim
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]

Inside the container, there's exactly one Node, and it's the version you specified. It doesn't matter what's installed on the host server. It doesn't matter what's in the PATH. The container is its own isolated world.

This is why Docker became so popular. It's not just about deployment convenience — it eliminates an entire category of "works on my machine" problems, including version conflicts. If your app runs in a Docker container locally, it will run the same way on the server.

Docker has its own learning curve, but once it's set up, you never think about version conflicts again.

Why AI makes this problem worse

If you asked an AI to help deploy your app, there's a good chance it introduced the version conflict in the first place. Here's how:

The worst case: the AI gives you a fix that appears to work because you tested it in your terminal. Everything looks fine. Then the server reboots, systemd starts your app with the wrong version, and it crashes at 3 AM.

Quick reference: common symptoms and causes

What you see What's happening
node: command not found nvm isn't loaded in this context (systemd, cron, different user)
node --version shows old version System Node in /usr/bin is winning over nvm's version
python3 shows wrong version System Python vs. pyenv Python — check which python3
ModuleNotFoundError Packages installed for one Python, app running under a different one
App works in terminal, crashes in systemd Systemd doesn't load nvm/pyenv — needs absolute path
nvm: command not found nvm isn't a standalone program — it's a shell function loaded by .bashrc
pip install doesn't seem to work pip belongs to a different Python than the one running your app

The bottom line

Version conflicts aren't a bug. They're a natural consequence of how Linux manages software. Multiple versions coexist peacefully — the confusion comes from not knowing which one is being used in which context.

The fix is almost always one of three things:

  1. Use absolute paths to the specific version you want, especially in service files and cron jobs.
  2. Install system-wide via your package manager if you need the same version available everywhere.
  3. Use Docker to eliminate the problem entirely.

And always run which node or which python3 before assuming you know what version is going to execute.

Still getting the wrong version?

Version conflicts can nest. Maybe your app calls a subprocess that uses a different Python. Maybe your build tool uses a different Node than your runtime. Maybe you've got three Pythons and none of them have the right packages. Press the MeatButton and a real expert will SSH in, untangle which version is running where, and get everything pointing at the right thing. First one's free.

Get MeatButton