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 ofApp.function
andApp.cls
- The
context_mount=
parameter of severalmodal.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.