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

# Apps and Execution

> Understand the fal App class and how code is split between your local machine and remote runners.

When you write a fal app, not all of your code runs in the same place. Some of it executes on your local machine at import time, and the rest executes remotely on a GPU [runner](/documentation/deployment/runners) when requests arrive. Understanding this boundary is essential for debugging issues, managing secrets, and controlling what gets shipped to the remote environment.

This page explains the execution model in detail. If you are looking for the lifecycle hooks that run on the remote side (setup, teardown, signal handling), see [App Lifecycle](/documentation/development/app-lifecycle). If you want to understand how the remote environment is constructed (packages, containers, system dependencies), see [Defining Your Environment](/documentation/development/container-setup).

## The App Class

An App is a Python class that inherits from `fal.App`. It is the unit of deployment on fal. Your app defines what hardware it needs, how to initialize your model, and what endpoints are available for callers. With the default file-reference workflow, `fal deploy` serializes this class and ships it to the remote runtime.

```python theme={null}
class MyApp(fal.App):
    machine_type = "GPU-H100"

    def setup(self):
        self.model = load_model()

    @fal.endpoint("/")
    def generate(self, input_data):
        return self.model(input_data)

    def teardown(self):
        self.model.close()
```

The `machine_type` attribute tells fal which [hardware](/documentation/deployment/machine-types) to provision. The `setup()` method runs once when a runner starts, loading your model into GPU memory. Endpoint methods handle individual requests. And `teardown()` runs when the runner shuts down, giving you a chance to release resources. For the full lifecycle, see [App Lifecycle](/documentation/development/app-lifecycle).

## What Runs Locally

With file-reference deploys, when Python imports your app file, all top-level code executes on your machine. This is standard Python behavior: class bodies are evaluated, module-level variables are assigned, and helper functions are defined. This happens during `fal run` or `fal deploy`, before anything is sent to the cloud.

This means that if you reference an environment variable at the top level, it reads from your local environment, not the remote runner's environment. If you construct a large object at module scope, it will be serialized (pickled) and shipped as part of the app payload. This is intentional for small configuration values but problematic for large objects.

```python theme={null}
import os
import fal

CONFIG = {"api_key": os.environ["MY_API_KEY"]}

def helper(x):
    import torch
    return torch.tensor(x)
```

In this example, `CONFIG` is evaluated locally and pickled into the app payload. The `helper` function is defined locally but its body only executes remotely when called. The `import torch` inside the function runs on the remote runner, not on your machine.

## What Runs Remotely

With file-reference deploys, your `fal.App` subclass is pickled locally, then unpickled and instantiated on the remote runner in the environment you configured (either via [pip requirements](/documentation/development/fal-runtime) or a [custom Docker container](/documentation/development/use-custom-container-image)). All methods on the class, including `setup()`, your `@fal.endpoint` handlers, and `teardown()`, execute on the remote machine.

Symbols referenced by your app are handled in one of two ways. Small objects (closures, constants, simple data structures) are pickled and shipped as part of the payload. Importable packages that are installed in the remote environment are imported there rather than being serialized. This is why you often see imports inside methods rather than at the top of the file: it ensures the import happens in the remote environment where the package is installed.

```python theme={null}
import fal

CONFIG = {"model_name": "stabilityai/sdxl-base"}

class MyApp(fal.App):
    machine_type = "GPU-A100"
    requirements = ["torch", "diffusers"]

    def setup(self):
        from diffusers import StableDiffusionXLPipeline
        self.pipe = StableDiffusionXLPipeline.from_pretrained(
            CONFIG["model_name"]
        ).to("cuda")

    @fal.endpoint("/")
    def generate(self, input: dict) -> dict:
        result = self.pipe(input["prompt"]).images[0]
        return {"image": result}
```

In this example, `CONFIG` is a small dictionary that gets pickled and shipped to the runner. The `diffusers` import happens inside `setup()`, so it runs remotely where the package is installed via `requirements`. The `StableDiffusionXLPipeline` is loaded into GPU memory on the runner, not on your laptop.

## The Serialization Boundary

For file-reference deploys, the boundary between local and remote is the pickle serialization step. When fal prepares your app for deployment, it pickles the class and any objects it references. This has practical implications.

If your app is packaged as an installable Python project, you can use a [`python_entry_point`](/documentation/development/import-code#package-entry-points) instead. In that mode, fal installs your package and imports the configured `<module>:<symbol>` on the runner, so the app definition is not pickled from your local process.

Objects that pickle well (strings, numbers, dicts, lists, simple dataclasses) can be freely referenced from top-level code. Objects that do not pickle well (database connections, file handles, GPU tensors, large numpy arrays) should be created inside `setup()` or endpoint methods, where they run on the remote machine.

If you need to pass secrets to your app, avoid putting them in top-level code where they would be pickled into the payload. Instead, use fal's [secrets management](/documentation/development/manage-secrets-securely) to store them securely and access them via [environment variables](/documentation/development/environment-variables) on the runner.

## Next Steps

<CardGroup cols={2}>
  <Card title="App Lifecycle" href="/documentation/development/app-lifecycle">
    The setup, handle\_exit, and teardown hooks that run on each runner.
  </Card>

  <Card title="Defining Your Environment" icon="box" href="/documentation/development/container-setup">
    Choose between pip requirements and custom Docker containers.
  </Card>
</CardGroup>
