Schedule Prefect deployments to run on Modal
Learn how to use Modal as a push work pool to schedule, run, and debug your Prefect flows
Prefect is the world’s best orchestrator. Modal is the world’s best serverless compute framework. It only makes sense that they would fit together perfectly.
In this tutorial, you’ll build your first Prefect deployment that runs on Modal every hour, and create a scalable framework to grow your project.
Here’s an architecture diagram which shows what you’ll build.
You’ll build a full serverless infrastructure framework for scheduling and provisioning workflows. GitHub will act as the source of truth, Prefect will be the orchestrator, and Modal will be the serverless infrastructure. You’ll only pay for the Modal CPU time you use.
First, here’s a recap of some important terms:
Term | Definition |
---|---|
Flow | A Python function in a file, decorated with @flow , that is an entrypoint to perform a task in Prefect. |
Run | An instance of a flow, executed at a specific time with a specific payload. |
Deployment | A manifest around a flow, including where it should run, when it should run, and any variables it needs to run. A deployment can schedule your flows, creating runs when needed. |
Work Pool | An interface to the hardware used to run a given flow. |
Prerequisites
- A free Prefect Cloud account.
- A free Modal account.
Create a GitHub repository
Create a GitHub repository containing your Prefect flows.
You can reference the Ben-Epstein/prefect-modal repository for a complete example.
Your repository should have a prefect.yaml
deployment configuration file, standard Python tooling, and CI/CD.
-
Start by creating the following
Makefile
to keep things easy:Makefile -
Set up and manage your environment with
uv
. -
Create your Python project.
If
uv
creates apython-version
that is pinned to a micro version (for example, 3.12.6), remove the micro version (for example, 3.12) to avoid downstream issues. -
Add your dependencies:
-
Add the following to the end of the
pyproject.toml
file created byuv
: -
Initialize the Prefect environment:
You will be prompted to choose your code storage; select
git
to store code within a git repository. -
Because this is meant to be a scalable repository, create a submodule just for your workflows.
Now that your repository is set up, take a look at the prefect.yaml
file that you generated.
The important sections here are pull
, which defines the repository and branch to pull from, as well as deployments
, which define the flows
that will be run
at some schedule
on a work_pool
.
The work pool will be set to modal later in this tutorial.
Finally, create your flow.
You can do anything in your flow, but use the following code to keep it simple.
Give Prefect access to your GitHub repository
First, authenticate to Prefect Cloud.
You’ll need a Personal Access Token (PAT) for authentication. For simplicity, use a classic token, but you can configure a fine-grained token if you want.
In GitHub, go to Personal access tokens and click Generate new token (classic).
Give the token a name, an expiration date, and repository access.
Once you’ve created the token, copy it and give it to Prefect. First, run:
The second command returns a URL.
Click that URL to give your block a name and paste in the token you got from GitHub.
Name the block prefect-modal
.
In prefect.yaml
, edit the pull:
section as follows:
- Replace the
access_token
key name withcredentials
. - Change the repository name to the name of your repository.
Now, add the following section to configure how you install your package and what your working directory is:
Typically, uv sync
creates a new virtual environment.
The following steps show how to tell uv
to use the root Python version.
This configuration tells Prefect how to download the code from GitHub into the Modal pod before running the flow.
This is important: you are telling Modal to load the code from main
.
This means that if you push new code to main, the next run of a flow in Modal will change to whatever is in main.
To keep this stable, you can set that to a release branch, for example, to only update that release branch when you’re stable and ready.
Now, configure a few more options on the deployment
to get it ready: entrypoint
, schedule
, and description
.
This schedule runs once a day at 12PM EST. You can add another schedule by adding another list element.
Because you referenced the entrypoint as a module and not a path, you can call it from anywhere (as long as it’s installed). This is much more flexible.
Connect Prefect and Modal
You need to give Prefect access to your Modal account to provision a serverless instance to run your flows and tasks. Modal will be the Work Pool in Prefect terms, remaining off when unused, and spinning up the infrastructure you need when a new flow run is scheduled. Prefect makes this incredibly easy.
First, authenticate with Modal:
Next, create a token:
Finally, create a work pool:
The name pref-modal-pool
in the last command is custom, you can name it whatever you want.
Take the work pool name and update your prefect.yaml
file with it.
This UV_PROJECT_ENVIRONMENT
is how you tell uv to use the root python.
You could pip install
without uv
but it would be significantly slower (minutes instead of seconds) and you can’t leverage your lockfile, resulting in unexpected behavior!
You can create multiple work queues across the same pool (for example, if you have another job using this pool). Multiple work queues let you handle concurrency and swim-lane priorities. To keep things simple, this tutorial will use the the default queue name that comes with every new pool.
When you deploy to Prefect, it will know to schedule all of the flow runs for this particular deployment on your modal work pool!
At this point, you could git add . && git commit -m "prefect modal" && git push
to get your code into GitHub, and then uv run prefect deploy --all
to get your deployment live.
But first, learn how to automate Prefect deployments.
Configure CI/CD
Since the code must exist in GitHub in order for Modal to download, install, and run it, GitHub should also be the source of truth for telling Prefect what to do with the code.
First, create two secrets to add to the GitHub repository: PREFECT_API_KEY
and PREFECT_API_URL
.
-
For
PREFECT_API_URL
, the format is as follows:https://api.prefect.cloud/api/accounts/{account_id}/workspaces/{workspace_id}
.Go to the Prefect Cloud Dashboard, and then extract the account ID and workspace ID from the URL.
The Prefect console URL uses
/account
and/workspace
but the API uses/accounts
and/workspaces
, respectively. -
For
PREFECT_API_KEY
, go to the API keys page and create a new API key.
Go to your repository, and then Settings / Secrets and variables / Actions.
Click New repository secret to add these secrets to your GitHub repository.
Now, add a simple GitHub workflow. Create a new folder in the root of your GitHub repository:
Create a workflow file.
Add the following code to this workflow.
The make prefect-deploy
command is used to deploy the flows in your deployment.
You can run make prefect-deploy
locally at any time to keep your deployment up to date.
Add the following command to your Makefile
.
Before pushing your code, run make prefect-deploy
once to make sure everything works.
Prefect will suggest changes to prefect.yaml
, which you should accept.
You let Prefect override your deployment spec, which allowed your CD to run without any interruptions or prompts. If you go to the Deployment page in Prefect Cloud, you’ll see your deployment!
There are no runs yet.
The output from make prefect-deploy
shows the command to run your deployed flow.
However, since the code isn’t in GitHub yet, this will fail.
To fix this, push your code:
Go to the repository in GitHub, and then open Actions to verify that your CI/CD is running successfully.
You can see the flow run in Prefect Cloud as well.
The flow will run automatically at 12 PM, but you can also run it manually.
In a new browser tab, open your [https://modal.com/apps/](Modal dashboard) alongside your Prefect Cloud dashboard.
In your terminal, run the following command:
You should see the run start in your Prefect dashboard. In 30-60 seconds, you should see it run in Modal. You can click on the run to see the logs.
Click into the app and then click App Logs on the left to show everything that’s happening inside the flow run!
Click on a specific log line to get more details.
If you open the URL in that log, you’ll see the corresponding logs in Prefect Cloud.
As you make changes to either the code or deployment file, Prefect will redeploy and Modal will automatically pick up changes. You’ve successfully built your first Prefect deployment that runs on Modal every hour!
For more information about CI/CD in Prefect, see Build deployments via CI/CD.
Was this page helpful?