I played around with trying to run a Temporal worker on Modal. I didn’t do a ton of research upfront – I just kind of gave it a shot. I suspect this isn’t possible. Both use Python magic to do the things they do. This is what I tried.

import asyncio
import os
import modal
from temporalio import activity, workflow
from temporalio.client import Client, TLSConfig
from temporalio.worker import Worker

@activity.defn
async def my_activity(name: str) -> str:
    return f"Hello, {name}!"

@workflow.defn
class MyWorkflow:
    @workflow.run
    async def run(self, name: str) -> str:
        return await workflow.execute_activity(
            my_activity, name, start_to_close_timeout=60
        )

async def worker_main():

    client = await Client.connect(
        "my.namespace.tmprl.cloud:7233",
        namespace="my.namespace",
        tls=TLSConfig(
            client_cert=bytes(os.environ["TEMPORAL_CLIENT_CERT"], "utf-8"),
            client_private_key=bytes(os.environ["TEMPORAL_CLIENT_KEY"], "utf-8"),
        ),
    )
    worker = Worker(
        client,
        task_queue="modal-task-queue",
        workflows=[MyWorkflow],
        activities=[my_activity],
    )
    await worker.run()


stub = modal.Stub("temporal-worker")

@stub.function(
    image=modal.Image.debian_slim().pip_install(
        [
            "temporalio==1.5.1",
        ]
    ),
    secrets=[modal.Secret.from_name("modal-temporal-worker")],
)
def main():
    asyncio.run(worker_main())

if __name__ == "__main__":
    with stub.run():
        main.call()

Run with

modal run worker::main

The error trace looked like

    RESPONSES: Mapping[int, Tuple[str, str]] = http.server.BaseHTTPRequestHandler.responses
                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/temporalio/worker/workflow_sandbox/_restrictions.py", line 845, in __getattribute__
    state.assert_child_not_restricted(__name)
  File "/usr/local/lib/python3.11/site-packages/temporalio/worker/workflow_sandbox/_restrictions.py", line 697, in assert_child_not_restricted
    raise RestrictedWorkflowAccessError(f"{self.name}.{name}")
temporalio.worker.workflow_sandbox._restrictions.RestrictedWorkflowAccessError: Cannot access http.server.BaseHTTPRequestHandler.responses from inside a workflow. If this is code from a module not used in a workflow or known to only be used deterministically from a workflow, mark the import as pass through.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/pkg/modal/_container_io_manager.py", line 488, in handle_input_exception
    yield
  File "/pkg/modal/_container_entrypoint.py", line 134, in run_input
    res = imp_fun.fun(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/root/worker.py", line 52, in main
    asyncio.run(worker_main())
  File "/usr/local/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "/root/worker.py", line 32, in worker_main
    worker = Worker(
             ^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/temporalio/worker/_worker.py", line 292, in __init__
    self._workflow_worker = _WorkflowWorker(
                            ^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/temporalio/worker/_workflow.py", line 118, in __init__
    raise RuntimeError(f"Failed validating workflow {defn.name}") from err
RuntimeError: Failed validating workflow MyWorkflow
╭─ Error ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ Failed validating workflow MyWorkflow                                                                                        │
╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

I believe Temporal needs to patch the Python runtime in Worker executions to make Workflow code deterministic. It seems what Modal does may be interfering with that.

I don’t know if running a Temporal worker on Modal is possible or if I will trying to come back to this by it was interesting trying to mash these two together.