Skip to main content
The fal runtime is the recommended way to define your app’s environment. Instead of writing a Dockerfile, you list your pip dependencies in the requirements attribute and fal builds an optimized, cached environment for you. This approach handles CUDA setup, PyTorch index URLs, and environment isolation automatically based on your chosen machine type. If you need system-level packages, a specific base image, or a non-Python runtime, use a custom container instead. If you need to include local Python packages, local modules, or external repositories, see Import Code. For an overview of when to use which approach, see Environment and Runtime.

Defining Requirements

The requirements attribute in your fal.App class is where you specify the Python packages your model needs. fal ensures these are installed in the runner’s environment before setup() is called.
Pin your package versions (e.g., torch==2.4.0) to ensure reproducible builds. Use the requirements attribute instead of running pip install inside setup(), since packages in requirements are installed during the container build and cached across deploys. Only include the packages your app actually needs to keep startup times short. When you deploy, fal creates an isolated environment based on your machine_type, installs your listed packages, and caches the resulting container image. On subsequent deploys, unchanged requirements are served from cache. Your setup() method then runs on the provisioned runner to load the model into memory.

Using Prebuilt Wheels

You can install packages directly from wheel URLs. This is useful for custom-built packages or packages not available on PyPI.

Direct URL

Provide the full URL to a wheel file:

Package @ URL (PEP 440)

Use the package@url syntax to give the package a name for dependency resolution:
This syntax is recommended when other packages depend on mypackage, as pip can properly track the dependency.

Alternative Package Indexes

Use --extra-index-url or --find-links to install packages from alternative sources.

Extra Index URL

Install packages from an additional PyPI-compatible index:
The --extra-index-url flag must appear before any packages that need it. Place index flags at the beginning or directly before the relevant packages.
Use --find-links to search for packages in a directory or URL containing wheel files:

Multiple Indexes

Combine multiple index sources when needed:

Private Packages

fal gives the requirements list to pip without changes. Any pip syntax for a private source works. Only the way you supply the credentials is different.

Credentials at Build Time

To authenticate a private install, write ${SECRET_NAME} in the requirement string. fal replaces the placeholder with the value of the secret. This replacement runs on the fal servers during your deploy, before the build starts.
The spelling looks like a shell variable, but this is not an environment variable. fal does not start a shell and does not read the environment. fal changes the string itself. pip then receives the complete URL as a normal argument. This is the only way to authenticate a dependency install. During the build, fal does not put your secrets in the environment. Your app code has not started at that point, and os.getenv() cannot help you. The install is already complete before the app starts. fal withholds the secrets because it caches each build artifact and uses it again for later deploys. A build that can read a secret can write that secret into a cached layer. fal would then give that layer to a different build. Replacement resolves only the strings that you mark, so the build receives no other secret.
A new secret value produces a different requirement string. This changes the cache key of the environment. The first deploy after you rotate a secret builds again instead of using the cache.
Replacement does not apply to requirements in Direct Server Mode (use_isolate=False). In this mode fal does not build a Python environment for you. fal keeps the placeholder in the string and does not resolve it. Give the credentials to ContainerImage(secrets={...}) instead, which does support ${...}. Refer to Docker Build Secrets.

Private Git Repositories

Install directly from a private GitHub repository:
Pin to a specific commit or tag for reproducibility:

Private Package Index

Install from a private index server with authentication:
--extra-index-url adds your index to PyPI. pip then installs the highest version that it finds in either index. A public package with the same name as your private package can replace it. If your server supplies all the packages that you need, use --index-url. pip then reads only that index.

Hosted Registries

Most managed registries use a fixed username and a token as the password. For Google Artifact Registry, create a service account key. Encode the key and store it in one step. This command prevents two problems. The base64 command adds a newline at the end of its output. The encoded value also contains +, /, and = characters, which a URL does not permit.
pip decodes the URL encoding before it sends the authentication header. The registry receives the original base64 value.
Tokens from AWS CodeArtifact and Azure Artifacts expire. A CodeArtifact token is valid for 12 hours at most. fal does not refresh a stored secret, so a build that was successful yesterday can fail today with a 401 error. Use a long-lived credential if the registry supplies one. If it does not, set the secret again in each deploy.

Pre-signed URLs

For one or two wheels, it is simpler to omit the index. Generate a pre-signed URL from your private storage and install the wheel directly. This method needs no index flag and no registry credentials.
A pre-signed URL expires. Generate the URL in each deploy. Do not commit a long-lived URL to your repository.

Troubleshooting

  • The log shows a literal ${MY_SECRET}, and the install fails with a 401 or 403 error. fal does not fail the build for a name that it cannot find. It keeps the placeholder, and pip receives it as text. Compare the name with the output of fal secrets list. Also confirm the environment that you deployed to.
  • The registry rejects the credentials, but the secret value is correct. The base64 command adds a newline at the end of its output. This newline becomes part of the password. Remove it before you set the secret.
  • A $ character in a fixed value disappears. A $ character starts a secret reference. Write $$ for one literal $ character. It is better to move the value into a secret.
  • The app declares a secrets allowlist. The build reads the same set of secrets as the runner. Every name that you use in requirements must also appear in that list. Refer to Scoping Secrets to an App.
Refer to Secrets to set, scope, and rotate secrets. Refer to private registries to authenticate to a private Docker registry.

Dynamic Wheel Selection

When you need different wheels for different Python versions or platforms, use a helper function:
Helper functions are evaluated at deploy time on your local machine, so they have access to local environment variables and can make decisions based on the target Python version.

Importing Local Code

Learn how to bring your local Python packages, modules, and files into the fal runtime.