🚀 Executive Summary
TL;DR: Manually checking WireGuard VPN status is inefficient and can lead to unnoticed issues. This tutorial provides a solution by creating a Python script to parse wg show output and automatically send formatted status reports to a private Telegram bot, offering at-a-glance visibility into VPN health.
🎯 Key Takeaways
- Telegram bot creation involves using
@BotFatherto obtain an HTTP API token and a utility script (find\_chat\_id.py) to retrieve the personal Chat ID for message delivery. - The core monitoring script (
wg\_monitor.py) executes thewg showcommand, parses its output using regular expressions to extract peer public keys, latest handshakes, and data transfer statistics. - Secure credential management is achieved by storing sensitive Bot Token and Chat ID in a separate
config.inifile, preventing hardcoding within the Python script. - Automation of status reports is implemented using cron jobs, scheduling the
wg\_monitor.pyscript to run at specified intervals for consistent monitoring. - Common implementation pitfalls include
wg showpermission errors (requiring elevated privileges or sudoers configuration) and Python environment discrepancies in cron jobs (requiring absolute paths for executables and system-wide dependency installation).
Self-Hosted VPN Monitoring: WireGuard Status to Telegram Bot
Introduction
Running your own WireGuard VPN server provides excellent privacy and control, but it can often feel like a black box. Are your peers connecting? When was the last successful handshake? How much data has been transferred? Answering these questions manually is tedious and inefficient. Without proactive monitoring, you might not notice a configuration issue or an inactive client until it becomes a problem.
This tutorial solves that problem by creating a simple, powerful monitoring system. We will write a Python script to parse the status of your WireGuard interface and automatically send a neatly formatted report to a private Telegram bot. This gives you at-a-glance visibility into your VPN’s health, delivered directly to your phone or desktop, turning your “black box” into a transparent, manageable service.
Prerequisites
Before we begin, ensure you have the following in place:
- A Linux server with WireGuard installed and running.
- Administrative (root or equivalent) access to the server.
- Python 3 and the
pippackage manager installed. - A Telegram account.
Step-by-Step Guide
We’ll break this process down into four main steps: creating the bot, writing the monitoring script, securing our credentials, and automating the execution.
Step 1: Create a Telegram Bot and Obtain Credentials
First, we need a bot to send messages. Telegram’s “BotFather” makes this incredibly easy.
-
Create the Bot: Open Telegram, search for the user
@BotFather, and start a chat. Send the/newbotcommand and follow the prompts to choose a name and username for your bot. -
Save Your Bot Token: Once created, BotFather will provide you with an HTTP API token. It will look something like
1234567890:ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789. Treat this token like a password and save it securely. -
Find Your Chat ID: The bot needs to know where to send the message. To get your personal Chat ID, send a message to your newly created bot first. Then, you can run the following Python script on any machine with Python installed to retrieve it. Remember to install the required library with
pip install python-telegram-bot.
# find_chat_id.py
# A simple utility to get your Telegram Chat ID.
import asyncio
from telegram import Bot
# IMPORTANT: Replace with your actual bot token
BOT_TOKEN = "YOUR_API_TOKEN_HERE"
async def main():
bot = Bot(token=BOT_TOKEN)
print("Fetching updates...")
try:
updates = await bot.get_updates(timeout=10, limit=1)
if updates:
chat_id = updates[0].message.chat_id
print(f"Success! Your Chat ID is: {chat_id}")
else:
print("No new messages found. Please send a message to your bot and run this script again.")
except Exception as e:
print(f"An error occurred: {e}")
print("Ensure your BOT_TOKEN is correct and you have sent a message to the bot.")
if __name__ == "__main__":
asyncio.run(main())
Run the script using python3 find_chat_id.py. Save the numeric Chat ID it outputs. You will need both the Bot Token and the Chat ID in the next steps.
Step 2: The Python Monitoring Script
Now, let’s create the core script that gathers WireGuard data and sends it to Telegram. This script will execute the wg show command, parse its output, and format a clear, readable status report.
First, install the necessary Python library:
pip install requests
Next, create a file named wg_monitor.py and add the following code. The logic is explained in the comments.
# wg_monitor.py
import subprocess
import re
import requests
import configparser
from datetime import datetime
def get_wireguard_status():
"""Executes 'wg show' and returns its output."""
try:
# The 'wg show' command requires elevated privileges.
# Ensure the user running this script has permission.
result = subprocess.run(
['wg', 'show'],
capture_output=True,
text=True,
check=True
)
return result.stdout
except (subprocess.CalledProcessError, FileNotFoundError) as e:
print(f"Error executing 'wg show': {e}")
return None
def parse_wg_status(output):
"""Parses the raw output from 'wg show' into a structured format."""
peers = []
current_peer = None
for line in output.strip().split('\n'):
# Each peer section starts with 'peer:'
peer_match = re.search(r'peer: (\S+)', line)
if peer_match:
if current_peer:
peers.append(current_peer)
current_peer = {'public_key': peer_match.group(1)}
continue
# Extract other details for the current peer
if current_peer:
handshake_match = re.search(r'latest handshake: (.+)', line)
transfer_match = re.search(r'transfer: (.+ received, .+ sent)', line)
if handshake_match:
current_peer['latest_handshake'] = handshake_match.group(1)
if transfer_match:
current_peer['transfer'] = transfer_match.group(1)
if current_peer:
peers.append(current_peer)
return peers
def format_telegram_message(peers):
"""Formats the parsed peer data into a Telegram-friendly message."""
if not peers:
return "WireGuard Status: No peers found or interface is down."
# Get the server name for a nicer title
hostname = subprocess.run(['hostname'], capture_output=True, text=True).stdout.strip()
# Using HTML for bold and monospace formatting in Telegram
message = f"<b>WireGuard Status Report ({hostname})</b>\n"
message += f"<i>Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</i>\n\n"
for i, peer in enumerate(peers):
# We use a comment or alias for the peer public key if available
# This requires you to add '# friendly-name' in your wg0.conf
public_key = peer.get('public_key', 'N/A')
handshake = peer.get('latest_handshake', 'N/A')
transfer = peer.get('transfer', 'N/A')
message += f"<b>Peer {i+1}:</b> <code>...{public_key[-8:]}</code>\n"
message += f" 🤝 Handshake: {handshake}\n"
message += f" 📊 Transfer: {transfer}\n\n"
return message
def send_telegram_message(token, chat_id, message):
"""Sends the formatted message to the Telegram Bot API."""
api_url = f"https://api.telegram.org/bot{token}/sendMessage"
payload = {
'chat_id': chat_id,
'text': message,
'parse_mode': 'HTML' # Use HTML for formatting
}
try:
response = requests.post(api_url, data=payload, timeout=10)
response.raise_for_status()
print("Successfully sent message to Telegram.")
except requests.exceptions.RequestException as e:
print(f"Failed to send message: {e}")
def main():
# Read credentials from a configuration file
config = configparser.ConfigParser()
config.read('config.ini')
bot_token = config['telegram']['BotToken']
chat_id = config['telegram']['ChatID']
wg_output = get_wireguard_status()
if wg_output:
peers_data = parse_wg_status(wg_output)
telegram_message = format_telegram_message(peers_data)
send_telegram_message(bot_token, chat_id, telegram_message)
if __name__ == "__main__":
main()
Step 3: Secure Your Credentials
Hardcoding tokens and IDs directly into your script is a security risk. A better practice is to store them in a separate configuration file. Create a file named config.ini in the same directory as your Python script.
# config.ini
[telegram]
BotToken = YOUR_API_TOKEN_HERE
ChatID = YOUR_CHAT_ID_HERE
Replace the placeholders with your actual Bot Token and Chat ID. Our Python script is already designed to read from this file, keeping your sensitive information separate from your code.
Step 4: Automate with Cron
Finally, we’ll use a cron job to run our script automatically on a schedule. This way, you’ll receive status reports without any manual intervention.
Open your cron editor with the appropriate command for your system (e.g., crontab -e).
Add the following line to the file to schedule the script to run every day at 8:00 AM. Adjust the schedule (0 8 * * *) and file path to fit your needs.
# Run the WireGuard monitor script daily at 8 AM
0 8 * * * python3 /home/user/wg_monitor/wg_monitor.py
Note: Make sure to use the absolute path to your Python script. The cron job will execute from your user’s home directory, so relative paths can fail.
Common Pitfalls
-
Permission Errors with
wgcommand: Thewg showcommand typically requires elevated privileges to inspect the network interface. If the user running the cron job does not have these permissions, the script will fail. You may need to configure your system to allow the user to run this specific command without a password. Search for how to grant specific command permissions via your system’s sudoers configuration. -
Python Environment and Dependencies: Cron jobs run in a minimal shell environment, which might not have the same PATH as your interactive shell. If the script fails to find the
requestslibrary, it’s because the Python interpreter being used by cron doesn’t know about it. To fix this, you can either use the absolute path to the Python executable within a virtual environment (e.g.,/home/user/venv/bin/python3) in your cron job, or ensure the library is installed for the system’s default Python 3 interpreter.
Conclusion
You have now successfully built an automated monitoring system for your self-hosted WireGuard VPN. This setup provides timely, valuable insights into your VPN’s activity, enhancing both security and reliability. The script is easily extensible; you could add logic to alert on peers with old handshakes, track data usage over time, or integrate it with other dashboarding tools.
By transforming command-line output into actionable notifications, you’ve taken a significant step towards professional-grade management of your personal infrastructure. Happy monitoring!
👉 Read the original article on TechResolve.blog
☕ Support my work
If this article helped you, you can buy me a coffee:

Top comments (0)