diff --git a/package.json b/package.json
index 4d94d0e4c..3eef4814b 100644
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
         "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push",
         "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain",
         "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain",
-        "setup": "git checkout 1.14.0 && npm ci --production && npm run download-dist",
+        "setup": "git checkout 1.14.1 && npm ci --production && npm run download-dist",
         "download-dist": "node extra/download-dist.js",
         "mark-as-nightly": "node extra/mark-as-nightly.js",
         "reset-password": "node extra/reset-password.js",
diff --git a/server/client.js b/server/client.js
index 31b10e844..ee925b357 100644
--- a/server/client.js
+++ b/server/client.js
@@ -3,7 +3,8 @@
  */
 const { TimeLogger } = require("../src/util");
 const { R } = require("redbean-node");
-const { io } = require("./server");
+const { UptimeKumaServer } = require("./uptime-kuma-server");
+const io = UptimeKumaServer.getInstance().io;
 const { setting } = require("./util-server");
 const checkVersion = require("./check-version");
 
diff --git a/server/proxy.js b/server/proxy.js
index 3de6425c4..621f24d06 100644
--- a/server/proxy.js
+++ b/server/proxy.js
@@ -3,7 +3,7 @@ const HttpProxyAgent = require("http-proxy-agent");
 const HttpsProxyAgent = require("https-proxy-agent");
 const SocksProxyAgent = require("socks-proxy-agent");
 const { debug } = require("../src/util");
-const server = require("./server");
+const { UptimeKumaServer } = require("./uptime-kuma-server");
 
 class Proxy {
 
@@ -151,6 +151,8 @@ class Proxy {
      * @returns {Promise<void>}
      */
     static async reloadProxy() {
+        const server = UptimeKumaServer.getInstance();
+
         let updatedList = await R.getAssoc("SELECT id, proxy_id FROM monitor");
 
         for (let monitorID in server.monitorList) {
diff --git a/server/routers/api-router.js b/server/routers/api-router.js
index 51aa5f3f1..578655e20 100644
--- a/server/routers/api-router.js
+++ b/server/routers/api-router.js
@@ -1,15 +1,16 @@
 let express = require("express");
 const { allowDevAllOrigin } = require("../util-server");
 const { R } = require("redbean-node");
-const server = require("../server");
 const apicache = require("../modules/apicache");
 const Monitor = require("../model/monitor");
 const dayjs = require("dayjs");
 const { UP, flipStatus, log } = require("../../src/util");
 const StatusPage = require("../model/status_page");
+const { UptimeKumaServer } = require("../uptime-kuma-server");
 let router = express.Router();
 
 let cache = apicache.middleware;
+const server = UptimeKumaServer.getInstance();
 let io = server.io;
 
 router.get("/api/entry-page", async (request, response) => {
diff --git a/server/server.js b/server/server.js
index 7d6ee42b1..90ec53290 100644
--- a/server/server.js
+++ b/server/server.js
@@ -1,3 +1,8 @@
+/*
+ * Uptime Kuma Server
+ * node "server/server.js"
+ * DO NOT require("./server") in other modules, it likely creates circular dependency!
+ */
 console.log("Welcome to Uptime Kuma");
 
 // Check Node.js Version
@@ -26,14 +31,10 @@ log.info("server", "Node Env: " + process.env.NODE_ENV);
 
 log.info("server", "Importing Node libraries");
 const fs = require("fs");
-const http = require("http");
-const https = require("https");
 
 log.info("server", "Importing 3rd-party libraries");
 log.debug("server", "Importing express");
 const express = require("express");
-log.debug("server", "Importing socket.io");
-const { Server } = require("socket.io");
 log.debug("server", "Importing redbean-node");
 const { R } = require("redbean-node");
 log.debug("server", "Importing jsonwebtoken");
@@ -50,26 +51,10 @@ log.debug("server", "Importing 2FA Modules");
 const notp = require("notp");
 const base32 = require("thirty-two");
 
-/**
- * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
- * @type {UptimeKumaServer}
- */
-class UptimeKumaServer {
-    /**
-     * Main monitor list
-     * @type {{}}
-     */
-    monitorList = {};
-    entryPage = "dashboard";
-
-    async sendMonitorList(socket) {
-        let list = await getMonitorJSONList(socket.userID);
-        io.to(socket.userID).emit("monitorList", list);
-        return list;
-    }
-}
-
-const server = module.exports = new UptimeKumaServer();
+const { UptimeKumaServer } = require("./uptime-kuma-server");
+const server = UptimeKumaServer.getInstance(args);
+const io = module.exports.io = server.io;
+const app = server.app;
 
 log.info("server", "Importing this project modules");
 log.debug("server", "Importing Monitor");
@@ -112,10 +97,7 @@ const port = [ args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001 ]
     .map(portValue => parseInt(portValue))
     .find(portValue => !isNaN(portValue));
 
-// SSL
-const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined;
-const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined;
-const disableFrameSameOrigin = args["disable-frame-sameorigin"] || !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || false;
+const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false;
 const cloudflaredToken = args["cloudflared-token"] || process.env.UPTIME_KUMA_CLOUDFLARED_TOKEN || undefined;
 
 // 2FA / notp verification defaults
@@ -134,25 +116,6 @@ if (config.demoMode) {
     log.info("server", "==== Demo Mode ====");
 }
 
-log.info("server", "Creating express and socket.io instance");
-const app = express();
-
-let httpServer;
-
-if (sslKey && sslCert) {
-    log.info("server", "Server Type: HTTPS");
-    httpServer = https.createServer({
-        key: fs.readFileSync(sslKey),
-        cert: fs.readFileSync(sslCert)
-    }, app);
-} else {
-    log.info("server", "Server Type: HTTP");
-    httpServer = http.createServer(app);
-}
-
-const io = new Server(httpServer);
-module.exports.io = io;
-
 // Must be after io instantiation
 const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList } = require("./client");
 const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler");
@@ -1483,12 +1446,12 @@ try {
 
     log.info("server", "Init the server");
 
-    httpServer.once("error", async (err) => {
+    server.httpServer.once("error", async (err) => {
         console.error("Cannot listen: " + err.message);
         await shutdownFunction();
     });
 
-    httpServer.listen(port, hostname, () => {
+    server.httpServer.listen(port, hostname, () => {
         if (hostname) {
             log.info("server", `Listening on ${hostname}:${port}`);
         } else {
@@ -1578,32 +1541,6 @@ async function afterLogin(socket, user) {
     }
 }
 
-/**
- * Get a list of monitors for the given user.
- * @param {string} userID - The ID of the user to get monitors for.
- * @returns {Promise<Object>} A promise that resolves to an object with monitor IDs as keys and monitor objects as values.
- *
- * Generated by Trelent
- */
-async function getMonitorJSONList(userID) {
-    let result = {};
-
-    let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [
-        userID,
-    ]);
-
-    for (let monitor of monitorList) {
-        result[monitor.id] = await monitor.toJSON();
-    }
-
-    return result;
-}
-
-/**
- * Connect to the database and patch it if necessary.
- *
- * Generated by Trelent
- */
 async function initDatabase(testMode = false) {
     if (! fs.existsSync(Database.path)) {
         log.info("server", "Copying Database");
@@ -1740,7 +1677,7 @@ function finalFunction() {
     log.info("server", "Graceful shutdown successful!");
 }
 
-gracefulShutdown(httpServer, {
+gracefulShutdown(server.httpServer, {
     signals: "SIGINT SIGTERM",
     timeout: 30000,                   // timeout: 30 secs
     development: false,               // not in dev mode
diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js
index 37c12256d..a9107ef93 100644
--- a/server/socket-handlers/cloudflared-socket-handler.js
+++ b/server/socket-handlers/cloudflared-socket-handler.js
@@ -1,6 +1,7 @@
 const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server");
 const { CloudflaredTunnel } = require("node-cloudflared-tunnel");
-const { io } = require("../server");
+const { UptimeKumaServer } = require("../uptime-kuma-server");
+const io = UptimeKumaServer.getInstance().io;
 
 const prefix = "cloudflared_";
 const cloudflared = new CloudflaredTunnel();
diff --git a/server/socket-handlers/proxy-socket-handler.js b/server/socket-handlers/proxy-socket-handler.js
index 817bdd49e..7862ff16b 100644
--- a/server/socket-handlers/proxy-socket-handler.js
+++ b/server/socket-handlers/proxy-socket-handler.js
@@ -1,7 +1,8 @@
 const { checkLogin } = require("../util-server");
 const { Proxy } = require("../proxy");
 const { sendProxyList } = require("../client");
-const server = require("../server");
+const { UptimeKumaServer } = require("../uptime-kuma-server");
+const server = UptimeKumaServer.getInstance();
 
 module.exports.proxySocketHandler = (socket) => {
     socket.on("addProxy", async (proxy, proxyID, callback) => {
diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js
index a06271da5..20d193823 100644
--- a/server/socket-handlers/status-page-socket-handler.js
+++ b/server/socket-handlers/status-page-socket-handler.js
@@ -6,7 +6,7 @@ const ImageDataURI = require("../image-data-uri");
 const Database = require("../database");
 const apicache = require("../modules/apicache");
 const StatusPage = require("../model/status_page");
-const server = require("../server");
+const { UptimeKumaServer } = require("../uptime-kuma-server");
 
 module.exports.statusPageSocketHandler = (socket) => {
 
@@ -215,6 +215,8 @@ module.exports.statusPageSocketHandler = (socket) => {
             ];
             await R.exec(`DELETE FROM \`group\` WHERE id NOT IN (${slots}) AND status_page_id = ?`, data);
 
+            const server = UptimeKumaServer.getInstance();
+
             // Also change entry page to new slug if it is the default one, and slug is changed.
             if (server.entryPage === "statusPage-" + slug && statusPage.slug !== slug) {
                 server.entryPage = "statusPage-" + statusPage.slug;
@@ -284,6 +286,8 @@ module.exports.statusPageSocketHandler = (socket) => {
 
     // Delete a status page
     socket.on("deleteStatusPage", async (slug, callback) => {
+        const server = UptimeKumaServer.getInstance();
+
         try {
             checkLogin(socket);
 
diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js
new file mode 100644
index 000000000..cc77bb186
--- /dev/null
+++ b/server/uptime-kuma-server.js
@@ -0,0 +1,82 @@
+const express = require("express");
+const https = require("https");
+const fs = require("fs");
+const http = require("http");
+const { Server } = require("socket.io");
+const { R } = require("redbean-node");
+
+/**
+ * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue.
+ * @type {UptimeKumaServer}
+ */
+class UptimeKumaServer {
+
+    /**
+     *
+     * @type {UptimeKumaServer}
+     */
+    static instance = null;
+
+    /**
+     * Main monitor list
+     * @type {{}}
+     */
+    monitorList = {};
+    entryPage = "dashboard";
+    app = undefined;
+    httpServer = undefined;
+    io = undefined;
+
+    static getInstance(args) {
+        if (UptimeKumaServer.instance == null) {
+            UptimeKumaServer.instance = new UptimeKumaServer(args);
+        }
+        return UptimeKumaServer.instance;
+    }
+
+    constructor(args) {
+        // SSL
+        const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined;
+        const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined;
+
+        console.log("Creating express and socket.io instance");
+        this.app = express();
+
+        if (sslKey && sslCert) {
+            console.log("Server Type: HTTPS");
+            this.httpServer = https.createServer({
+                key: fs.readFileSync(sslKey),
+                cert: fs.readFileSync(sslCert)
+            }, this.app);
+        } else {
+            console.log("Server Type: HTTP");
+            this.httpServer = http.createServer(this.app);
+        }
+
+        this.io = new Server(this.httpServer);
+    }
+
+    async sendMonitorList(socket) {
+        let list = await this.getMonitorJSONList(socket.userID);
+        this.io.to(socket.userID).emit("monitorList", list);
+        return list;
+    }
+
+    async getMonitorJSONList(userID) {
+        let result = {};
+
+        let monitorList = await R.find("monitor", " user_id = ? ORDER BY weight DESC, name", [
+            userID,
+        ]);
+
+        for (let monitor of monitorList) {
+            result[monitor.id] = await monitor.toJSON();
+        }
+
+        return result;
+    }
+}
+
+module.exports = {
+    UptimeKumaServer
+};