Introduction

Ansible can send email notifications using the community.general.mail module — for deployment reports, error alerts, job completion summaries, and scheduled status updates. This article covers Gmail SMTP setup, HTML emails, attachments, conditional failure alerts, and securing credentials with Ansible Vault.

Prerequisites

Install the community.general collection:

ansible-galaxy collection install community.general

Basic Email

---
- name: Send email report
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Send notification
      community.general.mail:
        host: smtp.gmail.com
        port: 587
        username: "{{ vault_email_user }}"
        password: "{{ vault_email_pass }}"
        to: "Ops Team <ops@example.com>"
        subject: "Ansible Playbook Report"
        body: "Deployment completed successfully on {{ ansible_date_time.date | default('today') }}"
      delegate_to: localhost

Module Parameters

ParameterRequiredDefaultDescription
hostNolocalhostSMTP server hostname
portNo25SMTP port (587 for TLS, 465 for SSL)
usernameNo—SMTP authentication username
passwordNo—SMTP authentication password
toYes—Recipient(s) — string or list
ccNo—CC recipients
bccNo—BCC recipients
fromNorootSender address
subjectYes—Email subject line
bodyNo—Email body content
subtypeNoplainplain or html
attachNo—File paths to attach
headersNo—Custom email headers
charsetNoutf-8Character encoding
secureNostarttlsalways, never, starttls, try
timeoutNo20Connection timeout in seconds

Gmail SMTP Setup

App Password (Required for Gmail)

Gmail requires an App Password when 2FA is enabled:

  1. Go to Google Account Security
  2. Enable 2-Step Verification
  3. Generate an App Password: Security → App Passwords → Mail
  4. Use the 16-character app password in your playbook

Secure Credentials with Vault

# Create encrypted vars file
ansible-vault create vars/email_creds.yml
# vars/email_creds.yml (encrypted)
vault_email_user: "your.email@gmail.com"
vault_email_pass: "abcd efgh ijkl mnop"  # App Password
---
- name: Send email with vault credentials
  hosts: localhost
  gather_facts: false
  vars_files:
    - vars/email_creds.yml
  tasks:
    - name: Send report
      community.general.mail:
        host: smtp.gmail.com
        port: 587
        username: "{{ vault_email_user }}"
        password: "{{ vault_email_pass }}"
        to: ops@example.com
        subject: "Daily Report"
        body: "All systems operational."
      delegate_to: localhost
      no_log: true

HTML Emails

- name: Send HTML report
  community.general.mail:
    host: smtp.gmail.com
    port: 587
    username: "{{ vault_email_user }}"
    password: "{{ vault_email_pass }}"
    to: team@example.com
    subject: "Deployment Report — {{ ansible_date_time.date }}"
    subtype: html
    body: |
      <html>
      <body>
        <h2>Deployment Report</h2>
        <table border="1" cellpadding="5">
          <tr><th>Host</th><th>Status</th></tr>
          {% for host in ansible_play_hosts %}
          <tr>
            <td>{{ host }}</td>
            <td style="color: green;">✅ Success</td>
          </tr>
          {% endfor %}
        </table>
        <p>Completed at {{ ansible_date_time.iso8601 }}</p>
      </body>
      </html>
  delegate_to: localhost

Email with Attachments

- name: Send report with log attachment
  community.general.mail:
    host: smtp.gmail.com
    port: 587
    username: "{{ vault_email_user }}"
    password: "{{ vault_email_pass }}"
    to: admin@example.com
    subject: "Ansible Job Log"
    body: "Please find the attached execution log."
    attach:
      - /var/log/ansible/playbook.log
      - /tmp/reports/inventory.csv
  delegate_to: localhost

Failure Notifications

Send Email on Playbook Failure

---
- name: Deploy application
  hosts: webservers
  become: true
  tasks:
    - name: Pull latest code
      ansible.builtin.git:
        repo: https://github.com/example/app.git
        dest: /opt/app
        version: main

    - name: Restart application
      ansible.builtin.service:
        name: myapp
        state: restarted

  rescue:
    - name: Send failure alert
      community.general.mail:
        host: smtp.gmail.com
        port: 587
        username: "{{ vault_email_user }}"
        password: "{{ vault_email_pass }}"
        to: oncall@example.com
        subject: "🚨 DEPLOYMENT FAILED on {{ inventory_hostname }}"
        subtype: html
        body: |
          <h3>Deployment Failed</h3>
          <p><b>Host:</b> {{ inventory_hostname }}</p>
          <p><b>Error:</b> {{ ansible_failed_result.msg | default('Unknown') }}</p>
          <p><b>Time:</b> {{ now() }}</p>
      delegate_to: localhost
      no_log: true

Send Summary After All Hosts

---
- name: Deploy and report
  hosts: webservers
  become: true
  tasks:
    - name: Deploy application
      ansible.builtin.command: /opt/deploy.sh
      register: deploy_result

- name: Send summary email
  hosts: localhost
  gather_facts: false
  tasks:
    - name: Send deployment summary
      community.general.mail:
        host: smtp.gmail.com
        port: 587
        username: "{{ vault_email_user }}"
        password: "{{ vault_email_pass }}"
        to: team@example.com
        subject: "Deployment Complete — {{ ansible_play_hosts | length }} hosts"
        body: |
          Deployment finished.
          Hosts: {{ groups['webservers'] | join(', ') }}
          Time: {{ now() }}
      no_log: true

Multiple Recipients

- name: Send to multiple recipients
  community.general.mail:
    host: smtp.gmail.com
    port: 587
    username: "{{ vault_email_user }}"
    password: "{{ vault_email_pass }}"
    to:
      - "Admin <admin@example.com>"
      - "Ops <ops@example.com>"
    cc:
      - "Manager <manager@example.com>"
    bcc:
      - "Audit <audit@example.com>"
    subject: "Weekly Infrastructure Report"
    body: "See attached report."

Other SMTP Providers

ProviderHostPortSecure
Gmailsmtp.gmail.com587starttls
Outlook/O365smtp.office365.com587starttls
Amazon SESemail-smtp.us-east-1.amazonaws.com587starttls
SendGridsmtp.sendgrid.net587starttls
Mailgunsmtp.mailgun.org587starttls
Local relaylocalhost25never

Troubleshooting

Authentication Failed

fatal: "Authentication unsuccessful"
  • Gmail: Use App Password, not your regular password
  • Ensure 2FA is enabled for App Passwords to work
  • Check secure: starttls for port 587

Connection Timeout

# Increase timeout for slow SMTP servers
- community.general.mail:
    host: smtp.example.com
    timeout: 60

Certificate Errors

# For self-signed SMTP servers (not recommended for production)
- community.general.mail:
    host: internal-smtp.example.com
    port: 587
    secure: try

Best Practices

  1. Always use Vault for SMTP credentials — never hardcode passwords
  2. Use no_log: true on mail tasks to hide credentials in output
  3. Use delegate_to: localhost — send from the controller, not remote hosts
  4. Set subtype: html for rich reports with tables and formatting
  5. Use rescue blocks for failure notifications, not separate plays
  6. Rate limit — don't send per-host emails; aggregate into summaries
  7. Test locally first — use mailhog or mailtrap.io for development

Conclusion

The community.general.mail module sends email notifications directly from Ansible playbooks — deployment reports, failure alerts, and scheduled summaries. Use Gmail with App Passwords and Ansible Vault for secure SMTP authentication. Combine with rescue blocks for automatic failure notifications and HTML subtype for rich formatted reports.