Try DeepSeek-R1 on Modal! View example

Modal 1.0 migration guide

We are planning to release version 1.0 of the modal Python SDK in Q1 of 2025. This release will signify an increased commitment to API stability and will imply some changes to our development workflow.

Preceding the 1.0 release, we are making a number of breaking changes based on feedback that we have received from early users. These changes are intended to address pain points and reduce confusion about some aspects of the Modal API. While they will require some changes to existing user code, we believe that they’ll make it easier to use Modal going forward.

Our plan is to gradually roll out changes — with deprecation warnings — across the final sequence of 0.X releases. Once we release 1.0, code that does not issue deprecation warnings can be considered stable API. We will eventually expire the deprecations introduced prior to 1.0 and remove support for the old APIs.

This page outlines the major changes that we’re making as part of the v1.0 releases.

Deprecating Image.copy_* methods

Introduced in: v0.72.11

We recently introduced new Image methods — Image.add_local_dir and Image.add_local_file — to replace the existing Image.copy_local_dir and Image.copy_local_file.

The new methods subsume the functionality of the old ones, but their default behavior is different and more performant. By default, files will be mounted to the container at runtime rather than copied into a new Image layer. This can speed up development substantially when iterating on the contents of the files.

Building a new Image layer should be necessary only when subsequent build steps will use the added files. In that case, you can pass copy=True in Image.add_local_file or Image.add_local_dir.

The Image.add_local_dir method also has an ignore= parameter, which you can use to pass file-matching patterns (using dockerignore rules) or predicate functions to exclude files.

Deprecating Mount as part of the public API

Introduced in: v0.72.4

Currently, local files can be mounted to the container filesystem either by including them in the Image definition or by passing a modal.Mount object directly to the App.function or App.cls decorators. As part of the 1.0 release, we are simplifying the container filesystem configuration to be defined only by the Image used for each Function. This implies deprecation of the following:

  • The mount= parameter of App.function and App.cls
  • The context_mount= parameter of several modal.Image methods
  • The Image.copy_mount method
  • The Mount object

Code that uses the mount= parameter of App.function and App.cls should be migrated to pass those files / directories to the Image used by that Function or Cls, i.e. using the Image.add_local_file, Image.add_local_dir, or Image.add_local_python_source methods:

# Mounting local files

# Old way (deprecated)
mount = modal.Mount.from_local_dir("data").add_local_file("config.yaml")
@app.function(image=image, mount=mount)
def f():
    ...

# New way
image = image.add_local_dir("data").add_local_file("config.yaml")
@app.function(image=image)
def f():
    ...

## Mounting local Python source code

# Old way (deprecated)
mount = modal.Mount.from_local_python_packages("my-lib"))
@app.function(image=image, mount=mount)
def f()
    ...

# New way
image = image.add_local_python_source("my-lib")
@app.function(image=image)
def f(...):
    ...

## Using Image.copy_mount

# Old way (deprecated)
mount = modal.Mount.from_local_dir("data").add_local_file("config.yaml")
image.copy_mount(mount)

# New way
image.add_local_dir("data").add_local_file("config.yaml")

Code that uses the context_mount= parameter of Image.from_dockerfile and Image.dockerfile_commands methods can delete that parameter; we now automatically infer the files that need to be included in the context.

Deprecating the @modal.build decorator

Introduced in: v0.72.17

As part of consolidating the filesystem configuration API, we are also deprecating the modal.build decorator.

For use cases where modal.build would previously have been the suggested approach (e.g., downloading model weights or other large assets to the container filesystem), we now recommend using a modal.Volume instead. The main advantage of storing weights in a Volume instead of an Image is that the weights do not need to be re-downloaded every time you change something else about the Image definition.

Many frameworks, such as Hugging Face, automatically cache downloaded model weights. When using these frameworks, you just need to ensure that you mount a modal.Volume to the expected location of the framework’s cache:

cache_vol = modal.Volume.from_name("hf-hub-cache")
@app.cls(
    image=image.env({"HF_HUB_CACHE": "/cache"}),
    volumes={"/cache": cache_vol},
    ...
)
class Model:
    @modal.enter()
    def load_model(self):
        self.model = ModelClass.from_pretrained(...)

For frameworks that don’t support automatic caching, you could write a separate function to download the weights and write them directly to the Volume, then modal run against this function before you deploy.

In some cases (e.g., if the step runs very quickly), you may wish for the logic currently decorated with @modal.build to continue modifying the Image filesystem. In that case, you can extract the method as a standalone function and pass it to Image.run_function:

def download_weights():
    ...

image = image.run_function(download_weights)

Requiring explicit inclusion of local Python dependencies

Introduced in: 0.73.11

Prior to 1.0, Modal will inspect the modules that are imported when running your App code and automatically include any “local” modules in the remote container environment. This behavior is referred to as “automounting”.

While convenient, this approach has a number of edge cases and surprising behaviors, such as ignoring modules with imports that are deferred using Image.imports. Additionally, it is difficult to configure the automounting behavior to, e.g., ignore large data files that are stored within your local Python project directories.

Going forward, it will be necessary to explicitly include the local dependencies of your Modal App. The easiest way to do this is with [Image.add_local_python_source] (/docs/reference/modal#modal.Image.add_local_python_source):

import modal
import helpers

image = modal.Image.debian_slim().add_local_python_source("helpers")

In the period leading up to the change in default behavior, the Modal client will issue deprecation warnings when automounted modules are not included in the Image. Updating the Image definition will remove these warnings.

Note that Modal will continue to automatically include the source module or package defining the App itself. We’re introducing a new App or Function-level parameter, include_source, which can be set to False in cases where this is not desired (i.e., because your Image definition already includes the App source).

Deprecating the .lookup method on Modal objects

Introduced in: v0.72.56

Most Modal objects can be instantiated through two distinct methods: .from_name and .lookup. The redundancy between these methods is a persistent source of confusion.

The .from_name method is lazy: it operates entirely locally and instantiates only a shell for the object. The local object won’t be associated with its identity on the Modal server until you interact with it. In contrast, the .lookup method is eager: it triggers a remote call to the Modal server, and it returns a fully-hydrated object.

Because Modal objects can now be hydrated on-demand, when they are first used, there is rarely any need to eagerly hydrate. Therefore, we’re deprecating .lookup so that there’s only one obvious way to instantiate objects.

In most cases, the migration is a simple find-and-replace of .lookup.from_name.

One exception is when your code needs to access object metadata, such as its ID, or a web endpoint’s URL. In that case, you can explicitly force hydration of the object by calling its .hydrate() method. There may be other subtle consequences, such as errors being rasied at a different location if no object exists with the given name.

Deprecating modal.gpu objects

Introduced in: v0.73.31

The modal.gpu objects are being deprecated; going forward, all GPU resource configuration should be accomplished using strings.

This should be an easy code substitution, e.g. gpu=modal.gpu.H100() can be replaced with gpu="H100". When using the count= parameter of the GPU class, simply append it to the name with a colon (e.g. gpu="H100:8"). In the case of the modal.gpu.A100(size="80GB") variant, the name of the corresponding gpu is "A100-80GB".

Note that string arguments are case-insensitive, so "H100" and "h100" are both accepted.

The main rationale for this change is that it will allow us to introduce new GPU models in the future without requring users to upgrade their SDK.