Programmatic Deployment API Reference
Programmatic Deployer API
Class: AppDeployer
AppDeployer(
name: Optional[str] = None,
port: Optional[int] = None,
description: Optional[str] = None,
app_type: Optional[str] = None,
image: Optional[str] = None,
tags: Optional[list] = None,
secrets: Optional[list] = None,
compute_pools: Optional[list] = None,
environment: Optional[dict] = None,
commands: Optional[list] = None,
resources: Optional[ResourceConfigDict] = None,
auth: Optional[AuthConfigDict] = None,
replicas: Optional[ReplicaConfigDict] = None,
code_package: Optional[tuple] = None,
force_upgrade: Optional[bool] = None,
persistence: Optional[str] = None,
project: Optional[str] = None,
branch: Optional[str] = None,
models: Optional[list] = None,
data: Optional[list] = None,
generate_static_url: Optional[bool] = None,
**kwargs
)
Module: metaflow.apps
Programmatic API For deploying Outerbounds Apps.
Parameters
- name (str, optional): The name of the app to deploy.
- port (int, optional): Port where the app is hosted. When deployed this will be port on which we will deploy the app.
- description (str, optional): The description of the app to deploy.
- app_type (str, optional): The User defined type of app to deploy. Its only used for bookkeeping purposes.
- image (str, optional): The Docker image to deploy with the App.
- tags (list, optional): The tags of the app to deploy.
- secrets (list, optional): Outerbounds integrations to attach to the app. You can use the value you set in the
@secretsdecorator in your code. - compute_pools (list, optional): A list of compute pools to deploy the app to.
- environment (dict, optional): Environment variables to deploy with the App.
- commands (list, optional): A list of commands to run the app with.
- resources (ResourceConfigDict, optional): Resource configuration for the app.
- cpu (str) CPU requests
- memory (str) Memory requests
- gpu (str) GPU requests
- disk (str) Storage disk size.
- shared_memory (str) Shared memory
- auth (AuthConfigDict, optional): Auth related configurations.
- type (str) The type of authentication to use for the app.
- public (bool) Whether the app is public or not.
- replicas (ReplicaConfigDict, optional): The number of replicas to deploy the app with.
- fixed (int) The fixed number of replicas to deploy the app with. If min and max are set, this will raise an error.
- min (int) The minimum number of replicas to deploy the app with.
- max (int) The maximum number of replicas to deploy the app with.
- scaling_policy (ScalingPolicyConfigDict)
Scaling policy defines the the metric based on which the replicas will horizontally scale. If min and max replicas are set and are not the same, then a scaling policy will be applied. Default scaling policies can be 60 rpm (ie 1 rps).
- rpm (int) Scale up replicas when the requests per minute crosses this threshold. If nothing is provided and the replicas.max and replicas.min is set then the default rpm would be 60.
- code_package (tuple, optional): Pre-packaged code from package_code(). A PackagedCode namedtuple containing url and key.
- force_upgrade (bool, optional): Force upgrade the app even if it is currently being upgraded.
- persistence (str, optional): The persistence mode to deploy the app with. [Experimental] May change in the future.
- project (str, optional): The project name to deploy the app to. [Experimental] May change in the future.
- branch (str, optional): The branch name to deploy the app to. [Experimental] May change in the future.
- models (list, optional): [Experimental] May change in the future.
- data (list, optional): [Experimental] May change in the future.
- generate_static_url (bool, optional): Generate a static URL for the app based on its name.
Examples
Basic deployment with bake_image and package_code:
from metaflow.apps import bake_image, package_code, AppDeployer
# Step 1: Bake dependencies into an image
baked = bake_image(pypi={"flask": ">=2.0", "requests": ""})
# Step 2: Package your application code
pkg = package_code(src_paths=["./src"])
# Step 3: Create deployer and deploy
deployer = AppDeployer(
name="my-flask-app",
port=8000,
image=baked.image,
code_package=pkg,
commands=["python server.py"],
replicas={"min": 1, "max": 3},
resources={"cpu": "1", "memory": "2048Mi"},
)
deployed = deployer.deploy()
print(deployed.public_url)
Deployment with API authentication:
deployer = AppDeployer(
name="my-api",
port=8000,
image=baked.image,
code_package=pkg,
commands=["python api.py"],
auth={"type": "API"},
)
deployed = deployer.deploy()
Deployment with environment variables and secrets:
deployer = AppDeployer(
name="my-app",
port=8000,
image=baked.image,
code_package=pkg,
commands=["python app.py"],
environment={"DEBUG": "false", "LOG_LEVEL": "info"},
secrets=["my-api-keys"],
)
deployed = deployer.deploy()
Interacting with a deployed app:
# Get app info
info = deployed.info()
# Get logs from all workers
logs = deployed.logs()
# Scale to zero workers
deployed.scale_to_zero()
# Delete the app
deployed.delete()
Method: AppDeployer.deploy
AppDeployer.deploy(
readiness_condition: str = 'at_least_one_running',
max_wait_time = 600,
readiness_wait_time = 10,
logger_fn: Callable = functools.partial(<built-in function print>, fi...,
**kwargs
)
Deploy the app to the Outerbounds Platform.
This method packages and deploys the configured app, waiting for it to reach the specified readiness condition before returning.
Parameters
- readiness_condition (str, optional): The condition that must be met for the deployment to be considered ready. Default is ATLEAST_ONE_RUNNING.
Deployment ready conditions define what is considered a successful completion of the current deployment instance. This allows users or platform designers to configure the criteria for deployment readiness.
Why do we need deployment readiness conditions?
- Deployments might be taking place from a CI/CD-esque environment.
In these setups, the downstream build triggers might be depending on
a specific criteria for deployment completion. Having readiness conditions
allows the CI/CD systems to get a signal of when the deployment is ready.
- Users might be calling the deployment API under different conditions:
- Some users might want a cluster of workers ready before serving
traffic while others might want just one worker ready to start
serving traffic.
Available readiness conditions:
ATLEAST_ONE_RUNNING ("at_least_one_running") At least min(min_replicas, 1) workers of the current deployment instance's version have started running. Usecase: Some endpoints may be deployed ephemerally and are considered ready when at least one instance is running; additional instances are for load management.
ALL_RUNNING ("all_running") At least min_replicas number of workers are running for the deployment to be considered ready. Usecase: Operators may require that all replicas are available before traffic is routed. Needed when inference endpoints may be under some SLA or require a larger load.
FULLY_FINISHED ("fully_finished") At least min_replicas number of workers are running for the deployment and there are no pending or crashlooping workers from previous versions lying around. Usecase: Ensuring endpoint is fully available and no other versions are running or endpoint has been fully scaled down.
ASYNC ("async") The deployment will be assumed ready as soon as the server acknowledges it has registered the app in the backend. Usecase: Operators may only care that the URL is minted for the deployment or the operator wants the deployment to eventually scale down to 0.
- max_wait_time (int, optional): Maximum time in seconds to wait for the deployment to reach readiness. Default is 600 (10 minutes).
- readiness_wait_time (int, optional): Time in seconds to wait between readiness checks. Default is 10.
- logger_fn (Callable, optional): Function to use for logging progress messages. Default prints to stderr.
Returns
- *** (DeployedApp*): An object representing the deployed app with methods to interact with it (logs, info, scale_to_zero, delete, etc.) and properties like public_url.
Raises
- *** (CodePackagingException*): If code_package is not provided or is not a valid PackagedCode instance.
- *** (AppConfigError*): If the app configuration is invalid.
- *** (AppCreationFailedException*): If the app deployment submission fails due to an API error. Contains status_code and error_text attributes for debugging.
- *** (AppCrashLoopException*): If a worker enters CrashLoopBackOff or Failed state during deployment. Contains worker_id and logs attributes for debugging.
- *** (AppReadinessException*): If the app fails to meet readiness conditions within max_wait_time.
- *** (AppUpgradeInProgressException*): If an upgrade is already in progress when deployment starts. Use force_upgrade=True to override. Contains upgrader attribute.
- *** (AppConcurrentUpgradeException*): If another deployment was triggered while this deployment was in progress, invalidating the current deployment. Contains expected_version and actual_version.
- *** (OuterboundsBackendUnhealthyException*): If the Outerbounds backend is unreachable (network issues, DNS failures) or returns server errors (HTTP 5xx). This indicates a platform-side issue, not a problem with your configuration. Retry the deployment or contact Outerbounds support.
- *** (AppDeletedDuringDeploymentException*): If the app was deleted by another process or user while this deployment was in progress. This can occur when concurrent operations conflict.
Examples
Basic deployment:
from metaflow.apps import bake_image, package_code, AppDeployer
baked = bake_image(pypi={"flask": ">=2.0"})
pkg = package_code(src_paths=["./src"])
deployer = AppDeployer(
name="my-app",
port=8000,
image=baked.image,
code_package=pkg,
commands=["python server.py"],
)
deployed = deployer.deploy()
print(deployed.public_url)
Wait for all replicas to be ready:
deployed = deployer.deploy(
readiness_condition="all_running"
)
Async deployment (don't wait for workers):
deployed = deployer.deploy(
readiness_condition="async"
)
Handling deployment errors:
from metaflow.apps import AppDeployer
from metaflow.apps.exceptions import (
AppReadinessException,
)
try:
deployed = deployer.deploy()
except AppReadinessException as e:
print(f"App {e.app_id} failed to become ready in time but we can move forward")
deployed_app:DeployedApp = e.deployed_app
# use DeployedApp to do what ever you need
Method: AppDeployer.list_deployments
AppDeployer.list_deployments(
name: str = None,
project: str = None,
branch: str = None,
tags: List[Dict[str, str]] = None
)
List deployed apps, optionally filtered by name, project, branch, or tags.
Parameters
- name (str, optional): Filter by app name.
- project (str, optional): Filter by project name.
- branch (str, optional): Filter by branch name.
- tags (List[Dict[str, str]], optional): Filter by tags. Each tag is a dict with a single key-value pair, e.g., [{"env": "prod"}] or [{"team": "ml"}, {"version": "v2"}]. Apps must have all specified tags to match.
Returns
- *** (List[DeployedApp]*): List of deployed apps matching the filters.
Examples
List all apps:
apps = AppDeployer.list_deployments()
Filter by name:
apps = AppDeployer.list_deployments(name="my-app")
Filter by project and branch:
apps = AppDeployer.list_deployments(project="ml-pipeline", branch="main")
Filter by a single tag:
apps = AppDeployer.list_deployments(tags=[{"env": "prod"}])
Filter by multiple tags (AND logic - must match all):
apps = AppDeployer.list_deployments(tags=[{"env": "prod"}, {"team": "ml"}])
Combine filters:
apps = AppDeployer.list_deployments(
project="recommendations",
tags=[{"env": "staging"}]
)
Class: DeployedApp
Module: metaflow.apps
A deployed app on the Outerbounds Platform.
Obtain instances via AppDeployer.deploy() or AppDeployer.list_deployments().
Examples
After deployment:
deployed = deployer.deploy()
print(deployed.public_url)
After listing:
apps = AppDeployer.list_deployments(tags=[{"env": "staging"}])
for app in apps:
print(f"{app.name}: {app.public_url}")
Inspect and manage:
# Get logs
for worker_id, lines in deployed.logs().items():
print(f"Worker {worker_id}: {len(lines)} log lines")
# Scale down
deployed.scale_to_zero()
# Clean up
deployed.delete()
Make authenticated requests (API auth):
import requests
response = requests.get(deployed.public_url, headers=deployed.auth())
Property: DeployedApp.id
Unique identifier for the deployed app.
Returns
- str: The unique app identifier assigned by the platform.
Property: DeployedApp.auth_type
Authentication type configured for this app. Can be either Browser , API, BrowserAndApi
Returns
- str: The authentication type
Property: DeployedApp.public_url
Public URL to access the deployed app.
Returns
- str: The publicly accessible URL for this app.
Property: DeployedApp.internal_url
Internal in-cluster URL to access the deployed app.
This URL bypasses external network routing and can be used from within Metaflow tasks running on Kubernetes. Authentication headers are not required when accessing the app via this URL from within the cluster.
Returns
- str: The in-cluster URL for this app.
Property: DeployedApp.name
Logical name given to the app.
Returns
- str: The human-readable name of the app.
Property: DeployedApp.deployed_version
Current deployment version of the app.
Returns
- str: The version identifier for the current deployment.
Property: DeployedApp.tags
Tags associated with this app.
Returns
- List[str]: List of tags assigned to this app.
Method: DeployedApp.info
DeployedApp.info()
Get detailed information about the deployed app.
Returns
- *** (dict*): Dictionary containing full app details including spec, status, metadata, and configuration.
Examples
info = deployed.info()
print(f"Status: {info.get('status')}")
print(f"Spec: {info.get('spec')}")
Method: DeployedApp.logs
DeployedApp.logs(previous: bool = False)
Get logs from all worker replicas.
Parameters
- previous (bool, optional): If True, returns logs from the previous execution of workers. Useful for debugging crashlooping workers. Default is False.
Returns
- *** (Dict[str, List[LogLine]]*): Dictionary mapping worker IDs to their log lines.
Examples
# Get current logs
logs = deployed.logs()
for worker_id, lines in logs.items():
print(f"Worker {worker_id}:")
for line in lines:
print(f" {line}")
# Get logs from crashed workers
previous_logs = deployed.logs(previous=True)
Method: DeployedApp.replicas
DeployedApp.replicas()
List all active worker replicas for this app.
Returns
- *** (List[dict]*): List of dictionaries containing worker information including workerId, status, and other metadata.
Examples
workers = deployed.replicas()
for worker in workers:
print(f"Worker {worker['workerId']}: {worker.get('status')}")
Method: DeployedApp.scale_to_zero
DeployedApp.scale_to_zero()
Scale the app down to zero replicas.
This stops all running workers while preserving the app configuration. The app can be scaled back up by sending traffic to the public URL (if autoscaling is configured) or by redeploying.
Examples
# Scale down to save resources
deployed.scale_to_zero()
Method: DeployedApp.delete
DeployedApp.delete()
Delete the deployed app.
This permanently removes the app from the platform, including all workers, configuration, and the public URL. This action cannot be undone.
Examples
# Clean up the app
deployed.delete()
Method: DeployedApp.auth
DeployedApp.auth()
Get authentication headers for making requests to this app.
Only available for apps configured with API authentication type. Use these headers when making HTTP requests to the app's public URL.
Returns
- *** (dict*): Dictionary of HTTP headers to include in requests.
Raises
- *** (ValueError*): If the app is not configured with API authentication.
Examples
import requests
response = requests.get(deployed.public_url, headers=deployed.auth())
Exceptions
Class: AppDeploymentException
Module: metaflow.apps.exceptions
Base exception for app deployment failures that occur after submission.
All deployment exceptions provide a deployed_app property that returns
a DeployedApp object, allowing you to inspect logs or app state even
after a failure.
Attributes
- args:
- deployed_app: Returns a
DeployedAppobject for the failed deployment. Use this to inspect logs, replica status, or other details after catching the exception. For example:e.deployed_app.logs()to fetch recent logs.
Class: AppCrashLoopException
Module: metaflow.apps.exceptions
Raised when an app worker crashes repeatedly during startup.
The logs attribute contains recent log lines from the failing worker,
which typically reveal the cause (e.g., import errors, missing dependencies,
or application exceptions). The worker_id identifies which replica failed.
Attributes
- args:
- deployed_app: Returns a
DeployedAppobject for the failed deployment. Use this to inspect logs, replica status, or other details after catching the exception. For example:e.deployed_app.logs()to fetch recent logs.
Class: AppReadinessException
Module: metaflow.apps.exceptions
Raised when the app does not become ready within max_wait_time.
This typically means workers are still starting up or stuck in a pending state.
Use deployed_app.logs() or deployed_app.replicas() to investigate.
Consider increasing max_wait_time if your app has a slow startup.
Attributes
- args:
- deployed_app: Returns a
DeployedAppobject for the failed deployment. Use this to inspect logs, replica status, or other details after catching the exception. For example:e.deployed_app.logs()to fetch recent logs.
Class: AppConcurrentUpgradeException
Module: metaflow.apps.exceptions
Raised when another deployment started while this one was in progress.
The current deployment has been invalidated because someone else deployed
a new version. Check modified_by to see who triggered the conflicting
deployment. Use unique app names or coordinate deployments to avoid this.
Attributes
- args:
- deployed_app: Returns a
DeployedAppobject for the failed deployment. Use this to inspect logs, replica status, or other details after catching the exception. For example:e.deployed_app.logs()to fetch recent logs.
Class: AppUpgradeInProgressException
Module: metaflow.apps.exceptions
Raised when another deployment to this app is already running.
This prevents conflicting concurrent deployments. Either wait for the
existing deployment to complete, or use force_upgrade=True to take over.
Attributes
- args:
- deployed_app: Returns a
DeployedAppobject for the failed deployment. Use this to inspect logs, replica status, or other details after catching the exception. For example:e.deployed_app.logs()to fetch recent logs.
Class: AppCreationFailedException
Module: metaflow.apps.exceptions
Raised when the platform rejects an app deployment request.
Common causes include invalid configuration, quota limits, or permission issues.
Check status_code and error_text for details on why the request was rejected.
Attributes
- args:
Class: AppNotFoundException
Module: metaflow.apps.exceptions
Raised when attempting to access an app that does not exist.
This can occur when calling methods on DeployedApp for an app that
has been deleted or never existed.
Attributes
- args:
Class: OuterboundsBackendUnhealthyException
Module: metaflow.apps.exceptions
Raised when the Outerbounds platform returns 5xx errors or is unreachable.
Catch this to handle temporary platform outages gracefully. The request can typically be retried after a short delay.
Attributes
- args:
Programmatic Deployer Utilities
Function: bake_image
bake_image(
pypi: Optional[Dict[str, str]] = None,
conda: Optional[Dict[str, str]] = None,
requirements_file: Optional[str] = None,
pyproject_toml: Optional[str] = None,
base_image: Optional[str] = None,
python: Optional[str] = None,
logger: Optional[Callable[[str], Any]] = None,
cache_name: Optional[str] = None
)
Module: metaflow.apps
Bake a Docker image with the specified dependencies.
This is a composable building block that can be used standalone or combined with AppDeployer to deploy apps with custom images.
Parameters
- pypi (Dict[str, str], optional): Dictionary of PyPI packages to install. Keys are package names, values are version specifiers. Example: {"flask": ">=2.0", "requests": ""} Mutually exclusive with requirements_file and pyproject_toml.
- conda (Dict[str, str], optional): Dictionary of Conda packages to install.
- requirements_file (str, optional): Path to a requirements.txt file. Mutually exclusive with pypi and pyproject_toml.
- pyproject_toml (str, optional): Path to a pyproject.toml file. Mutually exclusive with pypi and requirements_file.
- base_image (str, optional): Base Docker image to build from. Defaults to the platform default image.
- python (str, optional): Python version to use (e.g., "3.11.0"). If None (default), uses the Python already present in the base_image and installs dependencies into it. If a version is specified, a new Python environment at that version is created inside the base image, and all dependencies are installed into it.
- logger (Callable, optional): Logger function for progress messages.
Returns
- *** (BakedImage*): Named tuple containing:
- image: The baked Docker image URL
- python_path: Path to Python executable in the image
Raises
- *** (ImageBakingException*): If baking fails or if invalid parameters are provided.
Examples
Bake with PyPI packages:
result = bake_image(pypi={"flask": ">=2.0", "requests": ""})
print(result.image)
Bake from requirements.txt:
result = bake_image(requirements_file="./requirements.txt")
Bake from pyproject.toml:
result = bake_image(pyproject_toml="./pyproject.toml")
Combine with AppDeployer:
from metaflow.apps import bake_image, AppDeployer
baked = bake_image(pypi={"flask": ">=2.0"})
deployer = AppDeployer(name="my-app", port=8080, image=baked.image)
deployed = deployer.deploy()
Function: package_code
package_code(
src_paths: List[str],
suffixes: Optional[List[str]] = None,
logger: Optional[Callable[[str], Any]] = None
)
Module: metaflow.apps
Package code for deployment to the Outerbounds Platform.
This is a composable building block that can be used standalone or combined with AppDeployer to deploy apps with custom code packages.
Parameters
- src_paths (List[str]): List of directories to include in the package. All paths must exist and be directories.
- suffixes (List[str], optional): File extensions to include (e.g., [".py", ".json", ".yaml"]). If None, uses default suffixes: .py, .txt, .yaml, .yml, .json, .html, .css, .js, .jsx, .ts, .tsx, .md, .rst
- logger (Callable, optional): Logger function for progress messages. Receives a single string argument.
Returns
- *** (PackagedCode*): Named tuple containing:
- url: The package URL in object storage
- key: Unique content-addressed key identifying this package
Raises
- *** (CodePackagingException*): If packaging fails or if invalid paths are provided.
Examples
Package a directory:
pkg = package_code(src_paths=["./src"])
print(pkg.url)
Package multiple directories:
pkg = package_code(src_paths=["./src", "./configs"])
Package with specific file types:
pkg = package_code(
src_paths=["./app"],
suffixes=[".py", ".yaml", ".json"]
)
Using Apps within Flows
Decorator: @app_deploy
Module: metaflow
Simplify bookkeeping and lifecycle management for apps deployed from Metaflow flows.
While you can deploy apps from within a flow using the AppDeployer API directly,
doing so at scale introduces operational challenges: tracking which apps belong to
which run, cleaning up apps when flows complete or fail, and discovering apps
deployed across many runs. This decorator addresses these concerns automatically.
When applied to a flow, @app_deploy provides:
Automatic Tagging: Every app deployed gains Metaflow metadata tags (flow name, run ID, step, task ID, project/branch info) enabling easy discovery and association with specific flow executions.
Lifecycle Management: Configure automatic cleanup policies to scale down or delete apps when the flow exits (on success or failure), preventing orphaned apps from accumulating.
Convenient Access: Exposes
current.appswith the flow's code package and container image, plus alist()method to discover all apps deployed in the current run.
Parameters
- cleanup_policy (str, default "none"): Action to perform on all apps deployed in this run when the flow exits:
- "none": No cleanup; apps remain running after flow completion.
- "scale_down": Scale all deployed apps to zero replicas.
- "delete": Delete all deployed apps.
Examples
from metaflow import FlowSpec, step, current, app_deploy
from metaflow.apps import AppDeployer
@app_deploy
class MyFlow(FlowSpec):
@step
def start(self):
# Deploy an app using the flow's code package
deployer = AppDeployer(
name="my-service",
port=8000,
image=current.apps.current_image,
code_package=current.apps.metaflow_code_package,
commands=["python server.py"],
)
self.app = deployer.deploy()
self.next(self.end)
@step
def end(self):
# List all apps deployed in this run
apps = current.apps.list()
print(f"Deployed {len(apps)} app(s)")
With cleanup policy to prevent orphaned apps:
@app_deploy(cleanup_policy="scale_down")
class MyFlow(FlowSpec):
# Apps will be scaled to zero when flow completes or fails,
# preventing resource waste from forgotten deployments
...
Property: current.apps
Manager for apps deployed within a Metaflow flow execution.
Accessible via current.apps when using the @app_deploy decorator.
Provides access to the flow's code package, container image, and
methods to list apps deployed in the current run.
Attributes
- metaflow_code_package (PackagedCode): The code package for the current flow, ready to use with AppDeployer.
- current_image (str): The container image used by the current task (from fast_bakery or similar).
- default_image (str): The default Kubernetes container image from Metaflow config.
Examples
# python myflow.py --environment=fast-bakery run --with kubernetes
from metaflow.apps import AppDeployer
@pypi(packages={"flask": ">=2.0", "requests": ">=2.28"})
@step
def deploy_step(self):
image = current.apps.current_image
if image is None:
image = current.apps.default_image
# Use the flow's code package directly
deployer = AppDeployer(
name="my-app",
port=8000,
image=image,
code_package=current.apps.metaflow_code_package,
commands=["python app.py"],
)
deployed = deployer.deploy()
# List apps from this run
apps = current.apps.list()