Skip to main content

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 @secrets decorator 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 DeployedApp object 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 DeployedApp object 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 DeployedApp object 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 DeployedApp object 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 DeployedApp object 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:

  1. 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.

  2. 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.

  3. Convenient Access: Exposes current.apps with the flow's code package and container image, plus a list() 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()