agentleFS
botirkhaltaevbotirkhaltaev/botir-skills/skills/modal-fundamentals/SKILL.md

modal-fundamentals

Skill4 starsChanged 3 months agoApache-2.0View on GitHub

Core Modal Apps, Functions, entrypoints, deploy/run/serve, environments, and CLI workflows (including app/container logs and shell). Use when creating or structuring Modal apps, running ephemeral or deployed workloads, or looking up Modal CLI commands. Sibling primitives: Servers (modal-servers) and Auto Endpoints (modal-endpoints). For deep log debugging playbooks, use modal-observability.

  • Installs packages

We split this skill into the rules it gives an agent

13 rules. About 0 are common, 2 are distinctive, and 11 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 11

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use this skill when building, running, or deploying Modal applications from scratch or when you need to understand the core execution model …

    line 14
  2. Very specific:

    All Modal methods have async counterparts via .aio suffix:

    line 150
  3. Very specific:

    Use modal.Mount to add non-importable files explicitly

    line 194
  4. Distinctive:

    When using AI assistants to write Modal code:

    line 253
  5. Very specific:

    Use modal run for rapid iteration (not full deployments)

    line 256
  6. Very specific:

    Use modal serve for web endpoint development with live reload

    line 257
  7. Very specific:

    Ensure you have a @app.local_entrypoint() or exactly one @app.function()

    line 265
  8. Very specific:

    Check modal setup / auth was completed

    line 266
  9. Very specific:

    Ensure include_source=True (default) and file is importable from entrypoint

    line 278
  10. Very specific:

    Use --detach for long-running ephemeral jobs where the client may disconnect

    line 295
  11. Very specific:

    Use modal.is_local() to guard local-only code paths

    line 296
  12. Very specific:

    Use .aio suffix for async operations, not raw await on sync methods

    line 297
  13. Very specific:

    Prefer single_use_containers=True over deprecated max_inputs=1

    line 298

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Modal Fundamentals

Use this skill when building, running, or deploying Modal applications from scratch or when you need to understand the core execution model and CLI.

Related serving primitives (SDK 1.5+): Servers (modal-servers) for long-lived HTTP processes; Auto Endpoints (modal-endpoints) for managed LLM APIs; Function web decorators remain in modal-web-serving.

When to Use This Skill

  • Creating a new Modal App or Function
  • Running ephemeral apps with modal run or modal serve
  • Deploying persistent apps with modal deploy
  • Writing local_entrypoint or remote entrypoints
  • Managing Modal environments (dev/staging/prod)
  • Understanding the App/Function/container relationship
  • Looking up Modal CLI commands (run/serve/deploy/app/container/shell/secrets/volumes)
  • Using async Modal APIs
  • Structuring Modal projects

Runtime log debugging playbooks (fetch/filter/follow, hung containers, Live Profiling) belong in modal-observability — this skill only lists the CLI surface.

Core Concepts

App

modal.App groups one or more Functions for atomic deployment.

import modal

app = modal.App(name="my-app")

@app.function()
def f():
    print("Hello world!")

Function

Each Function is an independent autoscaling unit. Zero containers run (zero cost) when idle.

Single-use containers (replace one-shot max_inputs=1):

@app.function(single_use_containers=True)
def one_shot():
    ...

Entrypoints

  • @app.local_entrypoint() — runs locally, typically calls .remote() on Functions
  • A single @app.function() can be the entrypoint if no local_entrypoint exists
  • modal run script.py::app.func_name to specify a particular function

Argument parsing is automatic for primitive types:

@app.local_entrypoint()
def main(foo: int, bar: str):
    some_function.remote(foo, bar)

Execution Modes

CommandBehavior
modal run script.pyEphemeral app, stops when script exits
modal run --detach script.pyEphemeral but keeps running after client disconnects
modal serve script.pyEphemeral with live-reload for web endpoint development
modal deploy script.pyPersistent deployed app

Programmatic execution

with modal.enable_output():
    with app.run():
        some_function.remote()

Deployment

app.deploy(name="my-app", strategy="rolling")

Strategies (parity across deploy / rollover / rollback): rolling (default, gradual traffic shift) or recreate (terminate all, then start new).

modal deploy script.py
modal app rollover my-app --strategy=rolling    # refresh containers, no code change
modal app rollover my-app --strategy=recreate
modal app rollback my-app --strategy=rolling    # same strategy flags

Version pinning

Pin lookups so inputs stay on one App version after later redeploys (transitive within the App):

f = modal.Function.from_name("my-app", "f", version=3)
Cls = modal.Cls.from_name("my-app", "MyCls", version=3)

Environments

Environments isolate resources (Secrets, Volumes, deployed Apps):

modal environment create staging
modal run --env staging script.py
modal deploy --env production script.py

Set default: modal config set-environment staging

Dashboard access

modal dashboard                  # Apps homepage for current environment
modal app dashboard my-app
modal volume dashboard my-vol
fc = f.spawn()
print(fc.get_dashboard_url())  # object logs / dashboard deep-link

Python versions

  • Supported: Python 3.10–3.14 (3.14 supported; 3.9 dropped)
  • Experimental 3.14t (free-threaded) via registry + add_python (Image Builder 2025.06):
modal.Image.from_registry("debian:bookworm-slim", add_python="3.14t")

Async API

All Modal methods have async counterparts via .aio suffix:

# Sync
result = f.remote(arg)

# Async
result = await f.remote.aio(arg)

# Async volumes
await vol.read_file.aio("/path")

Async functions run concurrent inputs as asyncio tasks (not threads). Sync functions use threading.

Global Variables

Container-scope globals persist across invocations. Use modal.is_local() for conditional init:

if modal.is_local():
    weights = None  # don't download locally
else:
    weights = download_weights()

Project Structure

Recommended layout:

my-modal-project/
├── my_module/
│   ├── __init__.py
│   ├── app.py       # modal.App() and function definitions
│   └── utils.py
├── pyproject.toml
└── .modal/
    └── environment   # default environment name

Deploy with: modal deploy -m my_module.app

Key rules:

  • include_source=True (default) adds files reachable from entrypoint to container
  • Use modal.Mount to add non-importable files explicitly
  • Set MODAL_ENVIRONMENT env var or .modal/environment file for default environment

Go SDK

The Go SDK can call deployed Functions, create Sandboxes, and manage resources — but cannot define Functions.

import modal "github.com/modal-labs/modal-client/go"

// Create client
mc, _ := modal.NewClient()

// Reference an App
app, _ := mc.Apps.FromName(ctx, "my-app", &modal.AppFromNameParams{CreateIfMissing: true})

// Call a deployed Function
fn, _ := mc.Functions.FromName(ctx, "my-app", "my_function", nil)
result, _ := fn.Remote(ctx, []any{"arg1"}, nil)

Install: go get -u github.com/modal-labs/modal-client/go (requires Go 1.23+)

TypeScript SDK

The TypeScript SDK mirrors Go capabilities — call Functions, create Sandboxes, manage resources.

import { ModalClient } from "modal";

const modal = new ModalClient();

// Reference an App
const app = await modal.apps.fromName("my-app", { createIfMissing: true });

// Call a deployed Function
const fn = await modal.functions.fromName("my-app", "my_function");
const result = await fn.remote(["arg1"]);

Install: npm install modal (requires Node.js 22+)

SDK Feature Parity

FeaturePythonGoTypeScript
Define FunctionsYesNoNo
Call FunctionsYesYesYes
SandboxesYesYesYes
VolumesYesYesYes
QueuesYesYesYes
SecretsYesYesYes
ImagesYesYesYes
SchedulingYesNoNo
Web endpointsYesNoNo
Servers / EndpointsYes (define)Call/manage where exposedCall/manage where exposed

Developing with LLMs

When using AI assistants to write Modal code:

  • Install modal locally for type checking
  • Provide Modal Python SDK stubs for autocomplete
  • Use modal run for rapid iteration (not full deployments)
  • Use modal serve for web endpoint development with live reload
  • Install/update the official Modal agent skill: modal skills install / modal skills update
  • Surface new SDK features: modal changelog --since=1.5 or modal changelog --newer
  • Bootstrap starter apps (experimental): modal bootstrap

Symptom Triage

"modal run does nothing"

  • Ensure you have a @app.local_entrypoint() or exactly one @app.function()
  • Check modal setup / auth was completed

"App deploys but Functions don't run"

  • Scheduled Functions need modal deploy, not modal run
  • Non-scheduled Functions need explicit invocation (web URL, .remote(), etc.)

"Logs are missing"

  • Programmatic app.run(): wrap with modal.enable_output()
  • Dashboard deep links: modal dashboard / .get_dashboard_url()
  • Otherwise load modal-observability (references/logging-debugging.md) — do not improvise from dashboard-only advice

"Import error: module not found in container"

  • Ensure include_source=True (default) and file is importable from entrypoint
  • Or add explicit modal.Mount.from_local_dir() in image definition
  • Reproduce in a fresh image shell: modal shell script.py::fn

Reference Map

  • references/apps-and-functions.md — App/Function lifecycle, naming, entrypoints, single_use_containers, version pinning
  • references/cli-commands.md — run/serve/deploy, app/container logs, shell, rollover, bootstrap, changelog, skills, dashboard, env
  • references/environments.md — Environment isolation and configuration
  • references/local-development.md — serve, hot reload, interactive debug, shells
  • Sibling skills: modal-servers, modal-endpoints, modal-web-serving, modal-observability

Guardrails

  • Everything is code — no YAML configuration files
  • modal.Stub is removed since Modal 1.0; use modal.App
  • Protect app.run() in if __name__ == "__main__" blocks to avoid running in containers
  • Use --detach for long-running ephemeral jobs where the client may disconnect
  • Use modal.is_local() to guard local-only code paths
  • Use .aio suffix for async operations, not raw await on sync methods
  • Prefer single_use_containers=True over deprecated max_inputs=1
  • Deploy / rollover / rollback share --strategy=rolling|recreate
  • Python 3.9 is unsupported; pin client ~=1.5.0 when tracking 1.5 features
  • Official agent skill: modal skills install / modal skills update (version-aligned refs when installed via CLI)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.