Documentation Index Fetch the complete documentation index at: https://fal.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
This guide maps Modal concepts to their fal equivalents and shows how to convert your code.
Concept Mapping
Modal fal Notes @app.function()@fal.function()Standalone serverless functions @app.cls()class MyApp(fal.App)Class-based apps (recommended) @modal.method()@fal.endpoint("/")fal uses HTTP endpoints, not RPC @modal.enter()def setup(self)Container startup hook @modal.exit()def teardown(self)Container shutdown hook modal.Image.debian_slim().pip_install(...)requirements = [...]Or use ContainerImage for Dockerfiles modal.Image.from_dockerfile(...)ContainerImage.from_dockerfile(...)Custom container support gpu="A100"machine_type = "GPU-A100"GPU selection modal.Volume/data persistent storageMounted automatically on all runners modal.Secretfal secrets setSecrets exposed as env vars modal deployfal deployCLI deployment Cls().method.remote(x=123)fal_client.subscribe(...)fal uses HTTP + queue, not RPC
Migration Path 1: Standalone Functions
If you use @app.function() in Modal, the closest fal equivalent is @fal.function().
import modal
app = modal.App()
image = modal.Image.debian_slim().pip_install( "torch" , "transformers" )
@app.function ( image = image, gpu = "A100" )
def generate ( prompt : str ):
from transformers import pipeline
pipe = pipeline( "text-generation" , model = "gpt2" , device = "cuda" )
return pipe(prompt)[ 0 ][ "generated_text" ]
@app.local_entrypoint ()
def main ():
result = generate.remote( prompt = "Hello world" )
print (result)
import fal
@fal.function (
requirements = [ "torch" , "transformers" ],
machine_type = "GPU-A100" ,
)
def generate ( prompt : str ):
from transformers import pipeline
pipe = pipeline( "text-generation" , model = "gpt2" , device = "cuda" )
return pipe(prompt)[ 0 ][ "generated_text" ]
Migration Path 2: Class-Based Apps (Recommended)
If you use @app.cls() with @modal.enter() and @modal.method(), convert to a fal.App class.
import modal
app = modal.App()
image = (
modal.Image.debian_slim()
.pip_install( "torch" , "diffusers" , "transformers" , "accelerate" )
)
@app.cls ( image = image, gpu = "A100" )
class TextToImage :
@modal.enter ()
def load_model ( self ):
import torch
from diffusers import StableDiffusionXLPipeline
self .pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0" ,
torch_dtype = torch.float16,
).to( "cuda" )
@modal.method ()
def generate ( self , prompt : str ):
image = self .pipe(prompt).images[ 0 ]
return image
@modal.exit ()
def cleanup ( self ):
del self .pipe
@app.local_entrypoint ()
def main ():
result = TextToImage().generate.remote( prompt = "a sunset" )
import fal
from fal.toolkit import Image
class TextToImage ( fal . App ):
machine_type = "GPU-A100"
requirements = [ "torch" , "diffusers" , "transformers" , "accelerate" ]
def setup ( self ):
import torch
from diffusers import StableDiffusionXLPipeline
self .pipe = StableDiffusionXLPipeline.from_pretrained(
"stabilityai/stable-diffusion-xl-base-1.0" ,
torch_dtype = torch.float16,
).to( "cuda" )
@fal.endpoint ( "/" )
def generate ( self , prompt : str ) -> dict :
image = self .pipe(prompt).images[ 0 ]
return { "image" : Image.from_pil(image)}
def teardown ( self ):
del self .pipe
Deploying
# Modal
modal deploy my_app.py
# fal
fal deploy my_app.py::TextToImage
Calling Your App
# Modal
TextToImage().generate.remote( prompt = "a sunset" )
# fal
import fal_client
result = fal_client.subscribe( "your-username/text-to-image" , arguments = {
"prompt" : "a sunset"
})
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
# Modal
image = (
modal.Image.debian_slim()
.apt_install( "ffmpeg" )
.pip_install( "torch" , "diffusers" )
)
# fal (simple)
class MyApp ( fal . App ):
requirements = [ "torch" , "diffusers" ]
# fal (Dockerfile)
from fal.container import ContainerImage
class MyApp ( fal . App ):
image = ContainerImage.from_dockerfile_str( """
FROM python:3.11
RUN apt-get update && apt-get install -y ffmpeg
RUN pip install torch diffusers
""" )
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.
# Modal
volume = modal.Volume.from_name( "model-cache" )
@app.cls ( volumes = { "/cache" : volume})
class MyModel :
...
# fal -- /data is always available, no configuration needed
class MyModel ( fal . App ):
def setup ( self ):
model_path = "/data/models/my-model.pt"
if os.path.exists(model_path):
self .model = torch.load(model_path)
Secrets
Modal uses modal.Secret.from_name(...) and attaches secrets to functions. fal exposes secrets as environment variables:
# Modal
modal secret create my-secret HF_TOKEN=hf_xxx
# fal
fal secrets set HF_TOKEN=hf_xxx
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