Debian 13 Server: Pyenv, FastApi and Virtual Environments
This post describes how to set up a Debian 13 machine to run FastAPI applications with option to have different Python versions and virtual Python environments. This post does NOT contain detailed step-by-step instructions for the setup, only the parts that I consider important to document.
Server target configuration:
- Should support a number of websites that will be hosting FastAPI applications.
- Each application should be able to use it's own Python version and packages.
- The system state should be as close to the stock one as possible.
- There should be NO non-system files in folders where system files are present.
- Exceptions like website configurations and systemd services definitions are acceptable.
- There should be only one system package per function (e.g. no two web servers doing the same).
- There should be NO non-system files in folders where system files are present.
Location
All files will reside in /srv according to Filesystem Hierarchy Standard:
/srv/pyenv # Home for Pyenv
/srv/systemd-services # Systemd scripts
/srv/apache2-configs # Apache config files
/srv/example.com/ # Container for the domain
/srv/example.com/venv # Root domain Python virtual environment
/srv/example.com/backend # Root domain backend
/srv/example.com/frontend # Root domain frontend
/srv/example.com/subdomain1/ # Container for the subdomain
/srv/example.com/subdomain1/venv # Subdomain Python virtual environment
/srv/example.com/subdomain1/backend # Subdomain backend
/srv/example.com/subdomain1/frontend # Subdomain frontend
...
Pyenv Setup
Debian has a package for Pyenv:
sudo apt-get install pyenv
I could not find any guides on Pyenv setup for server environment, so we need to adapt the regular approach to let admin and www-data users to use it.
As Pyenv will be used at least by 2 users, it makes sense to add it's Bash configuration to the end of /etc/profile to be available system-wide:
# Using 'tee' because the redirection >> is performed by the user’s shell (that is not root)
echo '
# Pyenv configuration
export PYENV_ROOT="/srv/pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
' | sudo tee --append /etc/profile
You will see the effect after you login back to the machine.
However, users without shell like www-data will not receive the configuration in the above way. We will setup www-data to use the custom Python in the systemd file below.
# Setup custom Python version
cd /srv/example.com/subdomain1/
pyenv install 3.12
pyenv local 3.12
Virtual Environments
pyenv virtualenv is not a part of Debian pyenv and we will use standard Python venv:
# Setup virtual environment
python -m venv /srv/example.com/subdomain1/venv
source /srv/example.com/subdomain1/venv/bin/activate
Systemd Services
To use Python from the virtual environment we will invoke it directly:
# /srv/systemd-services/uvicorn-subdomain1.service
[Unit]
Description=FastAPI / Uvicorn – Subdomain1
After=network.target
[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/srv/example.com/subdomain1/backend/current
Environment=PATH=/srv/example.com/subdomain1/venv/bin:$PATH
ExecStart=/srv/example.com/subdomain1/venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8000
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
Apache Configs
Apache looks for domain names in the order of appearance, so if it loads the top domain configuration first, the subdomains will not receive traffic.