DEV Community

Marc Duiker for Diagrid

Posted on

What's New in the Dapr Dev Dashboard for Local Workflow Debugging

Introduction

The Diagrid Dapr Dev Dashboard has shipped nine releases since the first announcement, and if you build Dapr Workflow apps on your machine, several of them change how you debug. The Workflows page now loads thousands of instances in milliseconds instead of hanging. A new State page lets you browse, add, and delete the records in your state store. A context-aware Dapr CLI drawer gives you the exact dapr workflow command for the instance you are looking at, with the IDs already filled in. On top of that, you can publish test messages to a topic, check component secret references, and keep the app list and logs tidy as your dev session grows. This post covers the highlights from v0.0.14 to v0.0.22 and shows how each one helps you find out why a workflow behaves the way it does. You can find an overview of the dashboard on the Dapr Dev Dashboard page.

Why it matters

A Dapr Workflow is long-running and event-sourced. Its state lives in your state store as a history of events, so when an instance stalls or returns the wrong output on your machine, the answer is somewhere in that history. Finding it usually means querying the state store by hand, looking up the right CLI flags, or adding log statements and running the workflow again.

The dashboard already took care of the basics. It finds the apps you start with dapr run, .NET Aspire, Docker Compose, or Testcontainers, tails their sidecar and app logs, shows workflow event history, and generates component and resiliency YAML from a form. The recent releases go further into the questions you ask while debugging: what did the workflow write, which instances are stuck, and what do you run to fix them.

What's new

A Workflows page that stays fast on large state stores

Run a load test or leave a fan-out workflow running overnight, and your local state store can easily hold thousands of workflow instances. Earlier versions of the Workflows page slowed to a crawl at that point, because every instance load scanned the store for that instance's keys: a full keyspace SCAN on Redis, a LIKE table scan on the SQL backends.

The dashboard now loads instances the way Dapr loads its own state. It reads the instance's metadata record, builds the history keys from it, and fetches them in one bulk read. The status counts on the tabs are cached per workflow and only reload when that workflow changes, while the workflow detail page always reads fresh data. These are the numbers for 2,000 instances, before and after:

Backend List page Status counts, first load Status counts, refresh
SQLite 341 ms → 10 ms 13.7 s → 0.20 s 15.9 s → 0.03 s
Redis 702 ms → 39 ms 28.6 s → 1.28 s 29.4 s → 0.10 s
PostgreSQL 386 ms → 18 ms 14.9 s → 0.17 s 14.7 s → 0.02 s
MongoDB 200 ms → 7 ms 8.1 s → 0.28 s 8.1 s → 0.08 s

The page also behaves better while it loads. When you change a status tab, search, or page, the current rows stay on screen with Updating… in the pager until the new results arrive. The tabs show … until their counts are in, instead of a misleading 0.

Workflows page with status tabs and a large list of instances

Browse and edit state with the new State page

Workflow activities often read and write application state, and when a workflow produces the wrong result, you want to see what is actually in the store. The new State page lists the records in any connected state store, with the key, app prefix, value preview, size, version (etag), and TTL for each one. You can switch between stores from the page header and expand a row to see the full value, with a copy button. The dashboard opens Redis, PostgreSQL, SQLite, and MongoDB stores directly.

To find a record, filter by app prefix or search by key. If Dapr stored a value base64-encoded, flip Decode base64 to read it. Workflow history and actor state share the keyspace with your app's records, so they are hidden by default. Turn on Show internal keys when you want to look at the raw workflow keys.

You can also change state. + New record writes a record under <app-id>||<key>, the same key shape Dapr uses, which is handy for seeding data a workflow reads. An existing key is refused unless you tick Overwrite. You can delete records one at a time or select several, with a confirmation step before anything is removed.

Two limits are worth knowing. There is no "last modified" column, because Dapr state components do not expose a modification timestamp. And a store the dashboard cannot open directly, such as an in-memory store inside a Testcontainers app, cannot be browsed, since Dapr's state API has no way to list keys.

State page with an expanded record

Get the right Dapr CLI command for every page

The dashboard is great for looking around, but sometimes you want the terminal, for example to put a command in a script or rerun it after a restart. A collapsible Dapr CLI drawer on the right edge of the dashboard now shows the Dapr CLI commands that match the page you are on.

On the workflow views, that means dapr workflow list, history, suspend, resume, terminate, and purge, with the app ID and workflow instance ID from the current view already filled in. Every command has its own copy button, so you do not have to look up flags or paste IDs by hand.

Other pages get their own set: dapr list on the applications overview, dapr stop --app-id <id> on an app's detail page, dapr scheduler commands on actors, and dapr publish on subscriptions. The commands target self-hosted mode, so you will not find the Kubernetes -k variants here.

Workflow detail page with the Dapr CLI drawer open

Publish test messages from the Subscriptions page

If a message on a topic starts your workflow, testing it used to mean writing a small client or reaching for curl. Each row on the Subscriptions page now has a Publish button that sends a real message to the topic through the app's own sidecar. You edit the JSON or text payload, choose a content type, and optionally set ttlInSeconds or rawPayload under Advanced.

When the publish succeeds, you get a direct link to the app's logs, so you can watch the message get handled. If daprd rejects it, for example because the pub/sub component is unknown, the error shows up inline in the dialog.

The page also tells you more about each subscription. A Type column shows whether it is declarative or programmatic, and subscriptions with several routing rules expand to show each rule's match expression and path.

Publish dialog on the Subscriptions page

See which component secrets resolve

Your workflow state store is a Dapr component, and if its password comes from a secret store, one typo in a secretKeyRef is enough to break it. Until now, a component with a missing secret looked exactly like a working one. The dashboard now resolves secretKeyRef values against the secretstores.local.file and secretstores.local.env stores the same way daprd does, including nested keys and custom separators. envRef is supported too.

A component's detail page has a new Secret references panel. For each field it shows the store, the key, and a status, and when resolution fails, the exact file path it tried. Values stay masked; Reveal shows one field at a time and masks it again after 30 seconds. The Components list marks any component with an unresolved reference with a warning sign. When a state store cannot connect because of a secret, the State page names that secret instead of showing a vague connection error.

Non-local secret stores, such as HashiCorp Vault or Azure Key Vault, are listed but never read.

Cleaner logs and app list during long dev sessions

On the Logs page, choosing what to tail is now a single Target dropdown, grouped into Applications and Control plane. When you view an app, a compact daprd | app toggle lets you show the sidecar logs, the app logs, or both side by side.

After a few rounds of starting and stopping apps, the Applications list fills up with stopped entries. Clear inactive removes all fully stopped apps in one click, and each stopped row has its own Remove action. Removing an app only hides it from the list; if it starts again, it reappears. Restart and Start now only appear for Docker Compose apps, where they work reliably. For dapr run apps you get Stop, with a hint to restart from your terminal.

How to get started

The dashboard is a single self-contained binary with no runtime dependencies, available for macOS and Linux (amd64 and arm64) and Windows (amd64). Install it with the one-liner for your OS.

macOS or Linux:

curl -sSL https://raw.githubusercontent.com/diagridio/dev-dashboard/main/scripts/install.sh | sh
Enter fullscreen mode Exit fullscreen mode

Windows (PowerShell):

iwr -useb https://raw.githubusercontent.com/diagridio/dev-dashboard/main/scripts/install.ps1 | iex
Enter fullscreen mode Exit fullscreen mode

Start the dashboard next to your running Dapr apps:

diagrid-dev-dashboard
Enter fullscreen mode Exit fullscreen mode

It serves the UI on http://localhost:9090 and opens your browser. If no Dapr apps are running yet, the Applications page points you to the Dapr Quickstarts so you have something to try it on. The GitHub repo has the full README and the release notes for every version.

Summary

Most of the recent work on the Dapr Dev Dashboard targets the questions you ask while debugging a workflow on your machine: what state did it write, which instances are stuck, and which command gets them moving again. A faster Workflows page, the new State page, and the Dapr CLI drawer answer those questions from your browser. You can also publish a test message from the subscription it targets, and spot a missing secret before it shows up as a vague connection error.

Install the dashboard with the one-liner for your OS, go install, or the container image from the GitHub repo, and run diagrid-dev-dashboard next to your Dapr apps. For an overview of everything it can do, visit the Dapr Dev Dashboard page.

Once the dashboard is open, try the Konami code: type ↑ ↑ ↓ ↓ ← → ← → B A in your browser and see what appears. There is a fresh challenge every day, and you can share your best run with an execution ID so others can watch it back.

Top comments (0)