Cron and Python Virtualenv
Cron doesn't know about an activated virtualenv — it uses the system Python and misses your installed packages. The reliable fix is to call the venv's python interpreter by its absolute path instead of activating anything.
Call the venv python directly
No activation needed — just use the interpreter inside the venv:
* * * * * /home/you/venv/bin/python /home/you/app.py >> /var/log/app.log 2>&1
That interpreter already sees the venv's packages, so imports work.
Or activate inside the command
* * * * * /bin/bash -c 'source /home/you/venv/bin/activate && python /home/you/app.py'. Wrapping in bash is required because cron's default shell won't run source the same way.
Why activation isn't enough
Activating a venv just prepends its bin to PATH in your shell. Cron starts a fresh minimal shell without that PATH, so it falls back to system Python and raises ModuleNotFoundError.
FAQ
How do I run a Python venv script from cron?
Call the venv's interpreter by absolute path: /home/you/venv/bin/python /home/you/app.py. It already sees the venv packages, no activation required.
Why does cron use the wrong Python?
Cron starts a minimal shell without your activated venv on PATH, so it uses system Python and misses installed packages, causing ModuleNotFoundError.
Can I activate a virtualenv in cron?
Yes: wrap it in bash — /bin/bash -c 'source venv/bin/activate && python app.py'. But calling venv/bin/python directly is simpler and more reliable.