diff --git a/docs/reference/notification.md b/docs/reference/notification.md
index 88b4c4e0b0..1cf43649c6 100644
--- a/docs/reference/notification.md
+++ b/docs/reference/notification.md
@@ -262,3 +262,16 @@ Copy the API key generated with the service activation.
 | `FREEMOBILE_API_KEY` | API key generated with your notification option activation |
 
 Note: here you do not need to give neither your password nor phone number.
+
+## NTFY.sh
+
+You can send notifications using NTFY.sh, which supports various features like priority, tags, and action buttons.
+Use the free service at [ntfy.sh](https://ntfy.sh) or host your own instance.
+
+| Environment variable | Description |
+|:---:|---|
+| `NTFY_URL` | ntfy server URL, e.g. `https://ntfy.sh` |
+| `NTFY_TOPIC` | Topic to publish alerts to |
+| `NTFY_PRIORITY` | Message priority, e.g. max/high/default/low/min, I recommend to use the numbers instead of the string values for the priority. https://docs.ntfy.sh/publish/?h=priority#message-priority |
+| `NTFY_TITLE` | Title of the message |
+| `NTFY_ACCESS_TOKEN` | Access token for authentication. https://docs.ntfy.sh/config/#access-tokens |
diff --git a/dotenv-example b/dotenv-example
index 12bb3f8b4e..376ffb1733 100644
--- a/dotenv-example
+++ b/dotenv-example
@@ -163,4 +163,9 @@ STREAMLABS_SOUND=
 STREAMLABS_DURATION=
 FREEMOBILE_ID=
 FREEMOBILE_API_KEY=
+NTFY_TOPIC=
+NTFY_PRIORITY=
+NTFY_TITLE=
+NTFY_ACCESS_TOKEN=
+NTFY_URL=
 WEB_PORT=
diff --git a/src/config.ts b/src/config.ts
index adc106b71d..7709426bcc 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -379,6 +379,13 @@ const notifications = {
     id: envOrString(process.env.FREEMOBILE_ID),
     apiKey: envOrString(process.env.FREEMOBILE_API_KEY),
   },
+  ntfy: {
+    url: envOrString(process.env.NTFY_URL, 'https://ntfy.sh'),
+    topic: envOrString(process.env.NTFY_TOPIC),
+    priority: envOrString(process.env.NTFY_PRIORITY),
+    title: envOrString(process.env.NTFY_TITLE),
+    accessToken: envOrString(process.env.NTFY_ACCESS_TOKEN),
+  },
 };
 
 const nvidia = {
diff --git a/src/messaging/notification.ts b/src/messaging/notification.ts
index ac4de7a355..521cf34c22 100644
--- a/src/messaging/notification.ts
+++ b/src/messaging/notification.ts
@@ -20,10 +20,12 @@ import {updateRedis} from './redis';
 import {sendStreamLabsAlert} from './streamlabs';
 import {sendFreeMobileAlert} from './freemobile';
 import {DMPayload} from '.';
+import {sendNtfyAlert} from './ntfy';
 
 export function sendNotification(link: Link, store: Store) {
   // Priority
   playSound();
+  sendNtfyAlert(link, store);
   sendDiscordMessage(link, store);
   sendDesktopNotification(link, store);
   sendEmail(link, store);
diff --git a/src/messaging/ntfy.ts b/src/messaging/ntfy.ts
new file mode 100644
index 0000000000..f10f572741
--- /dev/null
+++ b/src/messaging/ntfy.ts
@@ -0,0 +1,55 @@
+import {Link, Store} from '../store/model';
+import {Print, logger} from '../logger';
+import {config} from '../config';
+import fetch from 'node-fetch';
+
+const {ntfy} = config.notifications;
+
+export function sendNtfyAlert(link: Link, store: Store) {
+  if (ntfy.topic) {
+    logger.debug('↗ sending ntfy alert');
+
+    (async () => {
+      const message = `${Print.inStock(link, store)}`;
+      const headers: Record<string, string> = {};
+
+      if (ntfy.priority) headers['Priority'] = ntfy.priority;
+      headers[
+        'Tags'
+      ] = `${store.name},${link.model},${link.series},${link.brand}`;
+      if (ntfy.title) headers['Title'] = ntfy.title;
+      if (ntfy.accessToken)
+        headers['Authorization'] = `Bearer ${ntfy.accessToken}`;
+
+      const body = {
+        topic: ntfy.topic,
+        message,
+        actions: [
+          {
+            action: 'view',
+            label: 'Add to cart',
+            url: link.cartUrl ?? link.url,
+          },
+        ],
+      };
+
+      try {
+        const response = await fetch(ntfy.url, {
+          method: 'POST',
+          body: JSON.stringify(body),
+          headers: {
+            ...headers,
+            'Content-Type': 'application/json',
+          },
+        });
+
+        if (!response.ok)
+          throw new Error(`Failed to send ntfy alert: ${response.statusText}`);
+
+        logger.info('✔ ntfy alert sent');
+      } catch (error: unknown) {
+        logger.error("✖ couldn't send ntfy alert", error);
+      }
+    })();
+  }
+}