Skip to main content
Modal is the managed runtime behind uvx synth-ai deploy --runtime modal. It packages the exact task app you validated locally, uploads any necessary code, injects secrets, and runs modal deploy or modal serve via the Modal CLI. The first *.modal.run URL produced by the CLI is automatically written to your .env as TASK_APP_URL, so Synth trainers and smoke tests can target the deployed service immediately.

Why Modal?

  • Durable HTTPS endpoint with GPU/CPU scale and zero local infrastructure.
  • Identical code paths to local development (your repo is mounted into the Modal image).
  • Automatic secret handling: the CLI injects ENVIRONMENT_API_KEY as an inline Modal Secret, so the remote app keeps auth parity with local testing.

Prerequisites

  1. Complete uvx synth-ai setup so both SYNTH_API_KEY and ENVIRONMENT_API_KEY are available (from .env or your environment).
  2. Install the Modal CLI (pip install modal-client or brew install modal-labs/homebrew-tap/modal).
  3. Log in once: the deploy command will prompt you, but you can pre-run modal setup to cache MODAL_TOKEN_ID/MODAL_TOKEN_SECRET.
  4. Create a Modal app file that exposes app = modal.App("my-task-app") (or imports App directly). The CLI’s validate_modal_app rejects files without a literal app name so deployments always have a stable identifier.

Preview with modal serve

Use --modal-mode serve for an interactive container that mirrors the local runtime but runs inside Modal’s infrastructure:
uvx synth-ai deploy \
  --runtime modal \
  --modal-mode serve \
  --modal-app examples/task_app/modal_app.py \
  --env examples/warming_up_to_rl/.env
Key details:
  • The CLI spawns a temporary wrapper module that adds your repo and task-app directory as local mounts inside the Modal image, so imports continue working without publishing a wheel.
  • ENVIRONMENT_API_KEY is converted into a Modal inline secret and attached to every function decorator that already references modal.Secret(...). You still need to register other secrets normally (or pass them via --env).
  • modal serve blocks your terminal and tears down when you exit—ideal for rapid iteration or debugging Modal-specific behavior before promoting to a full deploy.

Launch a production deploy

uvx synth-ai deploy \
  --runtime modal \
  --modal-app examples/task_app/modal_app.py \
  --name grpo-crafter-task-app \
  --env examples/warming_up_to_rl/.env
What happens:
  1. The CLI validates both the FastAPI task app and the Modal app file.
  2. ModalDeployCfg resolves the Modal binary (or respects --modal-cli PATH), enforces that --dry-run is only available with --modal-mode deploy, and raises clear errors if the CLI is missing.
  3. deploy_app_modal runs modal deploy with a temporary wrapper, streams CLI output, and captures the first *.modal.run URL.
  4. The URL is saved to .env as TASK_APP_URL, so you can immediately run uvx synth-ai smoke --url "$TASK_APP_URL".

Useful flags

FlagDescription
--nameOverride the Modal app name. Default is whatever literal string you used in modal.App(...).
--modal-cli PATHPoint to a custom Modal binary (helpful when using uv tool install modal).
--dry-runPrint the exact Modal CLI command instead of executing it (deploy mode only).
--env PATHLoad additional .env files; secrets found there are inlined before uploading.
Note: --modal-mode serve cannot be combined with --dry-run, and MCP-based executions block dry runs entirely for safety.

After deployment

  1. Sanity check the service: curl -H "X-API-Key: $ENVIRONMENT_API_KEY" "$TASK_APP_URL/health".
  2. Record the URL in your RL or prompt-optimization configs (e.g., task_app_url = "https://my-task-app.modal.run").
  3. Optionally re-run the local smoke test against the Modal URL:
    uvx synth-ai smoke --url "$TASK_APP_URL" --env-name crafter
    
  4. Any time you change code, rerun the same deploy command; Modal takes care of building the image, uploading differences, and recycling the container.