Working with Virtual Environments in Flask
When building Flask applications, getting your Python version, virtual environment, and dependency management right early saves a lot of pain later.
This note focuses on three tools you will see a lot in modern Flask work:
We’ll walk through two real-world scenarios:
Throughout, we’ll be mindful of operating systems:
The focus here is environment and dependency management, not your Flask routes or templates.
1. Concepts: Python version vs virtual env vs dependencies
There are three separate layers you should keep straight:
You can mix and match tools. For example:
The rest of this note shows concrete flows and explains what each step does.
2. Scenario A – New Flask project
You’re starting from scratch and want a good, repeatable way to manage your Python version, virtualenv, and dependencies.
We’ll show three common setups:
2.1 pyenv + pyenv-virtualenv + pip + requirements.txt
This is a good choice when:
2.1.1 Install pyenv (high level)
Full instructions are in this separate note:
But in short:
macOS (Homebrew) — typical steps:
brew install pyenv pyenv-virtualenv
Then add the init snippet to your shell (~/.zshrc or ~/.bashrc) as described in that note.
Ubuntu / WSL — typical steps:
After installation, restart your shell and verify:
type pyenv
pyenv --version
2.1.2 Choose a Python version with pyenv
pyenv install 3.11.9 # or another version you need
mkdir my_flask_app
cd my_flask_app
pyenv local 3.11.9
You can confirm:
python -V
which python
pyenv version
2.1.3 Create a pyenv virtualenv for the project
pyenv virtualenv 3.11.9 my-flask-env
pyenv local my-flask-env
Now, simply entering this directory activates the env (no source .venv/bin/activate needed):
cd my_flask_app
python -V # should show 3.11.9 in the pyenv env
which python # should point inside ~/.pyenv/versions/my-flask-env/bin
2.1.4 Install Flask and create requirements.txt
pip install Flask python-dotenv Flask-Mail
pip freeze > requirements.txt
To recreate this environment elsewhere:
cd my_flask_app
pyenv local my-flask-env # or recreate the env with pyenv virtualenv
pip install -r requirements.txt
From here, you run your Flask app in the usual way (flask run, python main.py, etc.), with pyenv-virtualenv ensuring the right env is active.
2.2 pyenv + Poetry (recommended for new projects)
Here we let:
This is a very modern and portable setup.
2.2.1 Ensure pyenv is set to the Python you want
pyenv install 3.11.9 # if not already installed
mkdir my_flask_app
cd my_flask_app
pyenv local 3.11.9
python -V # should be 3.11.9
2.2.2 Install pipx and Poetry (OS-specific)
Before installing Poetry, it helps to understand pipx:
You install pipx once per machine, then use it to install Poetry and other global CLI tools.
On macOS (Homebrew):
brew install pipx
pipx ensurepath
# restart shell (close and reopen your terminal)
pipx --version
pipx install poetry
On Ubuntu / WSL:
sudo apt install pipx
pipx ensurepath
# or, if apt doesn't have pipx:
python3 -m pip install --user pipx
python3 -m pipx ensurepath
# restart shell
pipx --version
pipx install poetry
Once this is done, you can run poetry from any terminal without activating a venv first.
2.2.3 Initialize Poetry in the project
From the project directory:
poetry init
Poetry walks you through prompts (name, version, description, Python range, etc.). A typical interactive session looks like this:
This command will guide you through creating your pyproject.toml config.
Package name [my-flask-app]: my_flask_app
Version [0.1.0]: 0.1.0
Description []: Simple Flask demo app
Author [Your Name <you@example.com>, n to skip]: Your Name <you@example.com>
License []: Proprietary (BLC)
Compatible Python versions [^3.11]: >=3.11,<4.0
Would you like to define your main dependencies interactively? (yes/no) [yes]: no
Would you like to define your development dependencies interactively? (yes/no) [yes]: no
What each of these means:
After you answer these prompts, Poetry writes a pyproject.toml file containing this metadata.
Because this app is not a published library package, you can optionally disable package mode (if you only want Poetry for env + dependencies):
[tool.poetry]
package-mode = false
This tells Poetry not to try to install a root package and instead just manage dependencies and the virtualenv.
2.2.4 Add Flask and other dependencies
poetry add Flask python-dotenv Flask-Mail
2.2.5 Install and run the app
For you or any collaborator:
poetry install
poetry run flask run
You can also open a subshell inside the env:
poetry shell
flask run
2.3 Poetry without pyenv
You don’t have to use pyenv. For small or throwaway projects, or when the system Python is already what you want, you can rely on:
2.3.1 Install Poetry via pipx as above
Follow the pipx + pipx install poetry steps from 2.2.2, but skip any pyenv commands.
2.3.2 Create and configure the project
mkdir my_flask_app
cd my_flask_app
poetry init
# answer prompts, set Python version like ">=3.11,<4.0"
Optionally, keep the virtualenv inside the project:
poetry config virtualenvs.in-project true
Tell Poetry which Python to use (usually just python3):
poetry env use python3
Then add dependencies and run just like in 2.2:
poetry add Flask python-dotenv Flask-Mail
poetry install
poetry run flask run
If later you decide to adopt pyenv, you can first set pyenv local ... in the project directory, then re-run poetry env use python3 to make Poetry’s env use that interpreter.
3. Scenario B – Existing Flask app using requirements.txt
Now consider a project you already have:
You want to move toward a more robust setup using pyenv and/or Poetry.
We’ll cover three paths:
3.1 Keep requirements.txt but use pyenv-virtualenv
This is a minimal change that improves Python version control without changing how you manage dependencies.
3.2 Migrate an existing app to Poetry + pyenv
This is the pattern you’re adopting in your BLC Flask apps.
# deactivate any old venv first
deactivate 2>/dev/null || true
pyenv install 3.11.9 # if needed
pyenv local 3.11.9
poetry init
Answer the prompts
[tool.poetry]
package-mode = false
so Poetry is used only for dependencies/env, not packaging.
3. Import dependencies from requirements.txt:
while read -r dep; do
poetry add "$dep"
done < requirements.txt
4. Install via Poetry using pyenv’s interpreter:
poetry env use python3 # this python is the one pyenv chose via pyenv local
poetry install
5. Run the app:
poetry run flask run
6. Optionally regenerate requirements.txt from Poetry:
poetry export -f requirements.txt -o requirements.txt --without-hashes
3.3 Migrate to Poetry without pyenv
If you don’t want to use pyenv on a particular machine (or can’t), you can still migrate the same app to Poetry using the system python3.
deactivate 2>/dev/null || true
poetry init
# answer prompts; set Python version like ">=3.11,<4.0"
4. Import dependencies from requirements.txt using the same while read -r dep; do ... loop.
5. Tell Poetry which Python interpreter to use (usually just python3):
poetry env use python3
poetry install
6. Run the app:
poetry run flask run
Later, if you adopt pyenv, you can set pyenv local in the project directory and re-run poetry env use python3 to switch Poetry’s env to that interpreter.
4. OS notes: macOS, Ubuntu, and Windows (WSL)
macOS
brew install pyenv pyenv-virtualenv pipx
pipx ensurepath
pipx install poetry
Ubuntu / Linux
sudo apt install python3 python3-venv python3-pip pipx
pipx ensurepath
pipx install poetry
# pyenv install per official docs (or your 01_new_python_version... note)
Windows
5. Choosing a default workflow for your Flask apps
For serious Flask work going forward, a solid, teachable default is:
A typical pattern you can reuse:
# One-time per machine
brew install pyenv pyenv-virtualenv pipx # macOS example
pipx ensurepath
pipx install poetry
# Per project
mkdir my_flask_app
cd my_flask_app
pyenv install 3.11.9 # if needed
pyenv local 3.11.9
poetry init # answer prompts, skip adding deps interactively
# (optionally add [tool.poetry] package-mode = false)
poetry add Flask python-dotenv Flask-Mail
poetry install
poetry run flask run
You can then adapt this same pattern to existing projects by importing from requirements.txt as shown above.
About Me: