Deployments
Line up every chart with the release behind it.
Unhandled records a deployment when a service starts reporting a new version, and when your pipeline declares one. Both land in one history, show up as markers on your charts, and each one compares the hour before the release with the hour after.
- Detected for you
- From the service.version your spans carry
- Declared from CI
- unhandled deploy reads six CI providers
- Markers on charts
- Overview, dashboards, traces, logs, issues
- Before vs after
- 5xx rate, P95, throughput and exceptions
See what a release changed
Each deployment page compares the hour before the release with the hour after it, for the same service. A deployment less than an hour old compares against the time so far.
- 5xx error rate, P95 latency, throughput and exceptions on each side
- Exception fingerprints first seen within 24 hours after the release
- Issues sorted into new, regressed, still occurring and resolved by this deploy
Declare it from your pipeline
unhandled deploy sends one request with a project deploy key. In CI it reads the commit, the actor, the run URL and the repository from the provider, so a step needs little more than the service name.
- Works out of the box on GitHub Actions, GitLab CI, CircleCI, Buildkite, Bitbucket Pipelines and Cloudflare Workers Builds
- A started call and a succeeded call in the same run update one deployment
- A failed request prints a warning and exits 0, so it never fails your deploy unless you pass --strict
jobs:
ship:
runs-on: ubuntu-latest
env:
UNHANDLED_API_KEY: ${{ secrets.UNHANDLED_DEPLOY_KEY }}
UNHANDLED_ENVIRONMENT: production
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 24
- run: npm ci
- run: npx --no-install unhandled deploy --service shop-api --status started
- id: rollout
run: ./scripts/rollout.sh
- if: success()
run: npx --no-install unhandled deploy --service shop-api --status succeeded
- if: failure() && steps.rollout.outcome == 'failure'
run: npx --no-install unhandled deploy --service shop-api --status failedA marker on every time axis
Deployments appear on the charts of the overview, the app dashboards, Traces, Logs, Exceptions, issue pages and alert details. A latency jump lines up with the release that started it.
- Filled markers were declared from CI, outlined ones were detected from telemetry
- Hover a marker for the service and version, click it to open the deployment
- Failed and canceled deployments stay in the history without a marker
The endpoint that got slower
Below the comparison, the deployment lists the endpoints whose P95 latency rose after the release, largest change first, so you know which route to open.
- Up to 10 endpoints whose P95 rose by at least 5 ms, with 20 or more calls on each side
- Links to traces, logs and exceptions for the service and version, 30 minutes either side
- The commit, linked to the repository, the CI run, the actor and the previous release
Every issue knows its release
The issue page names the release an issue was first seen in and the last one it was seen in, each linked to its deployment. The same markers sit under its chart.
- Search first_release:2.14.0 to list the issues a release introduced
- A regressed issue names the release it came back in
- Resolve an issue in a release through the API, and only that release or a later one reopens it
No pipeline changes to start
Every minute Unhandled reads each service's entry spans and records a deployment when a new service.version takes more than half of them. The Node SDK and the AdonisJS agent find the version on their own.
- Without serviceVersion, they read UNHANDLED_VERSION, APP_VERSION, the commit your platform or CI sets, or the local .git directory
- Placeholders such as 0.0.0, 1.0.0, unknown, dev and latest are ignored
- A declaration within 60 minutes that matches the version or commit merges into the same deployment
import env from '#start/env'
import { defineConfig } from '@unhandled/agent-adonisjs'
export default defineConfig({
dsn: env.get('UNHANDLED_DSN'),
environment: env.get('APP_ENV'),
serviceVersion: env.get('APP_VERSION'),
})Works with
However you ship
- Unhandled CLIunhandled deploy in any pipeline, with a dry run to check it.
- GitHub ActionsCommit, actor, run URL and repository read from the run.
- GitLab CITag pipelines can send the tag as the version.
- Deployments APIOne POST from any runtime, with status, metadata and an external ID.
- OpenTelemetryAny SDK that sets service.version gets detected deployments.
- MCP serverAgents list deployments and read each one's impact.
Questions
Try it on your own stack.
Every feature is on every plan, priced by the telemetry you send.