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.File objects (use flyte.io.File.from_existing_remote(...) for remote files)
  • Directories: flyte.io.Dir objects (use flyte.io.Dir.from_existing_remote(...) for remote directories)
  • Delayed values: RunOutput (from task runs) or AppEndpoint (inject endpoint urls of other apps)

Basic parameter types

passing-parameters-examples.py
# 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:

passing-parameters-examples.py
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:

passing-parameters-examples.py
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:

passing-parameters-examples.py
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:

passing-parameters-examples.py
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:

app-parameters-fastapi-example.py
# /// 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

passing-parameters-examples.py
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

  1. Use from_existing_remote: Always use flyte.io.File.from_existing_remote(...) or flyte.io.Dir.from_existing_remote(...) to reference remote files and directories as parameter values.
  2. Use delayed parameters: Leverage RunOutput and AppEndpoint to create app dependencies between tasks and apps, or app-to-app chains.
  3. Override for testing: Use the parameter_values argument in flyte.with_servecontext() to test different configurations without changing code.
  4. Mount paths clearly: Use descriptive mount paths for file/directory parameters so your app code is easy to understand.
  5. Use environment variables: For paths that your app needs to reference dynamically, use env_var to inject values as environment variables.
  6. Use get_parameter for flexibility: When you want to keep your parameter access decoupled from specific mount paths or env var names, use get_parameter(name).

Limitations

  • Large files/directories can slow down app startup.
  • Parameter overrides are only available when using flyte.with_servecontext(...).serve(...).