underrunner Sign in Try the live demo

Getting started

From nothing to a ranked list of what you could remove, usually in under an hour. Nobody is emailed and nothing changes until you say so.

1. Connect a cloud, read-only

Go to Sources and add one. Each form shows the exact access it needs and tests it before saving.

Costs are measured where the platform says what each resource costs, and marked as an estimate where it doesn't. For exact AWS costs after discounts, point it at a Cost and Usage Report in CSV form.

If you limit connections by address, as a Snowflake network policy, a Databricks IP access list or a mail server might, Underrunner connects from 46.4.105.186 and 2a01:4f8:141:23e::2.

2. Tell it who's who

Ownership decides who gets asked, so it's worth a few minutes.

The People screen also finds what things with no owner have in common, like a name, a tag or an account, and suggests whose they are from what's known about their neighbours. Assigning a group adds an owner rule, so anything new that matches goes to the same place. Below that, it lists the biggest things still with no clear owner, with a best guess you can accept in one click.

3. Define the scope

A scope is a team, an org, or all of engineering. Everything is then inside it, outside it, or unsure. Only "inside" can be asked about or removed. Preview a scope before saving it to see how each resource falls.

4. Read the queue

The Queue ranks everything by savings times how likely it is to be unneeded. Open one to see the measurements, where they came from, and a command to check them yourself. Filters use the same language everywhere:

Is anything pointing at it? It reads the DNS records in Route 53, Cloudflare, Google Cloud DNS, Azure DNS, DigitalOcean, Linode and Vultr. A load balancer, address or machine that a name still points at says so and ranks lower, whichever cloud it's in, and one that nothing points at says that too. A removal stops if a new name has appeared since it was approved. With Jira connected, an open ticket that mentions something and has moved in the last 90 days counts the same way.

5. Decide how careful to be

Three settings do most of the work, and the safe choice is the default for each.

Connect mail in Settings, with your own mail server or by verifying your domain. To reach people on Slack instead, add a bot token there. Anyone not found in Slack gets the email.

6. What happens to each resource

What a scan costs you

Reading isn't always free, so here's what your own bill sees.

Scans run once a day unless you change it under Settings.

Terraform and other infrastructure code

Anything defined in code goes through the code, because deleting it directly only lasts until the next apply. Add a token with write access to your infrastructure repositories on the GitHub or GitLab source, and Underrunner opens a pull request removing the block once it's approved. Merging and applying it removes the resource. Closing it counts as keeping it. Without the token, the owner gets the change by email. You can see the change in advance on any resource.

Your own rules

In Settings, "Your rules" lets you flag what only you know about ("sandbox, and over a month old"), say who owns what, and protect anything from ever being flagged. Tags like do-not-delete also protect a resource, and the list of them is yours to change.

Connecting an LLM

Optional. With an OpenAI or Anthropic key, flagged resources get a plain explanation of what they probably are and what to ask. You set a monthly budget. It can nudge the ranking a little, and can't advance or approve anything.

Signing in

Single sign-on works with any OpenID Connect provider, once you've verified your email domain. Two-step sign-in with an authenticator app is in Settings for everyone else.

Create an account · Try the demo first · How we handle what you connect

API and webhooks

Make a token in Settings and send it as Authorization: Bearer urt_... on any GET under /api, like /api/candidates or /api/resources?q=provider:aws. Tokens can only read.

A webhook gets one POST for every stage change, in order, retried until your server answers 2xx. Each has the headers x-underrunner-timestamp and x-underrunner-signature. To check it, compute HMAC-SHA256 of timestamp.body with your signing secret, compare it with the hex after sha256=, and refuse anything with a timestamp over five minutes old.

{"id": 812, "at": 1793000000000, "event": "approved", "from_stage": "confirmation", "to_stage": "scheduled", "actor": "priya@acme.com",
 "candidate_id": 41, "savings_per_month": 3910.0, "detail": "",
 "resource": {"id": 77, "name": "ml-feature-store-v1", "type": "rds_instance", "provider": "aws", "account": "4829...", "region": "us-east-1", "native_id": "...", "team": "Machine Learning"}}