Email and Slack Alerts

Send alerts via email and Slack in this Python for DevOps automation tutorial — hands-on steps, troubleshooting, and what to study next.

Focus: send alerts via email and slack

Sponsored

Your pager is silent — that’s the good case. The bad case is worse: a database disk fills at 3 a.m., a CI pipeline goes red, or a certificate expires silently — and you find out from a user, not from an alert. In a DevOps role, you can’t watch every dashboard, so your automation needs a voice. This lesson teaches you how to make your Python scripts call for help using two of the most universal channels: email and Slack. By the end, you’ll be able to wrap any check or cron job in a reliable alerting layer that actually gets noticed — without building a whole monitoring platform.

The problem this lesson solves

Assume you’ve built a Python script that checks disk usage, scrapes an API, or validates a TLS certificate. What happens when the check fails? Nothing — unless you’ve explicitly coded the failure path. Silent failures are the worst kind in DevOps: the infrastructure may be broken for hours or days before a human stumbles onto it.

Email has been the default alerting channel for decades, but inboxes are noisy and messages can be ignored. Slack, on the other hand, gives you real-time notifications with threads, mentions, and emoji that make urgent issues pop out of the chat. The modern DevOps standard is to use both: email for a formal record, Slack for the immediate “drop everything” signal.

The core problem this lesson solves is making your Python scripts actively push alerts — not just log a line that nobody reads. You’ll learn the setup, the code, and the fallback strategies so a failed alert doesn’t go unnoticed either.

Core concept / mental model

Think of alerting as a messaging layer that sits between your automation and your humans. Instead of having every script implement its own notification logic, you build a small notification function that acts as a single doorway — write one piece of code, then reuse it everywhere.

The two main doors are:

  • SMTP for email — the classic protocol that sends plain-text or HTML messages from your script to any inbox.
  • Webhooks for Slack — Slack gives you a URL that accepts a JSON payload; you POST to it and the message appears in a channel.

A useful mental model is the “pipeline” view:

  1. Your automation detects a condition (e.g., disk > 90%).
  2. You call a function like send_alert("disk", severity="critical").
  3. That function formats the message and pushes it through one or more transports (email, Slack, or both).
  4. The transport delivers the message to a human (or a ticketing system).

This separation means you can change the alerting provider or format without touching your scripts’ core logic. You could even add a third channel (like a webhook to PagerDuty) by expanding the function.

One important mental note: email is asynchronous and reliable-ish (it will retry in the background), while Slack webhooks are synchronous — your script gets an HTTP response, and if that response isn’t 200 OK, the message didn’t arrive. We’ll handle both cases in the troubleshooting section.

How it works step by step

Let’s break down the journey of an alert from your script to a human’s screen.

Step 1: Detect the condition

Your automation runs a check (e.g., disk usage percentage). You compare it to a threshold and decide if you need to alert.

Step 2: Format the alert

Create a consistent message structure: timestamp, severity, script name, and a human-readable summary. Include enough context to act — like the disk path and capacity.

Step 3: Choose the transport

For email, you’ll use Python’s smtplib to send a message via an SMTP server (like Gmail or your company’s relay). For Slack, you’ll use requests or urllib to POST a JSON payload to a webhook URL.

Step 4: Handle failure

If the Slack POST fails, you don’t want to silently lose the alert. Fall back to email, or log a critical error. Email is more robust, so it’s often the fallback.

Step 5: Integrate with your existing scripts

Wrap the alert logic into a reusable module, then import it anywhere in your codebase.

Hands-on walkthrough

Let’s build a simple but complete alerting system. We’ll start with email, then Slack, and finally combine both into a unified alert()function.

Email alerts with SMTP

First, you need an SMTP server. We’ll use Gmail’s SMTP for example, but any provider works. Enable an App Password for your Google account (not your regular password) — that’s the modern secure way.

The core is Python’s smtplib and email.message.EmailMessage.

import smtplib
from email.message import EmailMessage
from datetime import datetime

def send_email_alert(subject, body, to_addr):
    msg = EmailMessage()
    msg["Subject"] = f"[ALERT] {subject} - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
    msg["From"] = "alertbot@yourdomain.com"
    msg["To"] = to_addr
    msg.set_content(body)

    # Use Gmail SMTP as an example
    smtp_server = "smtp.gmail.com"
    smtp_port = 587
    username = "yourapp@gmail.com"
    password = "your-app-password"  # Store in env var!

    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()  # secure connection
        server.login(username, password)
        server.send_message(msg)
    print("Email sent")

# Example usage
send_email_alert("Disk usage critical", "Disk / is at 95% capacity on host-01", "oncall@example.com")

Expected output:

Email sent

Slack alerts via webhook

Slack webhooks are simpler. Create an Incoming Webhook in your Slack app, copy the URL, and POST a JSON payload to it.

import requests
from datetime import datetime

def send_slack_alert(message, webhook_url):
    payload = {
        "text": f"🚨 *Alert* at {datetime.now().strftime('%Y-%m-%d %H:%M')} - {message}",
        "username": "alertbot",
        "icon_emoji": ":rotating_light:",
    }
    response = requests.post(webhook_url, json=payload)
    if response.status_code != 200:
        raise ValueError(f"Slack webhook failed with code {response.status_code}")
    print("Slack alert sent")

# Usage
webhook = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX"
my_disk_check()  # imagine this returns disk usage
if disk_usage > 90:
    send_slack_alert("Disk usage is at 95% on host-01", webhook)

Expected output:

Slack alert sent

Combine both into a fallback pattern

Now we create a robust send_alert function that tries Slack first and falls back to email if Slack fails.

import logging

def send_alert(subject, message, to_addr, slack_webhook=None):
    try:
        if slack_webhook:
            send_slack_alert(message, slack_webhook)
        else:
            raise RuntimeError("No Slack webhook provided")
    except Exception as e:
        logging.warning(f"Slack failed, falling back to email: {e}")
        send_email_alert(subject, message, to_addr)

In this pattern, the try/except catches any Slack failure — network error, invalid webhook, or missing webhook — and automatically uses email instead. This way alerting never silently dies.

Compare options / when to choose what

Both email and Slack have their strengths. Here’s a quick comparison:

Characteristic Email (SMTP) Slack (Webhook)
Setup complexity Medium (SMTP server, credentials) Low (create webhook URL)
Real-time delivery Fast, but can be ignored Instant, visible in busy channel
Reliability High (SMTP retries) Medium (depends on HTTP request)
Message content Text/HTML Text with markdown, emoji, mentions
Best for Formal audits, logs, archival Urgent notifications, team collaboration
Cost Free with your SMTP provider, but may have limits Free for Incoming Webhooks on any Slack plan

When to choose what:

  • Email is non-negotiable for compliance or audit trails — you need a permanent record.
  • Slack is your goto for critical, actionable alerts that need immediate attention — like a production outage.
  • In practice, use both: Slack for alerting the on-call, email for the paper trail.

Variations to consider

  • Upgrade from SMTP to a service like SendGrid or AWS SES — more reliable, higher deliverability, but requires API keys.
  • Use Slack’s chat.postMessage API instead of webhooks — allows more control, like mentioning specific users with @userId, but requires a bot token.

Troubleshooting & edge cases

Even with a simple setup, things go wrong. Here are common failures and fixes.

SMTP authentication fails (535, 5.7.8)

Symptom: smtplib.SMTPAuthenticationError or Error: Authentication unsuccessful.

Cause: The username/password is wrong, or you’re using your normal Gmail password instead of an App Password.

Fix:

  1. Enable 2FA on your Google account.
  2. Generate an App Password under Google Account → Security → App passwords.
  3. Use that 16-character password in your script.

Slack webhook returns 404

Symptom: Status code 404 from Slack.

Cause: The webhook URL is invalid (maybe you’ve only pasted part of it).

Fix: Recreate the webhook in your Slack app and ensure the full URL is copied, including the random string at the end.

Slack returns 200 but message doesn’t appear

Symptom: Slack returns HTTP 200, but no message in the channel.

Cause: The message might have been posted but with an invalid icon_emoji or other field that Slack ignores. It could also be sent to the wrong channel if your webhook is configured for a specific channel.

Fix: Check the webhook’s configured channel in the Slack app settings. Also test with a minimal payload ({"text":"hello"}) to isolate the issue.

Email goes to spam or isn’t delivered

Cause: Your server’s IP reputation or missing SPF/DKIM records.

Fix: For production, use a trusted SMTP provider (like SendGrid or SES). For local testing, keep console output instead of real email.

Alert spam — too many emails/Slack messages

Cause: The script runs every minute and alerts every time.

Fix: Add deduplication — only alert if the state has changed. For example:

# Track previous state in a file or variable
if disk_usage > 90 and not already_alerted:
    send_alert(...)
    already_alerted = True
elif disk_usage <= 90:
    already_alerted = False

That prevents the classic alert storm.

What you learned & what's next

You now have a practical, reusable alerting layer in Python. Specifically, you learned:

  • How to send an email alert using smtplib and EmailMessage.
  • How to send a Slack alert by POSTing to a webhook.
  • How to combine both into a resilient fallback pattern.
  • How to troubleshoot common SMTP and webhook failures.
  • A deduplication trick to stop alert spam.

You’ve satisfied the learning objectives of this lesson: you can explain the core idea behind send alerts via email and Slack, and you’ve completed a hands-on exercise.

Next step in the track: In the next lesson, we’ll explore monitoring external services using headless browsers with Selenium. You’ll take the alerting skills you just built and apply them to watch over a web application — clicking buttons, filling forms, and catching JavaScript errors that plain HTTP checks miss. That’s the perfect marriage of alerting and automation.

Go ahead and run the examples above — swap in your real email and webhook (or use a test channel), and you’ll have your own alerting system in minutes.

Practice recap

Build a simple disk-usage script that sends a Slack alert when usage exceeds a threshold and falls back to email. Test both paths by temporarily disabling the webhook. Then add a state file so it only alerts once per threshold crossing — that’s a realistic mini-project you can run in your own environment.

Common mistakes

  • Storing SMTP credentials or Slack webhook URLs in plain text inside the script — always use environment variables or a secrets manager.
  • Not falling back from Slack to email — if the webhook fails, the alert is lost forever. Always wrap Slack in a try/except and have an email backup.
  • Alerting on every check without deduplication, causing alert storms that get ignored.
  • Assuming the email was sent because send_message() returned without error — check the SMTP server’s response, and always use TLS for security.

Variations

  1. Use a third-party email API like SendGrid or AWS SES instead of raw SMTP for better deliverability and built-in monitoring.
  2. Use Slack’s chat.postMessage API with a bot token instead of an incoming webhook — allows targeting specific users or threaded replies.
  3. Implement a unified notification abstraction that can send to email, Slack, or a custom webhook — making future channels drop-in additions.

Real-world use cases

  • A nightly cron job checks disk usage on a fleet of servers and alerts the on-call via Slack when any disk crosses 90%.
  • A CI/CD pipeline posts a test failure summary to a team’s Slack channel and emails the release manager for formal tracking.
  • A certificate expiration checker sends a warning email a week before expiry, and a Slack alert the day before to trigger renewal.

Key takeaways

  • Email and Slack alerting are separate transports, both triggered from Python — SMTP for email, webhooks for Slack.
  • Always build a unified alert function that tries one channel and falls back to another to avoid silent failures.
  • Format alerts with timestamps, severity, and context to make them actionable.
  • Deduplicate alerts to avoid spam — only alert on state changes.
  • Test your alerting path with a simulated failure — don’t wait for a real incident.
  • Store all credentials (SMTP password, webhook URLs) in environment variables, never in the source code.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.