Python Environments#
This guide explains how bfabric-app-runner manages Python virtual environments for the python_env command type.
Overview#
When a command uses type: python_env, the app runner automatically creates and manages a Python virtual environment with the specified dependencies. Two strategies are available:
Cached environments: Persistent, reused across runs for the same dependency set.
Ephemeral environments: Temporary, created fresh each run and cleaned up afterward.
Cached Environments#
By default, python_env commands use cached environments. The environment is identified by a hash computed from:
Hostname
Python version
Absolute path to the pylock file
Modification time of the pylock file
Absolute paths of any local extra dependencies (if present)
If an environment with the same hash already exists and is fully provisioned, it is reused without reinstalling dependencies.
Cache Location#
Environments are stored under:
$XDG_CACHE_HOME/bfabric_app_runner/envs/<environment_hash>
If XDG_CACHE_HOME is not set, it defaults to ~/.cache/bfabric_app_runner/envs/.
Concurrency Safety#
File locking (<env_path>.lock) prevents multiple processes from provisioning the same cached environment concurrently. A .provisioned marker file inside the environment directory indicates that provisioning completed successfully. Partially provisioned environments are not used.
Ephemeral Environments#
When refresh: true is set on a command, an ephemeral environment is created:
$XDG_CACHE_HOME/bfabric_app_runner/ephemeral/env_<random_suffix>
Ephemeral environments are always provisioned from scratch and automatically cleaned up after the command finishes. This is useful for:
Development, where source code changes frequently.
Testing, where a clean environment is needed each time.
Debugging dependency issues.
Provisioning Process#
Whether cached or ephemeral, provisioning follows the same steps:
Create virtual environment:
uv venvwith the specified Python version.Install dependencies:
uv pip installfrom the pylock file.Install local extras: If
local_extra_depsare specified, install them with--no-deps.Mark as provisioned: Create a
.provisionedmarker file.
Note
Local extra dependencies (wheels or source paths) are installed with --no-deps because all dependencies are already covered by the pylock file. This ensures the wheel provides only the application code.
Environment Structure#
A provisioned environment contains:
<env_path>/
bin/
python # Python executable
... # Other installed scripts
lib/
...
.provisioned # Marker indicating successful provisioning
Path Management#
When a python_env command executes:
The environment’s
bin/directory is prepended toPATH.Any
prepend_pathsspecified in the command are also prepended.Environment variables from the
envfield are set.The command is executed using the environment’s Python interpreter.
Configuration Example#
commands:
process:
type: python_env
pylock: /deploy/my_app/pylock.toml
command: -m my_app.process
python_version: "3.12"
local_extra_deps:
- /deploy/my_app/my_app-1.0.0-py3-none-any.whl
env:
DATA_PATH: /data
prepend_paths:
- /opt/tools/bin
refresh: false # Use cached environment (default)
When to Use Refresh Mode#
Scenario |
|
Rationale |
|---|---|---|
Production deployment |
|
Stable, cached environment for speed |
Active development |
|
Pick up source code changes immediately |
Debugging dependency issues |
|
Start fresh to isolate problems |
CI/CD testing |
|
Ensure clean environment each run |