diff --git a/packages/core/src/ExceptionlessClient.ts b/packages/core/src/ExceptionlessClient.ts
index d652c635..21f67c3f 100644
--- a/packages/core/src/ExceptionlessClient.ts
+++ b/packages/core/src/ExceptionlessClient.ts
@@ -7,6 +7,7 @@ import { EventContext } from "./models/EventContext.js";
 import { EventPluginContext } from "./plugins/EventPluginContext.js";
 import { EventPluginManager } from "./plugins/EventPluginManager.js";
 import { PluginContext } from "./plugins/PluginContext.js";
+import { allowProcessToExitWithoutWaitingForTimerOrInterval } from "./Utils.js";
 
 export class ExceptionlessClient {
   private _intervalId: ReturnType<typeof setInterval> | undefined;
@@ -92,9 +93,11 @@ export class ExceptionlessClient {
         void SettingsManager.updateSettings(this.config);
       if (initialDelay < interval) {
         this._timeoutId = setTimeout(updateSettings, initialDelay);
+        allowProcessToExitWithoutWaitingForTimerOrInterval(this._timeoutId);
       }
 
       this._intervalId = setInterval(updateSettings, interval);
+      allowProcessToExitWithoutWaitingForTimerOrInterval(this._intervalId);
     }
   }
 
diff --git a/packages/core/src/Utils.ts b/packages/core/src/Utils.ts
index 55ad91b4..52530165 100644
--- a/packages/core/src/Utils.ts
+++ b/packages/core/src/Utils.ts
@@ -308,3 +308,13 @@ export function toError(errorOrMessage: unknown, defaultMessage = "Unknown Error
 
   return new Error(JSON.stringify(errorOrMessage) || defaultMessage);
 }
+
+
+/**
+ * Unrefs a timeout or interval. When called, the active Timeout object will not require the Node.js event loop to remain active
+ */
+export function allowProcessToExitWithoutWaitingForTimerOrInterval(timeoutOrIntervalId: ReturnType<typeof setTimeout> | ReturnType<typeof setInterval> | undefined): void {
+  if (typeof timeoutOrIntervalId === "object" && "unref" in timeoutOrIntervalId) {
+    (timeoutOrIntervalId as { unref: () => ReturnType<typeof setTimeout> | ReturnType<typeof setInterval> }).unref();
+  }
+}
diff --git a/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts b/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts
index 26ca526b..ccae12ac 100644
--- a/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts
+++ b/packages/core/src/plugins/default/DuplicateCheckerPlugin.ts
@@ -1,6 +1,6 @@
 import { InnerErrorInfo } from "../../models/data/ErrorInfo.js";
 import { KnownEventDataKeys } from "../../models/Event.js";
-import { getHashCode } from "../../Utils.js";
+import { allowProcessToExitWithoutWaitingForTimerOrInterval, getHashCode } from "../../Utils.js";
 import { EventPluginContext } from "../EventPluginContext.js";
 import { IEventPlugin } from "../IEventPlugin.js";
 
@@ -25,6 +25,7 @@ export class DuplicateCheckerPlugin implements IEventPlugin {
   public startup(): Promise<void> {
     clearInterval(this._intervalId);
     this._intervalId = setInterval(() => void this.submitEvents(), this._interval);
+    allowProcessToExitWithoutWaitingForTimerOrInterval(this._intervalId);
     return Promise.resolve();
   }
 
diff --git a/packages/core/src/plugins/default/HeartbeatPlugin.ts b/packages/core/src/plugins/default/HeartbeatPlugin.ts
index 46a7c279..ffc543a5 100644
--- a/packages/core/src/plugins/default/HeartbeatPlugin.ts
+++ b/packages/core/src/plugins/default/HeartbeatPlugin.ts
@@ -1,4 +1,5 @@
 import { KnownEventDataKeys } from "../../models/Event.js";
+import { allowProcessToExitWithoutWaitingForTimerOrInterval } from "../../Utils.js";
 import { EventPluginContext } from "../EventPluginContext.js";
 import { IEventPlugin } from "../IEventPlugin.js";
 
@@ -49,6 +50,8 @@ export class HeartbeatPlugin implements IEventPlugin {
         () => void context.client.submitSessionHeartbeat(<string>config.currentSessionIdentifier),
         this._interval
       );
+
+      allowProcessToExitWithoutWaitingForTimerOrInterval(this._intervalId);
     }
 
     return Promise.resolve();
diff --git a/packages/core/src/queue/DefaultEventQueue.ts b/packages/core/src/queue/DefaultEventQueue.ts
index 579c5729..5929ee3d 100644
--- a/packages/core/src/queue/DefaultEventQueue.ts
+++ b/packages/core/src/queue/DefaultEventQueue.ts
@@ -3,6 +3,7 @@ import { ILog } from "../logging/ILog.js";
 import { Event } from "../models/Event.js";
 import { IEventQueue } from "../queue/IEventQueue.js";
 import { Response } from "../submission/Response.js";
+import { allowProcessToExitWithoutWaitingForTimerOrInterval } from "../Utils.js";
 
 interface EventQueueItem {
   file: string,
@@ -133,6 +134,7 @@ export class DefaultEventQueue implements IEventQueue {
     if (!this._queueIntervalId) {
       // TODO: Fix awaiting promise.
       this._queueIntervalId = setInterval(() => void this.onProcessQueue(), 10000);
+      allowProcessToExitWithoutWaitingForTimerOrInterval(this._queueIntervalId);
     }
 
     return Promise.resolve();
diff --git a/packages/node/src/plugins/NodeLifeCyclePlugin.ts b/packages/node/src/plugins/NodeLifeCyclePlugin.ts
index 298293cc..696826cf 100644
--- a/packages/node/src/plugins/NodeLifeCyclePlugin.ts
+++ b/packages/node/src/plugins/NodeLifeCyclePlugin.ts
@@ -17,7 +17,16 @@ export class NodeLifeCyclePlugin implements IEventPlugin {
 
     this._client = context.client;
 
+    let processingBeforeExit: boolean = false;
     process.on("beforeExit", (code: number) => {
+      // NOTE: We need to check if we are already processing a beforeExit event
+      // as async work will cause the runtime to call this handler again.
+      if (processingBeforeExit) {
+        return;
+      }
+
+      processingBeforeExit = true;
+
       const message = this.getExitCodeReason(code);
       if (message) {
         void this._client?.submitLog("beforeExit", message, "Error");