# My Cloud Run Worker Died of Stage Fright: A 60-Second Cold-Start Tragedy

_In which import boto3 becomes a murder weapon._

June 18, 2026 · 3 min · Amit Jethva

Cloud Run has a rule that sounds reasonable until it ruins your evening: if your container doesn't get its act together fast enough on startup, it gets a polite `SIGTERM` and a one-way ticket to the void. Around the 60-second cold-start mark, the platform decides you've had your chance.

Our Celery worker kept dying before it could even take the stage. No useful error. Just a worker that booted, blinked, and expired. Classic stage fright.

## The autopsy

The culprit wasn't our code. It was our code's _opening monologue_ - the module-level imports:

```python
# top of aws_scanner.py
import boto3
import numpy
from azure.mgmt.compute import ComputeManagementClient
# ...the entire cloud SDK family reunion, loaded before we did anything
```

Every one of those heavyweight SDKs gets imported the instant Python touches the module. `boto3`, `numpy`, the `azure.mgmt.*` clan, `google.cloud.*` - they all show up at once, each one taking its sweet time. By the time the import party finished unpacking, the cold-start clock had run out and Cloud Run had already sent the SIGTERM.

The worker was being killed _during its costume change_, before the curtain even rose.

## The fix: lazy imports (a.k.a. don't unpack until you need it)

Move the heavy imports _inside_ the functions that actually use them:

```python
def scan(self):
    import boto3            # imported only when we genuinely scan
    client = boto3.client("ec2")
    ...
```

Now startup is featherweight. The SDKs only load when a scan actually runs - well after Cloud Run has decided we're alive and healthy. The worker survives long enough to do its job. Standing ovation.

## The sequel nobody asked for: the test patches broke

Of course, moving the imports broke a pile of tests, because the old patches targeted the _scanner module_:

```python
# used to work, now patches a name that no longer exists there
patch("app.services.aws_scanner.boto3")
```

When the import is lazy, `boto3` isn't an attribute of the scanner module anymore - it's summoned fresh inside the function. So you patch the **source package** instead:

```python
patch("boto3.client")                              # AWS
patch("azure.mgmt.compute.ComputeManagementClient") # Azure
patch("google.cloud.something")                     # GCP
```

The mock has to wait at the door the function will actually walk through, not the one it used to.

## The moral

- **Module-level imports run at import time, not at call time.** On a serverless platform with a startup budget, that's the difference between "deployed" and "deceased."
- Heavy SDK? Import it lazily, inside the function that needs it.
- When you make imports lazy, your test patches have to follow the import to its new home - patch the source package, not the module that re-imports it.

Our worker no longer has stage fright. It just needed to stop trying to memorize the whole script before walking on.

---

_Amit Jethva is the CTO and co-founder of Nuvika Technologies Pvt Ltd, makers of [Fintropy](https://www.nuvikatech.com/Fintropy_Overview.html), a multi-cloud FinOps platform. Learn more at [nuvikatech.com](https://www.nuvikatech.com)._
