---
title: "Test storage automations locally"
description: "Watch a local directory with the Tilebox CLI, register a Python storage-event automation, and trigger a job by writing a file."
image: "https://tilebox.com/images/tilebox-docs-social-preview.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://tilebox.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Test storage automations locally

Use a [local storage location](/docs/workflows/automations/storage-events#local-storage-locations) to test storage-event automations without configuring a cloud bucket or cloud notifications. The CLI watches a directory and sends events to Tilebox. A Python runner on the same machine reads the files and executes the triggered tasks.

Local storage locations also work for processing files locally. Files stay on your machine, while Tilebox coordinates the jobs. The watcher and runner need a connection to Tilebox.

## Prerequisites

- Install the [Tilebox CLI](/docs/cli#installation) and [uv](https://docs.astral.sh/uv/getting-started/installation/).
- Set up [authentication](/docs/authentication). The CLI and Python SDK both use `TILEBOX_API_KEY`; saving it in your shell configuration makes it available in new terminals.

## Create a project

The shell examples use Bash. Create a project and install the Python workflow SDK:

```bash
mkdir local-storage-test
cd local-storage-test
uv init --no-package
uv add tilebox-workflows
mkdir input
```

## Register and watch the directory

Register `input` as a local storage location:

```bash
tilebox storage-location create local --path ./input --name "Local automation test"
```

Copy the returned location ID. Replace `<storage-location-id>` with it throughout this guide. The CLI resolves `--path` to an absolute path, so the registered location does not depend on the watcher's working directory.

Start the watcher in this terminal:

```bash
tilebox storage-location watch <storage-location-id>
```

Wait for the `Watching ...` message and leave the command running.

The watcher reports new and modified files in the directory and its subdirectories after one second without changes. Existing, unchanged files are not sent when it starts. It respects `.gitignore` rules by default; use `--no-gitignore` to include ignored files.

## Define the Python task

Open a second terminal in `local-storage-test`. Save this as `local_automation.py`:

```python title="local_automation.py"
from tilebox.workflows import Client, ExecutionContext, Runner
from tilebox.workflows.automations import StorageEventTask


class ReadFile(StorageEventTask):
    def execute(self, context: ExecutionContext) -> None:
        path = self.trigger.location
        data = self.trigger.storage.read(path)
        context.logger.info(
            "Read file",
            path=path,
            size_bytes=len(data),
        )


if __name__ == "__main__":
    client = Client()
    runner = Runner(tasks=[ReadFile])
    runner.connect_to(client).run_forever()
```

`self.trigger.location` is relative to the registered directory, for example `hello.txt` or `nested/hello.txt`, without a leading `/`. `self.trigger.storage.read()` returns the file's bytes. The task logs its path and size, so it works with text, images, and other binary files.

## Register the automation

Save this second file beside `local_automation.py`:

```python title="register_automation.py"
import sys
from uuid import UUID

from tilebox.workflows import Client

from local_automation import ReadFile


client = Client()
automations = client.automations()
location_id = UUID(sys.argv[1])
location = next(
    location
    for location in automations.storage_locations()
    if location.id == location_id
)

automation = automations.create_storage_event_automation(
    "read-local-files",
    ReadFile(),
    triggers=[(location, "**")],
)
print(f"Automation ID: {automation.id}")
```

Select the location by the ID returned by the CLI, then register the automation once:

```bash
uv run python register_automation.py <storage-location-id>
```

Save the printed automation ID for cleanup. Register once: each run creates a new automation.

The pattern `**` matches all files in the directory and its subdirectories. To process only text files, use `**.txt`. See [glob patterns](/docs/workflows/automations/storage-events#glob-patterns) for the syntax, examples, and an online way to test them.

## Start the runner

In the second terminal, start the task runner:

```bash
uv run python local_automation.py
```

Leave it running alongside the watcher on the same machine. Both the automation and this runner use the default cluster. Storage reads use the registered absolute path, so the runner does not need to start in the watched directory. The command above runs from the project directory so `uv` can find the Python environment and script.

## Trigger an event and inspect the job

Open a third terminal in `local-storage-test`. With both the watcher and runner running, create a matching file:

```bash
echo "Hello from local storage" > input/hello.txt
```

After the file has been unchanged for a second and the notification is delivered, the watcher prints:

```text
Event: hello.txt
```

The runner should log `Read file` with `path=hello.txt` and `size_bytes=25`. You can also copy an image or another file into `input` to trigger a job. Check the received event and its triggered jobs:

```bash
tilebox storage-location events <storage-location-id> --limit 10 --json
```

Find `object_key: "hello.txt"` and copy a `job_id` from `triggered_jobs`. Inspect the job in the [Console](https://console.tilebox.com/workflows/jobs) or read its logs:

```bash
tilebox job logs <job-id>
```

To trigger another event, modify the file after the first event has arrived:

```bash
echo "A second update" >> input/hello.txt
```

If no event appears, check that the watcher is still running, the file is inside the registered directory, and no `.gitignore` rule excludes it. If the event has no triggered jobs, check the automation's location ID and glob. If a job stays queued or fails, check the runner's cluster and access to the directory.

## Stop the test

Press Ctrl+C in the watcher and runner terminals.

In the [Console's Automations page](https://console.tilebox.com/workflows/automations), delete `read-local-files`. Use the ID printed during registration to identify it if you have several automations with that name. You can keep the location for another test if you want.

## Use the same task with cloud storage

The same `StorageEventTask` works with local storage, AWS S3, Google Cloud Storage, and Azure Blob Storage. To use `ReadFile` with a cloud location, [connect the bucket or container](/docs/guides/operations/connect-storage), then register an automation with that location's ID. Configure the runner's cloud credentials so `self.trigger.storage.read()` can read the objects. Cloud notifications replace the local CLI watcher; the task code stays the same.

Source: https://tilebox.com/docs/guides/operations/test-storage-automations-locally/index.mdx
