Skip to main content
This guide maps Modal concepts to their fal equivalents and shows how to convert your code.

Concept Mapping


Migration Path 1: Standalone Functions

If you use @app.function() in Modal, the closest fal equivalent is @fal.function().
If you use @app.cls() with @modal.enter() and @modal.method(), convert to a fal.App class.

Deploying

Calling Your App


Key Differences

HTTP Endpoints vs RPC

Modal uses .remote() to invoke functions — a Python-to-Python RPC call. fal uses HTTP endpoints that any language or tool can call. Your fal App exposes a REST API automatically. This means:
  • No .remote() calls — clients use fal_client.subscribe() or raw HTTP
  • Your endpoints accept and return JSON (use Pydantic models for validation)
  • The same endpoint works from Python, JavaScript, cURL, or any HTTP client

Queue-Based by Default

fal provides a persistent queue that handles retries, scaling, and load balancing automatically. When callers use subscribe or submit, requests go through this queue by default. Direct calls via run bypass the queue for lower overhead.

Container Images

Modal chains image methods (Image.debian_slim().pip_install(...).apt_install(...)). fal offers two approaches:
  • requirements list (simpler): Just list your pip packages
  • ContainerImage (full control): Bring your own Dockerfile

Volumes and Storage

Modal uses named Volume objects mounted at specific paths. fal provides /data — a persistent filesystem automatically mounted on every runner, shared across all your apps.

Secrets

Modal uses modal.Secret.from_name(...) and attaches secrets to functions. fal exposes secrets as environment variables:
Both make secrets available via os.environ["HF_TOKEN"] in your code.

Getting Started

Deploy your first fal app

App Lifecycle

Learn about runners, scaling, and the fal architecture