How Uptime Kuma Handles Notification Retry Logic and Queuing

Uptime Kuma delegates all retry logic to individual notification providers rather than implementing a central queue, with only Google Chat featuring explicit client-side retries while most providers rely on immediate dispatch or external service parameters.

Uptime Kuma’s notification architecture in the louislam/uptime-kuma repository follows a lightweight, provider-driven design. When a monitor detects a state change, the system synchronously dispatches alerts without an internal message queue. This approach shifts the burden of reliability and retry handling to the specific notification channels or their underlying APIs.

Core Notification Dispatch Flow

State Detection and List Gathering

When a heartbeat is processed, the Monitor.isImportantForNotification method determines if the state change warrants an alert. The Monitor.getNotificationList method then retrieves all enabled notification configurations linked to that monitor from the database.

The Synchronous Send Loop

Inside server/model/monitor.js, the Monitor.sendNotification method iterates through the notification list and awaits each call to Notification.send. Errors are caught and logged via log.error, but the loop continues without retrying failed dispatches.

// server/model/monitor.js (simplified)
for (let notification of notificationList) {
    try {
        await Notification.send(
            JSON.parse(notification.config),
            msg,
            monitor.toJSON(preloadData, false),
            heartbeatJSON
        );
    } catch (e) {
        log.error("monitor", "Cannot send notification to " + notification.name);
        log.error("monitor", e);
    }
}

This means a network failure or 5xx error results in a single attempt per heartbeat cycle. The monitor proceeds to the next notification immediately, regardless of success or failure.

Provider-Specific Retry Logic

Unlike distributed systems with centralized retry queues, Uptime Kuma pushes reliability concerns into the concrete provider implementations. Each provider in server/notification-providers/ handles failures differently based on the external service's capabilities.

Google Chat – Explicit Client-Side Retry

The Google Chat provider implements the most robust retry logic in the codebase. Located in server/notification-providers/google-chat.js, it wraps HTTP requests in a while-loop that specifically handles HTTP 429 (rate-limit) responses.

// server/notification-providers/google-chat.js
const post = async (url, data, config) => {
    let retries = notification.googleChatMaxRetries || 1;
    retries = Math.min(retries, 10);
    while (retries > 0) {
        try {
            await axios.post(url, data, config);
            return;
        } catch (error) {
            if (error.response && error.response.status === 429) {
                retries--;
                if (!retries) throw error;
                const delay = 60000 + Math.random() * 120000;
                await new Promise(r => setTimeout(r, delay));
            } else {
                throw error;
            }
        }
    }
};

This provider respects a configurable googleChatMaxRetries setting (capped at 10) and implements randomized backoff delays between 60 and 180 seconds.

Pushover – Delegated Service-Side Retry

The Pushover provider takes the opposite approach, passing retry responsibility to the external service. In server/notification-providers/pushover.js, the payload includes retry and expire parameters that instruct Pushover's infrastructure to handle retransmission.

// server/notification-providers/pushover.js
let data = {
    message: msg,
    user: notification.pushoveruserkey,
    token: notification.pushoverapptoken,
    priority: notification.pushoverpriority,
    retry: "30",      // Retry every 30 seconds
    expire: "3600",   // Stop retrying after 1 hour
    html: 1,
};
await axios.post(url, data, config);

No client-side retry logic exists; the provider relies entirely on Pushover's native retry mechanism.

SMSEagle – Queue Status Awareness

The SMSEagle provider in server/notification-providers/smseagle.js checks the API response for a "queued" status indicator. While it validates that messages were accepted into the provider's queue, it does not implement automatic retries for failed submissions.

// server/notification-providers/smseagle.js
const result = await axios.post(url, data, config);
if (result.data.queued && result.data.queued.length > 0) {
    // Messages queued successfully
} else {
    throw new Error("Failed to queue messages");
}

Standard Providers – Immediate Single Attempt

Most other providers (Discord, Telegram, Slack, etc.) perform a single axios.post request without any retry wrapper. If the request fails, the error bubbles up to Monitor.sendNotification, gets logged, and the system proceeds to the next notification or monitor check.

Absence of Central Queuing Infrastructure

Uptime Kuma intentionally omits a background job queue (such as Bull or RabbitMQ) for notifications. The LimitQueue utility found in server/utils/limit-queue.js is reserved for uptime statistics calculations, not notification buffering.

Because notifications are dispatched synchronously within the monitor's heartbeat processing loop, failed alerts rely on the periodic retry provided by the next scheduled check. When the monitor runs again and the state remains unchanged (or changes again), the notification system triggers another send attempt.

Summary

  • Uptime Kuma uses synchronous, provider-driven notification dispatch without a central message queue.
  • The Monitor.sendNotification method in server/model/monitor.js catches and logs errors but does not retry failed sends at the framework level.
  • Google Chat is the only provider with built-in client-side retry logic, handling HTTP 429 responses with configurable backoff and jitter.
  • Pushover delegates retry handling to the external service via retry and expire payload parameters.
  • SMSEagle verifies queue acceptance status but does not retry failed requests automatically.
  • Most providers perform single-attempt HTTP requests; reliability ultimately depends on the next monitor heartbeat cycle for re-notification.

Frequently Asked Questions

Does Uptime Kuma have a built-in retry queue for failed notifications?

No. The system does not implement a central retry queue or background job processor for notifications. Failed sends are logged in Monitor.sendNotification and skipped, relying on the monitor's next heartbeat to trigger a new notification attempt if the state persists.

Which notification providers in Uptime Kuma support automatic retries?

Only the Google Chat provider features explicit client-side retry logic with exponential backoff for rate-limited requests. Pushover supports retries by passing parameters to the Pushover API, letting the external service handle retransmission. Most other providers perform single attempts without retry logic.

How does Uptime Kuma handle rate limiting from notification APIs?

Handling is provider-specific. The Google Chat provider detects HTTP 429 responses and enters a retry loop with 60-180 second delays. Other providers do not implement specific rate-limit handling; they either fail immediately or rely on the external service's internal queue mechanisms.

What happens if a notification fails to send during a monitor check?

The failure is caught in the try-catch block within Monitor.sendNotification, logged via log.error, and the monitor continues processing. The system does not block on notification failures, nor does it enqueue the message for later delivery. The next monitor heartbeat serves as the natural retry mechanism.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →