Mailgun’s SMTP endpoint doesn’t actually send email itself; it’s a gateway that forwards your mail to one of their actual sending servers, which are themselves just sophisticated wrappers around standard SMTP servers.

Let’s get this email sending. We’ll use Mailgun’s SMTP service, which is a fantastic way to reliably send transactional emails from your application without managing your own mail server.

Here’s a typical setup for sending email via Mailgun’s SMTP from a Python application using the smtplib library.

import smtplib
from email.mime.text import MIMEText

# Mailgun SMTP server details
mailgun_smtp_server = "smtp.mailgun.org"
mailgun_smtp_port = 587  # Use 587 for STARTTLS, 465 for SSL
mailgun_smtp_login = "postmaster@your-domain.mailgun.org" # Replace with your Mailgun login
mailgun_smtp_password = "YOUR_API_KEY" # Replace with your Mailgun API key (SMTP password)

# Email details
sender_email = "sender@your-domain.com" # Must be a verified sender in Mailgun
recipient_email = "recipient@example.com"
subject = "Test Email from Mailgun SMTP"
body = "This is a test email sent using Mailgun's SMTP service."

# Create the email message
message = MIMEText(body)
message['Subject'] = subject
message['From'] = sender_email
message['To'] = recipient_email

try:
    # Connect to the Mailgun SMTP server
    with smtplib.SMTP(mailgun_smtp_server, mailgun_smtp_port) as server:
        server.set_debuglevel(1) # Uncomment for verbose debugging

        # Start TLS encryption
        server.starttls()

        # Login to Mailgun
        server.login(mailgun_smtp_login, mailgun_smtp_password)

        # Send the email
        server.sendmail(sender_email, recipient_email, message.as_string())

    print("Email sent successfully!")

except Exception as e:
    print(f"Error sending email: {e}")

This code snippet demonstrates the core mechanics. You instantiate an smtplib.SMTP object, establish a secure connection using starttls(), authenticate with your Mailgun credentials, and then use sendmail() to dispatch the message. The email.mime.text.MIMEText part is just standard Python for constructing an email message.

What’s Happening Under the Hood?

When your application makes that smtplib.SMTP connection, it’s not talking directly to the Mailgun servers that will eventually deliver the email to the recipient’s inbox. Instead, it’s connecting to a specific Mailgun SMTP relay server. This relay server’s job is to receive your email, authenticate your application, and then pass the email along to Mailgun’s internal routing system. From there, Mailgun’s actual sending infrastructure takes over, handling DNS lookups, IP reputation, retries, and all the nitty-gritty of getting the email to its final destination.

The postmaster@your-domain.mailgun.org address is a convention. Mailgun uses this as the username for SMTP authentication. The password for this login is your Mailgun API key, specifically the one labeled as "SMTP Password" in your Mailgun control panel.

The port 587 is the standard for SMTP with STARTTLS. This means the connection starts unencrypted, and then starttls() negotiates an encrypted TLS tunnel. If you prefer, you can use port 465 with smtplib.SMTP_SSL to establish an SSL/TLS encrypted connection from the very beginning.

Key Configuration Points

  1. Domain Verification: Before you can send email from a domain through Mailgun, that domain must be verified in your Mailgun account. This involves adding specific DNS records (like MX, TXT, and CNAME) to your domain’s DNS zone. Mailgun provides these records in your account dashboard. Without this, Mailgun won’t accept mail from your domain, and your SMTP connection will likely fail authentication or the emails will be rejected.

  2. Sender Authorization: The sender_email you use in sendmail() (e.g., sender@your-domain.com) must be an authorized sender within your Mailgun account. For new domains, Mailgun automatically authorizes postmaster@your-domain.mailgun.org. For other addresses, you need to explicitly add them as "Authorized Recipients" or "Verified Senders" in your Mailgun domain settings. This prevents spoofing.

  3. API Key (SMTP Password): The mailgun_smtp_password is your Mailgun API key. You can find this in your Mailgun Control Panel under "Account Settings" -> "API Keys". It’s crucial to use the correct API key, as this is how Mailgun authenticates your application’s requests. Treat this like a password; don’t commit it directly into your code. Use environment variables or a secrets management system.

  4. SMTP Server and Port: While smtp.mailgun.org and port 587 (STARTTLS) are the most common, Mailgun also supports smtp.eu.mailgun.org for their EU region. Always use the server and port appropriate for your Mailgun region. Port 465 is also available for SSL-only connections.

Common Pitfalls and Debugging

  • Authentication Errors: If you get an authentication error (e.g., "535 Authentication failed"), double-check your mailgun_smtp_login and mailgun_smtp_password. Ensure you are using the correct API key and that the login format is postmaster@your-domain.mailgun.org (or your custom subdomain if you’ve configured one).
  • Invalid Sender: If emails are bounced back with an "unauthorized sender" error, ensure the sender_email is verified in your Mailgun domain settings.
  • Domain Not Verified: If you get a general connection or authentication failure, and your domain isn’t verified, this is often the culprit. Verify your domain’s DNS records with Mailgun.
  • Network/Firewall Issues: Ensure your server or local machine can reach smtp.mailgun.org on port 587 or 465. Firewalls can sometimes block outbound SMTP traffic.
  • Incorrect Port: Using port 25 is generally discouraged and often blocked by ISPs. Stick to 587 or 465.

The most common next issue you’ll encounter is dealing with email deliverability, specifically how Mailgun handles bounces, complaints, and suppressed email addresses, which requires setting up webhooks.

Want structured learning?

Take the full Smtp course →