Why you want to do that?
If you run a server exposed to the internet (a VPS, a home lab box, a Raspberry Pi behind a tunnel), people will try to log in. Most of them are bots, but the day a real login happens that you didn’t initiate, you want to know about it immediately.
I keep my phone with Discord on it all the time, so a Discord ping is the fastest way for me to know “hey, someone just SSH’d into your machine”. No need to open a dashboard or grep through /var/log/auth.log. The same trick works for Slack, Telegram or a plain email, but Discord webhooks are dead simple, so that’s what we will use.
Prerequisite
- A Linux machine you can
sudoon (I’m doing this on Ubuntu/Debian, same idea works elsewhere). - A Discord server where you can create a webhook.
- Basic understanding of SSH.
Step 1: Create a Discord webhook
Open your Discord server, go to Server Settings → Integrations → Webhooks → New Webhook. Pick a channel where you want the alerts to land, give it a name like ssh-alerts, and click Copy Webhook URL.
It will look something like this:
https://discord.com/api/webhooks/123456789012345678/AbCdEf-your-very-long-token-here
Keep this URL secret. Anyone with it can post messages to your channel.
Step 2: Write the notification script
Now we write a small script that PAM will call on every session event. It reads the variables PAM exposes about the session and posts a formatted Discord embed. We handle both open_session and close_session, so you get a ping for logins and logouts, with the title changing to match the event.
/usr/local/bin/ssh-login-alert.sh
#!/bin/bash
# Discord webhook URL
WEBHOOK_URL="https://discord.com/api/webhooks/123456789012345678/AbCdEf-your-very-long-token-here"
EVENT_TYPE="$PAM_TYPE"
USER_NAME="$PAM_USER"
REMOTE_HOST="$PAM_RHOST"
SERVICE="$PAM_SERVICE"
HOSTNAME=$(hostname)
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
if [ "$PAM_TYPE" = "open_session" ]; then
TITLE="SSH Login"
elif [ "$PAM_TYPE" = "close_session" ]; then
TITLE="SSH Logout"
else
TITLE="SSH Event"
fi
curl -s \
-H "Content-Type: application/json" \
-X POST \
-d "{
\"embeds\": [{
\"title\": \"$TITLE\",
\"description\": \"User: $PAM_USER\nHost: $HOSTNAME\nservice: $SERVICE\nRemote IP: $PAM_RHOST\nTime: $(date '+%Y-%m-%d %H:%M:%S')\"
}]
}" \
"$WEBHOOK_URL" >/dev/null 2>&1
Make it executable and lock down the permissions, since it holds your webhook URL:
$ sudo mv ssh-login-alert.sh /usr/local/bin/ssh-login-alert.sh
$ sudo chown root:root /usr/local/bin/ssh-login-alert.sh
$ sudo chmod 700 /usr/local/bin/ssh-login-alert.sh
The interesting bit here is the PAM_* variables. When PAM runs an external program, it passes the session details through the environment, so we don’t have to parse any logs:
PAM_USER— the user that just logged inPAM_RHOST— the IP/host the connection came fromPAM_SERVICE— the service (will besshdfor SSH)PAM_TYPE— the kind of PAM event (open_session,close_session, etc.). We use this to pick the title, so a login shows “SSH Login” and a logout shows “SSH Logout”.
Step 3: Hook the script into PAM
This is where the magic happens. PAM (Pluggable Authentication Modules) lets us run our script as part of the SSH session setup using the pam_exec.so module.
Open the SSH PAM config:
$ sudo nano /etc/pam.d/sshd
Add this line at the end of the file:
/etc/pam.d/sshd
# Send a Discord alert on every SSH session open and close
session optional pam_exec.so seteuid /usr/local/bin/ssh-login-alert.sh
A few things worth knowing about this line:
sessionmeans it runs when a session is opened and closed, which is exactly when someone logs in or out — both of which our script reports.optionalis important. If you userequiredand your script (or Discord) ever fails, you could lock yourself out. Withoptional, a failed alert never blocks the login.seteuidruns the script with the effective user id of the calling process, which keepscurland networking happy.
That’s it. No need to restart sshd, PAM picks up the config change on the next login.
Step 4: Test it
Open a new terminal and SSH into the machine:
$ ssh user@your-server
Within a second or two you should see an embed land in your Discord channel:
SSH Login
User: user
Host: my-server
service: sshd
Remote IP: 49.36.xxx.xxx
Time: 2026-06-23 21:34:11
And when you log out, a matching SSH Logout embed shows up the same way.
If nothing shows up, run the script manually to check the webhook works, then check /var/log/auth.log for any pam_exec errors:
$ sudo PAM_TYPE=open_session PAM_USER=test PAM_RHOST=1.2.3.4 PAM_SERVICE=sshd /usr/local/bin/ssh-login-alert.sh
$ sudo grep pam_exec /var/log/auth.log
A note on what this is and isn’t
This is a notification, not a defense. It tells you after someone has already logged in, so it doesn’t replace key-only auth, fail2ban, or a properly locked down sshd_config. But as a “second pair of eyes” on your machine it’s fantastic, and it took us all of three small steps to set up.
You can extend the script easily, for example only alert when the login is not from your home IP, or include the output of last to show recent sessions. But even the basic version above has saved me more than once from a “wait, that wasn’t me” moment.