What Letting Ana Go Actually Is
It's a lightweight Python-based workflow tool that runs scheduled tasks and data pipelines without the overhead of something like Airflow or Prefect. You install it, point it at a config file, and it executes jobs in sequence or parallel depending on how you set the dependencies up. That's basically the whole pitch. I've been using it for about two years across a few side projects and a couple of production-adjacent scripts at my day job. The initial setup takes maybe ten minutes if you already have Python 3.10+ and pip access. The first time I tried to deploy it in a containerized environment, I hit a weird edge case with cron-like scheduling where the timezone conversion was off by three hours because the host machine and the container were reporting different UTC offsets. I ended up explicitly setting TZ=UTC in the Dockerfile and running the scheduler with --force-timezone utc at startup. Worked fine after that.
Installation and First Run
Install it with pip: pip install letting-ana-go Create a project directory, then add a config file called ana.yml in the root:
Basic Config Structure for Letting Ana Go
The YAML looks like this: jobs: - name: daily_export
Get the Full Details

schedule: "0 2 * * *" command: python export.py depends_on: []
- name: process_data schedule: "30 2 * * *" command: python process.py
depends_on: [daily_export] Then run the scheduler: ana scheduler --config ana.yml

That's it. It'll start executing. The default log output goes to stdout, which is fine for development but becomes a pain once you have more than three jobs. I switched to writing logs to /var/log/ana/ by adding --log-dir to the scheduler args. Much cleaner.
How It Actually Works Under the Hood
Each job gets its own subprocess when it runs. Dependencies are tracked in a simple DAG structure. If job A depends on job B, Ana Go won't start A until B exits with code 0. If B fails, A is skipped for that run cycle. You can also set retry: true on a job, which defaults to three retries with a ten-second backoff. One thing people miss: the scheduler doesn't use Python's built-in threading for job execution. It uses multiprocessing because some of the pipelines it runs do heavy I/O or CPU work, and GIL contention becomes a problem quickly. This means your jobs are fully isolated from each other. That's usually a good thing, but it also means shared state between jobs doesn't exist unless you explicitly use a database or filesystem to pass data around.
Common Pitfalls
The biggest issue I see is relative vs. absolute paths in the command field. If you write python scripts/export.py and your working directory changes between runs, it breaks. Always use absolute paths or set working_dir in the job config. This took me about forty minutes to track down on a Friday night because the logs didn't show a clear error — it just silently failed with exit code 127, which on Linux means "command not found." Another gotcha: the scheduler assumes your system cron timezone matches your local timezone. If they don't, jobs will run at the wrong time. I learned this the hard way when a job meant to run at 2 AM actually ran at 5 AM because the VPS was in UTC and my local machine was in EST. Set the timezone explicitly in the config: timezone: "America/New_York"

When Letting Ana Go Falls Apart
It's not designed for complex data engineering workflows. If you need dynamic DAG generation, parameterized runs, or integration with cloud storage services out of the box, you're better off with Prefect or Dagster. Ana Go excels at simple, deterministic task chains where the logic is straightforward and the dependencies don't change frequently. The monitoring interface is also pretty basic. There's a web dashboard you can enable with --dashboard, but it only shows the last twenty runs per job. If you need retention beyond that, you have to hook it up to an external logging service. I ended up piping the logs to a local Elasticsearch instance, which added complexity but gave me the visibility I needed.
Advanced: Custom Job Handlers
For people who want more control, Ana Go supports custom job handlers. Instead of running a shell command, you can define a Python class that implements the JobHandler interface: from ana_go import JobHandler class MyCustomJob(JobHandler):
def execute(self): your logic here return 0

This is useful when your job needs to interact with shared resources or maintain state between executions. I used this for a job that cleaned up temporary files older than seven days across multiple directories. The shell-command approach would have required spawning a subprocess for each directory, which was slow and messy. The custom handler approach was much cleaner.
Performance Notes
On a typical laptop with 8 GB of RAM, you can comfortably run around twenty concurrent jobs before you start seeing memory pressure. Each job subprocess consumes roughly 50-100 MB depending on what it does. If you're running heavy data transformations, factor in the memory footprint of whatever libraries you're loading inside the job. The scheduler itself is pretty lightweight. It uses about 30 MB of RAM and minimal CPU when idle. The heaviest operation is resolving the dependency graph on startup, which for a config with fifty jobs and complex interdependencies takes about two seconds. Not slow, but noticeable if you're restarting the scheduler frequently during development.
Where to Get It
The source code is on GitHub under the name letting-ana-go. The package is also available on PyPI, so pip install letting-ana-go is the quickest path. There's no official binary release, which means you need a working Python environment. The README covers Docker setup if you want to run it in a container, but the image is community-maintained, not official. I've been running it in production for about eight months now across three separate environments. It handles the job without complaint, and when something does go wrong, the error messages are usually clear enough to debug without digging through source code. That's about as good as it gets for a tool this size.
