diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d20067c0a..ac9c51eafa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@ _This release is scheduled to be released on 2025-01-01._
 - [docs] Added step for npm publishing in release process (#3595)
 - [core] Add GitHub workflow to run spellcheck a few days before each release.
 - [core] Add intest flag to index.html to pass to module js for test mode detection (needed by #3630)
+- [compliments] add support for refreshing remote compliments file, and testcases
 
 ### Removed
 
diff --git a/modules/default/compliments/compliments.js b/modules/default/compliments/compliments.js
index 4c4bef471f..9f9270f199 100644
--- a/modules/default/compliments/compliments.js
+++ b/modules/default/compliments/compliments.js
@@ -12,6 +12,7 @@ Module.register("compliments", {
 		},
 		updateInterval: 30000,
 		remoteFile: null,
+		remoteFileRefreshInterval: 0,
 		fadeSpeed: 4000,
 		morningStartTime: 3,
 		morningEndTime: 12,
@@ -20,6 +21,9 @@ Module.register("compliments", {
 		random: true,
 		specialDayUnique: false
 	},
+	urlSuffix: "",
+	compliments_new: null,
+	refreshMinimumDelay: 15 * 60 * 60 * 1000, // 15 minutes
 	lastIndexUsed: -1,
 	// Set currentweather from module
 	currentWeatherType: "",
@@ -41,6 +45,17 @@ Module.register("compliments", {
 			const response = await this.loadComplimentFile();
 			this.config.compliments = JSON.parse(response);
 			this.updateDom();
+			if (this.config.remoteFileRefreshInterval !== 0) {
+				if ((this.config.remoteFileRefreshInterval >= this.refreshMinimumDelay) || window.mmTestMode === "true") {
+					setInterval(async () => {
+						const response = await this.loadComplimentFile();
+						this.compliments_new = JSON.parse(response);
+					},
+					this.config.remoteFileRefreshInterval);
+				} else {
+					Log.error(`${this.name} remoteFileRefreshInterval less than minimum`);
+				}
+			}
 		}
 		let minute_sync_delay = 1;
 		// loop thru all the configured when events
@@ -185,7 +200,13 @@ Module.register("compliments", {
 	async loadComplimentFile () {
 		const isRemote = this.config.remoteFile.indexOf("http://") === 0 || this.config.remoteFile.indexOf("https://") === 0,
 			url = isRemote ? this.config.remoteFile : this.file(this.config.remoteFile);
-		const response = await fetch(url);
+		// because we may be fetching the same url,
+		// we need to force the server to not give us the cached result
+		// create an extra property (ignored by the server handler) just so the url string is different
+		// that will never be the same, using the ms value of date
+		if (this.config.remoteFileRefreshInterval !== 0) this.urlSuffix = `?dummy=${Date.now()}`;
+		//
+		const response = await fetch(url + this.urlSuffix);
 		return await response.text();
 	},
 
@@ -236,6 +257,27 @@ Module.register("compliments", {
 			compliment.lastElementChild.remove();
 			wrapper.appendChild(compliment);
 		}
+		// if a new set of compliments was loaded from the refresh task
+		// we do this here to make sure no other function is using the compliments list
+		if (this.compliments_new) {
+			// use them
+			if (JSON.stringify(this.config.compliments) !== JSON.stringify(this.compliments_new)) {
+				// only reset if the contents changes
+				this.config.compliments = this.compliments_new;
+				// reset the index
+				this.lastIndexUsed = -1;
+			}
+			// clear new file list so we don't waste cycles comparing between refreshes
+			this.compliments_new = null;
+		}
+		// only in test mode
+		if (window.mmTestMode === "true") {
+			// check for (undocumented) remoteFile2 to test new file load
+			if (this.config.remoteFile2 !== null && this.config.remoteFileRefreshInterval !== 0) {
+				// switch the file so that next time it will be loaded from a changed file
+				this.config.remoteFile = this.config.remoteFile2;
+			}
+		}
 		return wrapper;
 	},
 
diff --git a/tests/configs/modules/compliments/compliments_file.js b/tests/configs/modules/compliments/compliments_file.js
new file mode 100644
index 0000000000..d73e1b5c10
--- /dev/null
+++ b/tests/configs/modules/compliments/compliments_file.js
@@ -0,0 +1,17 @@
+let config = {
+	address: "0.0.0.0",
+	ipWhitelist: [],
+	modules: [
+		{
+			module: "compliments",
+			position: "bottom_bar",
+			config: {
+				updateInterval: 3000,
+				remoteFile: "http://localhost:8080/tests/mocks/compliments_test.json"
+			}
+		}
+	]
+};
+
+/*************** DO NOT EDIT THE LINE BELOW ***************/
+if (typeof module !== "undefined") { module.exports = config; }
diff --git a/tests/configs/modules/compliments/compliments_file_change.js b/tests/configs/modules/compliments/compliments_file_change.js
new file mode 100644
index 0000000000..51fd4f6408
--- /dev/null
+++ b/tests/configs/modules/compliments/compliments_file_change.js
@@ -0,0 +1,19 @@
+let config = {
+	address: "0.0.0.0",
+	ipWhitelist: [],
+	modules: [
+		{
+			module: "compliments",
+			position: "bottom_bar",
+			config: {
+				updateInterval: 3000,
+				remoteFileRefreshInterval: 1500,
+				remoteFile: "http://localhost:8080/tests/mocks/compliments_test.json",
+				remoteFile2: "http://localhost:8080/tests/mocks/compliments_file.json"
+			}
+		}
+	]
+};
+
+/*************** DO NOT EDIT THE LINE BELOW ***************/
+if (typeof module !== "undefined") { module.exports = config; }
diff --git a/tests/electron/modules/compliments_spec.js b/tests/electron/modules/compliments_spec.js
index 15c3c37c08..8626b503c8 100644
--- a/tests/electron/modules/compliments_spec.js
+++ b/tests/electron/modules/compliments_spec.js
@@ -78,6 +78,22 @@ describe("Compliments module", () => {
 				await expect(doTest(["just a test"])).resolves.toBe(true);
 			});
 		});
+	});
 
+	describe("Feature remote compliments file", () => {
+		describe("get list from remote file", () => {
+			it("shows 'Remote compliment file works!' as only anytime list set", async () => {
+				await helpers.startApplication("tests/configs/modules/compliments/compliments_file.js", "01 Jan 2022 10:00:00 GMT");
+				await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true);
+			});
+		});
+		describe("get updated list from remote file", () => {
+			it("shows 'test in morning' as test time set to 10am", async () => {
+				await helpers.startApplication("tests/configs/modules/compliments/compliments_file_change.js", "01 Jan 2022 10:00:00 GMT");
+				await expect(doTest(["Remote compliment file works!"])).resolves.toBe(true);
+				await new Promise((r) => setTimeout(r, 10000));
+				await expect(doTest(["test in morning"])).resolves.toBe(true);
+			});
+		});
 	});
 });
diff --git a/tests/mocks/compliments_file.json b/tests/mocks/compliments_file.json
new file mode 100644
index 0000000000..89171b16ed
--- /dev/null
+++ b/tests/mocks/compliments_file.json
@@ -0,0 +1,5 @@
+{
+	"morning": ["test in morning"],
+	"afternoon": ["test in afternoon"],
+	"evening": ["test in evening"]
+}