# CrowdSec Discord Notifications: Real-Time Security Alerts with Context

**URL:** <https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830>\
**Category:** Guides & Tutorials\
**Tags:** discord, crowdsec\
**Created:** [October 2, 2025, 6:09am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830 "2025-10-02T06:09:01Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Mattercoder](https://forum.hhf.technology/letter_avatar_proxy/v4/letter/m/54ee81/32.png) [@Mattercoder](https://forum.hhf.technology/u/Mattercoder)\
**Post date:** [October 2, 2025, 6:09am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/1 "2025-10-02T06:09:01Z")

</div>

# 📢 CrowdSec Discord Notifications: Real-Time Security Alerts with Context

Integrating CrowdSec with **Discord** lets you receive rich, real-time security alerts directly in your Discord server whenever CrowdSec bans a malicious IP. Instead of just a plain text notification, you’ll get detailed context including **geolocation, map previews, attack scenarios, and targeted URIs** — making it easier to debug false positives and fine-tune whitelists.

* * *

## 🔍 What You Get

- 🚨 **Immediate alerts** when CrowdSec bans an IP
- 🌍 **Geoapify static map image** of the attacker’s approximate location
- 🧭 **Geolocation details** (country, city, maliciousness score if CTI enabled)
- 🔗 **Clickable IP links** to WHOIS for quick investigation
- 🎯 **Target URIs and metadata** to understand attack context
- 🤖 **Fully automated Discord delivery** via webhooks

> Imagine getting a Discord embed that shows:
> 
> - “Ban issued for IP 123.45.67.89 (Maliciousness 85%)”
> - A static map snapshot of the attacker’s origin
> - Target URLs that were hit in your infrastructure

* * *

## ⚙ Requirements

1. **Geoapify API Key**

2. **Optional: CrowdSec CTI API Key**

3. **Discord Webhook**

4. **Notification Config**

5. **Environment Variables** (`.env` file):

* * *

## 🐳 Docker Compose Example

Here’s a minimal example with Discord + Geoapify integration:

```yaml
services:
  crowdsec:
    image: crowdsecurity/crowdsec:v1.6.5
    container_name: crowdsec
    environment:
      GEOAPIFY_API_KEY: ${GEOAPIFY_API_KEY}
      DISCORD_WEBHOOK_ID: ${DISCORD_WEBHOOK_ID}
      DISCORD_WEBHOOK_TOKEN: ${DISCORD_WEBHOOK_TOKEN}
      CTI_API_KEY: ${CTI_API_KEY}
    volumes:
      - ./config/acquis.yaml:/etc/crowdsec/acquis.yaml
      - ./config/profiles.yaml:/etc/crowdsec/profiles.yaml
      - ./config/config.yaml:/etc/crowdsec/config.yaml
      - ./notifications/discord.yaml:/etc/crowdsec/notifications/discord.yaml
      - /var/log/traefik:/var/log/traefik/:ro
    restart: unless-stopped

```

* * *

## 📝 Discord Notification Template

Your `discord.yaml` defines the alert format. Example:

```yaml
type: http
name: discord
format: |
  {
    "embeds": [
      {
        "title": "🚨 CrowdSec Alert",
        "description": "IP {{.Value}} banned for {{.Duration}}",
        "url": "https://app.crowdsec.net/cti/{{.Value}}",
        "image": {
          "url": "https://maps.geoapify.com/v1/staticmap?...&apiKey={{env "GEOAPIFY_API_KEY"}}"
        },
        "fields": [
          { "name": "Scenario", "value": "{{.Scenario}}" },
          { "name": "Country", "value": "{{.Source.Cn}}" },
          { "name": "Target URIs", "value": "{{range (GetMeta . "target_fqdn")}}`{{.}}`\n{{end}}" }
        ]
      }
    ]
  }
url: https://discord.com/api/webhooks/${DISCORD_WEBHOOK_ID}/${DISCORD_WEBHOOK_TOKEN}
method: POST
headers:
  Content-Type: application/json

```

* * *

## 🔧 Enable Notifications in Profiles

Add `discord` to your `profiles.yaml`:

```yaml
name: default_ip_remediation
filters:
 - Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
 - type: ban
   duration: 168h
notifications:
 - discord
on_success: break

```

* * *

## 🧪 Testing

Run a manual ban to confirm the notification fires:

```bash
docker exec crowdsec cscli decisions add --ip 192.168.1.10 -d 10m

```

You should see a new embed in your Discord channel with all the configured details.

* * *

## ✅ Summary

By integrating CrowdSec with Discord, you now have:

- **Real-time, rich notifications** on malicious IP bans
- **Visual map embeds** for attacker location
- **Detailed metadata** for debugging false positives
- **Optional CTI insights** for deeper threat context

This makes it much easier to monitor, react to, and tune your CrowdSec setup without constantly digging into logs.

---

<div class="post-metadata">

**Author:** ![Prototempus](https://forum.hhf.technology/letter_avatar_proxy/v4/letter/p/7feea3/32.png) [@Prototempus](https://forum.hhf.technology/u/Prototempus)\
**Post date:** [November 11, 2025, 8:01pm UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/2 "2025-11-11T20:01:53Z")

</div>

Thanks for posting, I am getting an error when testing the ban decision:

> time=“2025-11-11T19:55:13Z” level=info msg=“(localhost/cscli) manual ‘ban’ from ‘localhost’ by ip 192.168.100.10 : 10m ban on Ip 192.168.100.10”  
> time=“2025-11-11T19:55:13Z” level=info msg=“127.0.0.1 - [Tue, 11 Nov 2025 19:55:13 UTC] "POST /v1/alerts HTTP/1.1 201 8.479269ms “crowdsec/v1.7.3-c8aad699-docker” "”  
> time=“2025-11-11T19:55:13Z” level=error msg=“template: :5:27: executing “” at \<.Value\>: can’t evaluate field Value in type \*models.Alert” plugin:=discord

I’ve double-checked the alert template, and it seems to be correct. I’ve also tried a [variation](https://gist.github.com/bpbradley/6628f7c7486b46dfeefaa95a83373f01) I found online and I have the same issue.

---

<div class="post-metadata">

**Author:** ![hhf.technoloy](https://forum.hhf.technology/user_avatar/forum.hhf.technology/hhf.technoloy/32/68_2.png) [@hhf.technoloy](https://forum.hhf.technology/u/hhf.technoloy)\
**Post date:** [November 12, 2025, 3:41am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/3 "2025-11-12T03:41:23Z")

</div>

# CrowdSec Discord Notifications

This module implements a Discord notification mechanism for CrowdSec ban decisions. Upon detection and remediation, it dispatches an alert payload to a specified Discord channel. The notification shows critical metadata on the offending IP address, including geolocational coordinates, and embeds a static map visualization of the inferred origin location.

Additionally, the alert gives all implicated target URIs, facilitating analysis for false positive mitigation and targeted whitelist or parser refinements.

## Prerequisites

1. **Geoapify API Integration** : Obtain an API key from [Geoapify](https://www.geoapify.com/), selected for its free-tier static maps endpoint. Inject this key into the CrowdSec environment via the `GEOAPIFY_API_KEY` variable (see sample `compose.yaml` below).

2. **Optional: CrowdSec CTI Smoke Database Access** : For enhanced telemetry—such as Maliciousness Scores and granular geolocational enrichment—provision access to the CrowdSec CTI Smoke dataset (free tier: 30 queries/day). Generate a CTI API key via the [CrowdSec Console](https://app.crowdsec.net/settings/cti-api-keys). Configure ingestion in `config.yaml` (populate `CTI_API_KEY` environment variable or embed directly):

3. **Discord Webhook Configuration** : Establish a Discord webhook and extract its ID and token. The endpoint follows the format `https://discord.com/api/webhooks/${DISCORD_WEBHOOK_ID}/${DISCORD_WEBHOOK_TOKEN}`. Propagate these via environment variables (reference sample `compose.yaml`):

4. **Notification Plugin Mounting** : Bind the `discord.yaml` configuration to `/etc/crowdsec/notifications/discord.yaml` within the CrowdSec container (consult `compose.yaml`):

5. **Environment Variable Persistence** : Define a `.env` file to externalize sensitive credentials:

6. **Profile Remediation Update** : Amend the `profiles.yaml` to activate the `discord` notifier. Exemplar configuration:

7. **Validation Procedure** : Simulate a ban decision to verify notification emission: `docker exec crowdsec cscli decisions add --ip 192.168.1.10 -d 10m`. Monitor the Discord channel for the resultant payload.

## Change the volumes according to pangolin deployment, don’t just blindly copy paste.

```yml
services:
  crowdsec:
    image: crowdsecurity/crowdsec:v1.6.5@sha256:fdb487e130095709c1b1c5ba2bd2b461e6715676076e5f774230dcf47d366f76
    container_name: crowdsec
    environment:
      GID: ${GID-1000}
      COLLECTIONS: crowdsecurity/linux crowdsecurity/traefik
      # Get an API Key from Geoapify. Only service I could get a free tier for static maps
      GEOAPIFY_API_KEY: ${GEOAPIFY_API_KEY}
      # Details of Discord webhook. Check your discord webhook url, it will be of the form
      # https://discord.com/api/webhooks/${DISCORD_WEBHOOK_ID}/${DISCORD_WEBHOOK_TOKEN}
      DISCORD_WEBHOOK_ID: ${DISCORD_WEBHOOK_ID}
      DISCORD_WEBHOOK_TOKEN: ${DISCORD_WEBHOOK_TOKEN}
      TZ: ${TZ}
      # Get an API Key for the Crowdsec CTI API if you want Malicousness score and City details
      CTI_API_KEY: ${CTI_API_KEY}
    volumes:
      - ./config/acquis.yaml:/etc/crowdsec/acquis.yaml
      - ./config/profiles.yaml:/etc/crowdsec/profiles.yaml
      - ./config/config.yaml:/etc/crowdsec/config.yaml
      # Make sure to map discord.yaml to /etc/crowdsec/notifications/discord.yaml
      - ./notifications/discord.yaml:/etc/crowdsec/notifications/discord.yaml
      - crowdsec-db:/var/lib/crowdsec/data/
      - crowdsec-config:/etc/crowdsec/
      - /var/log/traefik:/var/log/traefik/:ro
    networks:
      - crowdsec
    security_opt:
      - no-new-privileges:true
    restart: unless-stopped
networks:
  crowdsec:
    external: true
volumes:
  crowdsec-db: null
  crowdsec-config: null

```

```json
#
# /etc/crowdsec/notifications/discord.yaml
#

type: http
name: discord
log_level: info
format: |
  {
    "embeds": [
      {
        {{range . -}}
        {{$alert := . -}}
        {{range .Decisions -}}
        {{- $cti := .Value | CrowdsecCTI -}}
        "timestamp": "{{$alert.StartAt}}",
        "title": "Crowdsec Alert",
        "color": 16711680,
        "description": "Potential threat detected. View details in [Crowdsec Console](<https://app.crowdsec.net/cti/{{.Value}}>)",
        "url": "https://app.crowdsec.net/cti/{{.Value}}",
        {{if $alert.Source.Cn -}}
        "image": {
          "url": "https://maps.geoapify.com/v1/staticmap?style=osm-bright-grey&width=600&height=400&center=lonlat:{{$alert.Source.Longitude}},{{$alert.Source.Latitude}}&zoom=8.1848&marker=lonlat:{{$alert.Source.Longitude}},{{$alert.Source.Latitude}};type:awesome;color:%23655e90;size:large;icon:industry|lonlat:{{$alert.Source.Longitude}},{{$alert.Source.Latitude}};type:material;color:%23ff3421;icontype:awesome&scaleFactor=2&apiKey={{env "GEOAPIFY_API_KEY"}}"
        },
        {{end}}
        "fields": [
              {
                "name": "Scenario",
                "value": "`{{ .Scenario }}`",
                "inline": "true"
              },
              {
                "name": "IP",
                "value": "[{{.Value}}](<https://www.whois.com/whois/{{.Value}}>)",
                "inline": "true"
              },
              {
                "name": "Ban Duration",
                "value": "{{.Duration}}",
                "inline": "true"
              },
              {{if $alert.Source.Cn -}}
              { 
                "name": "Country",
                "value": "{{$alert.Source.Cn}} :flag_{{ $alert.Source.Cn | lower }}:",
                "inline": "true"
              }
              {{if $cti.Location.City -}}
              ,
              { 
                "name": "City",
                "value": "{{$cti.Location.City}}",
                "inline": "true"
              },
              { 
                "name": "Maliciousness",
                "value": "{{mulf $cti.GetMaliciousnessScore 100 | floor}} %",
                "inline": "true"
              }
              {{end}}
              {{end}}
              {{if not $alert.Source.Cn -}}
              { 
                "name": "Location",
                "value": "Unknown :pirate_flag:"
              }
              {{end}}
              {{end -}}
              {{end -}}
              {{range . -}}
              {{$alert := . -}}
              {{range .Meta -}}
                ,{
                "name": "{{.Key}}",
                "value": "{{ (splitList "," (.Value | replace "\"" "`" | replace "[" "" |replace "]" "")) | join "\\n"}}"
              } 
              {{end -}}
              {{end -}}
        ]
      }
    ]
  }
url: https://discord.com/api/webhooks/${DISCORD_WEBHOOK_ID}/${DISCORD_WEBHOOK_TOKEN}
method: POST
headers:
  Content-Type: application/json

```

---

<div class="post-metadata">

**Author:** ![Prototempus](https://forum.hhf.technology/letter_avatar_proxy/v4/letter/p/7feea3/32.png) [@Prototempus](https://forum.hhf.technology/u/Prototempus)\
**Post date:** [November 13, 2025, 4:45am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/4 "2025-11-13T04:45:57Z")

</div>

Is this related to my question above?

---

<div class="post-metadata">

**Author:** ![hhf.technoloy](https://forum.hhf.technology/user_avatar/forum.hhf.technology/hhf.technoloy/32/68_2.png) [@hhf.technoloy](https://forum.hhf.technology/u/hhf.technoloy)\
**Post date:** [November 13, 2025, 4:55am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/5 "2025-11-13T04:55:35Z")

</div>

yes. this is my current setup for discord alerts

---

<div class="post-metadata">

**Author:** ![Prototempus](https://forum.hhf.technology/letter_avatar_proxy/v4/letter/p/7feea3/32.png) [@Prototempus](https://forum.hhf.technology/u/Prototempus)\
**Post date:** [November 13, 2025, 4:57am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/6 "2025-11-13T04:57:45Z")

</div>

Ah, okay. It must be something with my setup - the test ban initiates the discord, but fails with the error I posted. I can’t find anyone online with the same error. Not sure what is going on.

**Edit:** Never mind, it was an issue with the template! Thank you!

---

<div class="post-metadata">

**Author:** ![hhf.technoloy](https://forum.hhf.technology/user_avatar/forum.hhf.technology/hhf.technoloy/32/68_2.png) [@hhf.technoloy](https://forum.hhf.technology/u/hhf.technoloy)\
**Post date:** [November 13, 2025, 5:30am UTC](https://forum.hhf.technology/t/crowdsec-discord-notifications-real-time-security-alerts-with-context/3830/7 "2025-11-13T05:30:05Z")

</div>

my template works, it in production env right now
