GoAlert v0.35.0
It's been nearly a year since v0.34.1, and v0.35.0 brings a substantial set of improvements for both administrators and on-call users. Highlights include multi-ack escalation policy steps, private contact methods, an alerts view for escalation policies, richer webhook payloads, new webhook and Slack hardening options for admins, and continued progress on Universal Integration Keys and the job queue architecture.
⚠️ Upgrade Notes
- Database migrations: This release includes 7 new migrations. They are applied automatically on startup (or via
goalert migrate). As always, take and validate a full backup before upgrading.- Three migrations add database triggers that enqueue engine jobs directly for alert status, rotation, and signal changes (see Job Queue: Database-Driven Events below). These replace the in-process event bus and are transparent to operators, but you will see new trigger functions in the schema.
- Remaining migrations add columns for labels on escalation policies/schedules/rotations, the
multi_ackstep setting, and theprivatecontact method flag.
- All new behavior is opt-in. Multi-ack, private contact methods,
Webhook.BlockPrivateAddresses, andSlack.DisableBroadcastThreadRepliesall default to off and preserve existing behavior unless enabled. - Webhook payload changes are additive. New JSON fields were added to alert-related webhook payloads; no existing fields were removed or renamed.
- Building from source now requires Go 1.26.
🚀 New Features
Multi-Ack Escalation Policy Steps
Escalation policy steps now have an optional multi-ack setting. When enabled, acknowledging an alert no longer silences notifications for everyone else on that step. Each user activated by the step keeps getting notified until they individually acknowledge.
This is designed for teams where more than one person is expected to respond to every alert: primary/secondary pairs, onboarding shadows, or "whole team on call" policies for major outages. Previously the only workaround was telling people not to acknowledge so they wouldn't stop notifications for others.
What doesn't change:
- Escalation to the next step still stops once the first person acknowledges
- Closing an alert still cancels all pending notifications
- Steps without multi-ack behave exactly as before, even when mixed with multi-ack steps in the same policy
On a multi-ack step, each user's acknowledgment is recorded in the alert log, so you can see who came online for an incident.
This is an MVP intended to be safe to opt into; we'll refine it based on feedback. Known gaps: alert bundle messages count unacknowledged alerts (so a multi-ack user may see "0 unacknowledged"), and acknowledging an already-acknowledged alert from the web UI does not count as your individual acknowledgment.
API: multiAck on EscalationPolicyStep, and optional multiAck on CreateEscalationPolicyStepInput / UpdateEscalationPolicyStepInput.
1 PR by @mastercactapus
Private Contact Methods
Users can now mark a contact method as private, hiding its details (phone number, email address, etc.) from everyone except the owner. This addresses a long-standing request to avoid exposing personal contact details to all users of a GoAlert instance.
- Private is off by default and does not retroactively change existing contact methods; users can edit an existing contact method to make it private (or public again).
- Private contact methods show a
(PRIVATE)label to the owner. - When another user views a profile, private contact methods are omitted, and notification rules that reference them display
PRIVATEinstead of the destination. - Note for administrators: private contact methods are hidden from all other users, including admins. Notifications are still delivered normally.
Owner's view:
Other users' view:
API: new private field on contact methods, and optional private on the create/update inputs.
1 PR by @mastercactapus
Alerts View for Escalation Policies
Escalation policy detail pages now have an Alerts quick-link that shows alerts across all services assigned to that policy. This makes it easy to see everything a given on-call team is responsible for in one place, with the same filtering and bulk actions available on the service alerts page.
1 PR by @mastercactapus
- ui/escalation-policy: Add Alerts sub-page by @mastercactapus in #4469
Multiple Users per Temporary Schedule Shift
When adding a shift to a temporary schedule, you can now select multiple users at once instead of adding each one individually. Each selected user gets their own shift for the chosen time range.
1 PR by @Arunkumarlnr
- Allow multiple user selection in Temp Schedules by @Arunkumarlnr in #4432
Labels for Escalation Policies, Schedules, and Rotations (API Only)
Labels, previously available only on services, can now be set on escalation policies, schedules, and rotations via the GraphQL API. This enables the same ownership/team/cost-center tagging patterns across all of these resource types.
UI support and searching these types by label are planned for a future release.
API: labels field on EscalationPolicy, Schedule, and Rotation; optional labels on the corresponding create inputs; setLabel now accepts these types as targets.
1 PR by @mastercactapus
Richer Webhook Payloads
Alert-related webhook notifications now carry significantly more context, making it easier to build automations and integrations without a follow-up API call:
| Payload type | New fields |
|---|---|
AlertStatus
| Summary, Details, ServiceID, ServiceName, Meta, GoAlertURL
|
Alert
| GoAlertURL
|
AlertBundle
| GoAlertURL
|
AlertStatusBundle
| GoAlertURL
|
GoAlertURL is a direct link to the alert (or the service's alert list for bundles), built from the configured public URL. Meta contains the alert's key/value metadata.
Example AlertStatus payload:
{
"AppName": "GoAlert",
"Type": "AlertStatus",
"AlertID": 79694,
"Summary": "Disk usage above 90%",
"Details": "Volume /data on db-01",
"ServiceID": "9d1a4f7e-3a2c-4d6b-8f0e-2b7c1e5a9d34",
"ServiceName": "Primary Database",
"Meta": { "host": "db-01", "region": "us-east" },
"LogEntry": "Acknowledged by Jane Doe",
"GoAlertURL": "https://goalert.example.com/alerts/79694"
}1 PR by @MichaelUray
- Added more json fields to AlertStatus webhook notification by @MichaelUray in #4445
🛡️ Administration & Operations
Webhook: Block Private Network Destinations
A new Block Private Addresses toggle in the Admin → Webhook section rejects webhook deliveries to private, loopback, and link-local addresses (e.g. 10.0.0.0/8, 127.0.0.1, 169.254.169.254). The check happens at connection time against the resolved IP, so DNS names and redirects that resolve to internal addresses are covered as well. Blocked deliveries fail permanently with "destination address is not allowed by administrator" rather than retrying.
This is strictly opt-in and off by default. It was prompted by an external report and is treated as hardening rather than a vulnerability fix: webhooks are disabled by default, the request shape is fixed, and responses are never read.
Two related clarifications for admins:
- The
Webhook.AllowedURLsdescription and the webhook documentation now make explicit that an empty allowlist permits any destination, including internal ones. If you've enabled webhooks, review this setting. - If outbound requests go through an HTTP proxy, the proxy performs the dial, so destination policy must be enforced at the proxy. The setting description notes this.
2 PRs by @CameronJHall, @mastercactapus
- webhook: add opt-in blocking of private network destinations by @mastercactapus in #4562
- chore: ensures client is initialized safely by @CameronJHall in #4564
Slack: Option to Keep Thread Replies in the Thread
When an alert is acknowledged or closed, GoAlert replies in the original Slack message's thread and, until now, always broadcast that reply to the main channel. A new Disable Broadcast Thread Replies toggle (Admin → Slack) keeps status updates in the thread only, reducing noise in busy channels. Default is off (existing behavior).
1 PR by @AdityaHebballe
Application Name Applied Consistently
The General.ApplicationName setting is now used in the navigation bar and on the login page. Previously these were hardcoded to "GoAlert" even though the browser tab title respected the setting.
1 PR by @mattklaver-hs
Remote Monitor: Error API Key Now Optional
The goalert monitor remote monitor no longer requires an ErrorAPIKey in its config. If omitted, a warning is logged instead, which is convenient for test or staging monitors that don't need error reporting.
1 PR by @mastercactapus
🛠️ Universal Integration Keys (Experimental)
Continued enhancements to Universal Integration Keys (enabled with --experimental=univ-keys):
- Colored Slack signal messages: Slack channel actions now accept a
colordynamic parameter, supportinggood,warning,danger, or a#RRGGBBhex value. Unset or invalid values fall back to the default blue. See the UIK documentation for details. sendSignalmutation: Send a signal message directly to a service via GraphQL, for cases where the programmability of a full integration key isn't needed.- Auto-close fix: Alerts created through a UIK rule with a configured dedup key were being stored with the default summary+details hash instead, which prevented upstream systems from closing them automatically. The configured dedup key is now applied on create/update.
3 PRs by @CameronJHall, @KatieMSB, @mastercactapus
- feat: adds support for color on uik messages by @CameronJHall in #4536
- uik/signals: add
sendSignalmutation to directly schedule signal messages by @mastercactapus in #4501 - Fix auto closing by @KatieMSB in #4522
🔧 System Improvements
Job Queue: Database-Driven Events
Continuing the job queue migration from v0.33/v0.34, alert status updates, rotation changes, and signal processing are now triggered by database triggers that enqueue jobs directly (using LISTEN/NOTIFY), replacing the in-process event bus and the trigger → queue → poll fallback path for rotations. This guarantees no missed events regardless of which instance made the change, and is another step toward first-class multi-instance deployments.
A follow-up fix ensures these trigger-created jobs land in their dedicated queues rather than the default queue, so they're picked up immediately instead of waiting on the polling fallback.
3 PRs by @mastercactapus
Quieter Alert Logs
Repeated deduplication entries for the same alert are now debounced: if a duplicate arrives within 5 seconds of a previous dedup log entry, no additional entry is written. This keeps alert logs readable for noisy sources.
1 PR by @mastercactapus
🐛 Bug Fixes
- Slack account linking no longer fails when a Slack profile has empty
user.nameorusernamefields; linking is only rejected if both the user ID and team ID are missing. - Temporary schedules now reliably render a day header for every day, fixing an intermittent missing-subheader issue.
2 PRs by @Lanithane, @mastercactapus
- slack: only reject linking if we truly cannot complete the operation by @mastercactapus in #4440
- temp sched: guarantee every day header is rendered by @Lanithane in #4450
🏗️ Behind the Scenes
Go 1.26 and Toolchain Updates
GoAlert now builds with Go 1.26, along with updates to all Go dependencies and the build environment image. Deprecated River and net/http APIs were updated accordingly.
3 PRs by @AlaricWhitney, @KatieMSB, @mastercactapus
UI Framework: Material UI v6
The web UI was upgraded from MUI v5 to v6, with list components migrated to the newer CompList implementation as a prerequisite. This is largely invisible to users but keeps the frontend on a supported foundation.
2 PRs by @Lanithane, @mastercactapus
TypeScript Migration
The ongoing JavaScript → TypeScript migration continues, covering the wizard, actions, and remaining top-level config files.
Testing, CI, and Repository Housekeeping
Fixed several deadlock scenarios in the smoke test harness that caused flaky tests, updated the PR size labeler to work with forks, enabled DCO (Developer Certificate of Origin) checks per org policy, and removed the repo-level code of conduct in favor of the org-wide one.
5 PRs by @KatieMSB, @mastercactapus, @ospo-compliance-bot
📦 Dependencies
This release includes routine dependency updates for Go modules, JavaScript packages, and GitHub Actions, including gRPC, cel-go, Vite, Storybook, and CodeQL.
22 PRs by @dependabot[bot]
Note: The demo container is published with a separate commit, as the Makefile required a fix.
❯ docker run --rm -it docker.io/goalert/demo:v0.35.0 goalert version
Version: v0.35.0-demofix.1
GitCommit: 524cb0b73540616898fe85de371edd49e8bb1fcc (clean)
BuildDate: 2026-09-21T16:58:50Z
GoVersion: go1.26.6 (gc)
Platform: linux/amd64
Migration: cm-private (#281)
🙏 New Contributors
Thanks to our new contributors who helped make this release possible:
- @Arunkumarlnr made their first contribution in #4432
- @AdityaHebballe made their first contribution in #4464
- @mattklaver-hs made their first contribution in #4482
- @MichaelUray made their first contribution in #4445
- @CameronJHall made their first contribution in #4536
Full Changelog: v0.34.1...v0.35.0