Passing parameters into app environments
[[AppEnvironment]]s support various parameter types that can be passed at deployment time. This includes primitive values, files, directories, and delayed values like RunOutput and AppEndpoint.
Parameter types overview
There are several parameter types:
- Primitive values: Strings, numbers, booleans
- Files:
flyte.io.Fileobjects (useflyte.io.File.from_existing_remote(...)for remote files) - Directories:
flyte.io.Dirobjects (useflyte.io.Dir.from_existing_remote(...)for remote directories) - Delayed values:
RunOutput(from task runs) orAppEndpoint(inject endpoint urls of other apps)
Basic parameter types
# String parameters
app_env = flyte.app.AppEnvironment(
name="configurable-app",
parameters=[
Parameter(name="environment", value="production"),
Parameter(name="log_level", value="INFO"),
],
# ...
)
# File parameters
app_env2 = flyte.app.AppEnvironment(
name="app-with-model",
parameters=[
Parameter(
name="model_file",
value=flyte.io.File.from_existing_remote("s3://bucket/models/model.pkl"),
mount="/app/models/",
),
],
# ...
)
# Directory parameters
app_env3 = flyte.app.AppEnvironment(
name="app-with-data",
parameters=[
Parameter(
name="data_dir",
value=flyte.io.Dir.from_existing_remote("s3://bucket/data/"),
mount="/app/data",
),
],
# ...
)
Do not use flyte.io.File(...) or flyte.io.Dir(...) directly as parameter values.
Use flyte.io.File.from_existing_remote(...) and flyte.io.Dir.from_existing_remote(...) to
reference existing remote files and directories.
Accessing parameters in your app
There are three ways to access parameter values at runtime: mount paths, environment variables, and the get_parameter helper function.
mount
When mount is specified on a Parameter, the file or directory is downloaded to the given path.
If the mount path ends with a /, the file is placed inside that directory with its original
name. Otherwise, the file is downloaded to the exact path specified.
Your app reads directly from the mount path:
with open("/tmp/data_file.txt", "rb") as fh:
contents = fh.read()env_var
When env_var is specified, an environment variable is set containing the path to the downloaded
file or directory. This is useful when you want your app code to be decoupled from specific mount
paths:
import os
data_path = os.environ["DATA_FILE_PATH"]
with open(data_path, "rb") as fh:
contents = fh.read()get_parameter
The get_parameter(name) helper function from flyte.app returns the path to the downloaded
file or the string value of the parameter. This works regardless of whether mount or env_var
are specified and is the most flexible access method:
from flyte.app import get_parameter
data_path = get_parameter("data")
with open(data_path, "rb") as fh:
contents = fh.read()When a parameter has no mount or env_var configured, get_parameter is the only way to
access its value at runtime.
Full example
The following example defines a FastAPI app with parameters using all three access methods:
from fastapi import FastAPI
from flyte.app.extras import FastAPIAppEnvironment
DATA_MOUNT_PATH = "/tmp/data_file.txt"
DATA_ENV_VAR = "DATA_FILE_PATH"
demo_app = FastAPI()
demo_env = FastAPIAppEnvironment(
name="parameter-access-demo",
app=demo_app,
parameters=[
# With mount and env_var: the file is downloaded to the mount path,
# and an environment variable is set to point to the file location.
Parameter(
name="data",
type="file",
mount=DATA_MOUNT_PATH,
env_var=DATA_ENV_VAR,
),
# With no mount or env_var: accessible only via get_parameter,
# which returns the path to the downloaded file.
Parameter(name="data_raw", type="file"),
],
# ...
)
@demo_app.get("/from-mount")
def read_from_mount() -> dict:
"""Access the file directly at the mount path."""
with open(DATA_MOUNT_PATH, "rb") as fh:
return {"contents": fh.read().decode()}
@demo_app.get("/from-env-var")
def read_from_env_var() -> dict:
"""Access the file through its environment variable."""
data_path = os.environ[DATA_ENV_VAR]
with open(data_path, "rb") as fh:
return {"contents": fh.read().decode()}
@demo_app.get("/from-get-parameter")
def read_from_get_parameter() -> dict:
"""Access the file using the get_parameter helper."""
data_path = get_parameter("data")
with open(data_path, "rb") as fh:
return {"contents": fh.read().decode()}
@demo_app.get("/raw")
def read_raw() -> dict:
"""Access a parameter that has no mount or env_var."""
data_path = get_parameter("data_raw")
with open(data_path, "rb") as fh:
return {"contents": fh.read().decode()}
Delayed values
Delayed values are parameters whose actual values are materialized at deployment time.
RunOutput
Use RunOutput to pass outputs from task runs as app parameters:
env = flyte.TaskEnvironment(name="training-env")
@env.task
async def train_model() -> flyte.io.File:
# ... training logic ...
return await flyte.io.File.from_local("/tmp/trained-model.pkl")
app_env4 = flyte.app.AppEnvironment(
name="serving-app",
parameters=[
Parameter(
name="model",
value=flyte.app.RunOutput(type="file", task_name="training-env.train_model"),
mount="/app/model",
),
],
# ...
)
The type argument is required and must be one of string, file, or directory.
When the app is deployed, it will make the remote calls needed to figure out the
actual value of the parameter.
AppEndpoint
Use AppEndpoint to pass endpoints from other apps:
app1_env = flyte.app.AppEnvironment(name="backend-api")
app2_env = flyte.app.AppEnvironment(
name="frontend-app",
parameters=[
Parameter(
name="backend_url",
value=flyte.app.AppEndpoint(app_name="backend-api"),
env_var="BACKEND_URL",
),
],
# ...
)
The endpoint URL will be injected as the parameter value when the app starts.
This is particularly useful when you want to chain apps together (for example, a frontend app calling a backend app), without hardcoding URLs.
Overriding parameters at serve time
You can override parameter values when serving apps using parameter_values in
flyte.with_servecontext. File and directory values must use from_existing_remote:
if __name__ == "__main__":
import logging
flyte.init_from_config(log_level=logging.DEBUG)
app_handle = flyte.with_servecontext(
parameter_values={
demo_env.name: {
"data": flyte.io.File.from_existing_remote("s3://bucket/data.txt"),
"data_raw": flyte.io.File.from_existing_remote("s3://bucket/raw.txt"),
}
}
).serve(demo_env)
print(f"Deployed app: {app_handle.url}")
Parameter overrides are only available when using flyte.with_servecontext().serve().
The flyte.deploy() function does not support parameter overrides — parameters must be specified in the AppEnvironment definition.
This is useful for:
- Testing different configurations during development
- Using different models or data sources for testing
- A/B testing different app configurations
Example: FastAPI app with configurable model
Here’s a complete example showing how to use parameters in a FastAPI app:
# /// script
# requires-python = "==3.13"
# dependencies = [
# "fastapi",
# "uvicorn",
# "joblib",
# "scikit-learn",
# "flyte>=2.0.0b52",
# ]
# ///
from contextlib import asynccontextmanager
from pathlib import Path
import flyte
import flyte.app
import flyte.io
from flyte.app.extras import FastAPIAppEnvironment
from fastapi import FastAPI
image = flyte.Image.from_uv_script(__file__, name="app-parameters-fastapi-example")
task_env = flyte.TaskEnvironment(
name="model_serving_task",
image=image,
resources=flyte.Resources(cpu=2, memory="1Gi"),
cache="auto",
)
@task_env.task
async def train_model_task() -> flyte.io.File:
"""Train a model and return it."""
import joblib
import sklearn.ensemble
import sklearn.datasets
X, y = sklearn.datasets.make_classification(n_samples=1000, n_features=5, n_classes=2, random_state=42)
model = sklearn.ensemble.RandomForestClassifier()
model.fit(X, y)
model_dir = Path("/tmp/model")
model_dir.mkdir(parents=True, exist_ok=True)
model_path = model_dir / "model.joblib"
joblib.dump(model, model_path)
return await flyte.io.File.from_local(model_path)
state = {}
@asynccontextmanager
async def lifespan(app: FastAPI):
import joblib
model = joblib.load("/root/models/model.joblib")
state["model"] = model
yield
app = FastAPI(lifespan=lifespan)
app_env = FastAPIAppEnvironment(
name="model-serving-api",
app=app,
parameters=[
flyte.app.Parameter(
name="model_file",
# this is a placeholder
value=flyte.io.File.from_existing_remote("s3://bucket/models/default.pkl"),
mount="/root/models/",
download=True,
),
],
image=image,
resources=flyte.Resources(cpu=2, memory="2Gi"),
requires_auth=False,
)
@app.post("/predict")
async def predict(data: list[float]) -> dict[str, list[float]]:
model = state["model"]
return {"prediction": model.predict([data]).tolist()}
if __name__ == "__main__":
import logging
flyte.init_from_config(log_level=logging.DEBUG)
run = flyte.run(train_model_task)
print(f"Run: {run.url}")
run.wait()
model_file = run.outputs()[0]
print(f"Model file: {model_file.path}")
app = flyte.with_servecontext(
parameter_values={
"model-serving-api": {
"model_file": flyte.io.File.from_existing_remote(model_file.path)
}
}
).serve(app_env)
print(f"API URL: {app.url}")
Example: Using RunOutput for model serving
import joblib
from sklearn.ensemble import RandomForestClassifier
training_env = flyte.TaskEnvironment(name="training-env")
@training_env.task
async def train_model_task() -> flyte.io.File:
"""Train a model and return it."""
model = RandomForestClassifier()
# ... training logic ...
path = "./trained-model.pkl"
joblib.dump(model, path)
return await flyte.io.File.from_local(path)
serving_app = FastAPI()
model_serving_env = FastAPIAppEnvironment(
name="model-serving-app",
app=serving_app,
parameters=[
Parameter(
name="model",
value=flyte.app.RunOutput(
type="file",
task_name="training-env.train_model_task",
),
mount="/app/model",
env_var="MODEL_PATH",
),
],
)
Best practices
- Use
from_existing_remote: Always useflyte.io.File.from_existing_remote(...)orflyte.io.Dir.from_existing_remote(...)to reference remote files and directories as parameter values. - Use delayed parameters: Leverage
RunOutputandAppEndpointto create app dependencies between tasks and apps, or app-to-app chains. - Override for testing: Use the
parameter_valuesargument inflyte.with_servecontext()to test different configurations without changing code. - Mount paths clearly: Use descriptive mount paths for file/directory parameters so your app code is easy to understand.
- Use environment variables: For paths that your app needs to reference dynamically, use
env_varto inject values as environment variables. - Use
get_parameterfor flexibility: When you want to keep your parameter access decoupled from specific mount paths or env var names, useget_parameter(name).
Limitations
- Large files/directories can slow down app startup.
- Parameter overrides are only available when using
flyte.with_servecontext(...).serve(...).