---
title: 'send email with new cert when certbot has runned'
url: 'https://willifix.net/blog/send-email-with-new-cert-when-certbot-has-runned'
markdown: 'https://willifix.net/blog/send-email-with-new-cert-when-certbot-has-runned.md'
date: '2025-11-03'
description: "today, I'll show you how to use certbot and a small python script I wrote to receive the newly created certificates as email attachments. Why A few weeks ago, a webserver certificate expired. This is usually not a problem, as certbot takes care of it. If certbot fails, we also receive information a…"
---

## [send email with new cert when certbot has runned](https://willifix.net/blog/send-email-with-new-cert-when-certbot-has-runned)

    3rd Nov 2025  

today, I'll show you how to use [certbot](https://certbot.eff.org) and a small python script I wrote to receive the newly created certificates as email attachments.

### Why

A few weeks ago, a webserver certificate expired. This is usually not a problem, as `certbot` takes care of it. If `certbot` fails, we also receive information about our existing `icinga` monitoring. We also monitor the HTTP status, but this page was not covered by this check. However, in this case, things were a little different. The web application provided is protected by a WAF (Web Application Firewall).

My first thought was a script that uses `az Shell` to exchange the corresponding certificate on the WAF as well. Unfortunately, the WAF is not within my scope. This resulted in the script provided here.

### the script

  certmail.py```
# certmail Version 1.0 by ♞ Raffael.Willems@scheer-group.com
# This Script send the new Cert to imc Hosting Team for WAF Update.
# It can be full automated with az Shell but is not wished by imc hosting.

import smtplib
import os
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.image import MIMEImage
from email.mime.application import MIMEApplication

mxlist = ['host', 'user', 'password']
print(" Send Message over: " + mxlist[0])
msg = MIMEMultipart('related')
msg['Subject'] = "Certificate Update on XXX"
msg['From'] = mxlist[1]
msg['To'] = "someone@somewhere.com"
# msg['Cc'] = "matthias.fay@scheer-imc.de"
html = """\
        <html>
            <head></head>
                <body>
                    <p><center><h4 style="font-size:15px;"> Certificate Update!</h4></center></p>
                    The Certificate on XXX has been updated with certbot. Please update the WAF Certificate with the attached Certificate Chain and Key! </br></br>Thank you</br></br></br>
                    <hr>
                    <table border="0">
                        <tr>
                            <td><img src="cid:Emma" alt="Logo" style="width:90px;height:90px;"></td>
                            <td valign="top">
                            Contact: Raffael.Willems@scheer-group.com</br>
                            Adress: Uni-Campus Nord, 66123 Saarbrücken, Germany</br>
                            Phone: +49 162 4197862</br>
                            Web: <a href="https://www.willifix.net">willifix.net</a>
                            </td>
                        </tr>
                    </table>
                </body>
            </head>
        </html>
        """
part2 = MIMEText(html, 'html')

msg.attach(part2)
fp = open('/usr/local/bin/commanderkeen.gif', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
msgImage.add_header('Content-ID', '<Emma>')
msg.attach(msgImage)

fp = open('/etc/letsencrypt/live/www.aaa-kwu.de/fullchain.pem')
filename = os.path.basename(fp.name)
msgcert = MIMEApplication(fp.read())
fp.close()
msgcert.add_header(
    "Content-Disposition",
    "attachment",
    filename=filename,
    Content_Type="application/x-pem-file",
    Content_Disposition=f"attachment; filename={filename}",
)
msg.attach(msgcert)

fp = open('/etc/letsencrypt/live/www.aaa-kwu.de/privkey.pem')
filename = os.path.basename(fp.name)
msgcertkey = MIMEApplication(fp.read())
fp.close()
msgcertkey.add_header(
    "Content-Disposition",
    "attachment",
    filename=filename,
    Content_Type="application/x-pem-file",
    Content_Disposition=f"attachment; filename={filename}",
)
msg.attach(msgcertkey)

server = smtplib.SMTP(mxlist[0], 587)
server.starttls()
server.login(mxlist[1], mxlist[2])
server.send_message(msg)
server.quit()
```

#### howto install

```
curl -fssl https://willifix.net/blog/send-email-with-new-cert-when-certbot-has-runned/certmail.txt > /usr/local/bin/certmail.py
echo -n "python3 /usr/local/bin/certmail.py" >/usr/local/bin/certmail && chmod o+x /usr/local/bin/certmail
```

dont forget to change the `mxlist` and paths in the Script (highlighted lines)

##### configure certbot

###### per certificate

add the post\_hook parameter in the respective configuration file for the renewal. the configuration files are located in `/etc/letsencrypt/renewal`

add the following line:

```
# configuration for automailing the new cert by ♞ Raffael.Willems@scheer-group.com
  post_hook = /usr/local/bin/certmail
```

###### Global Configuration

it is also possible to add a global hook for all created certificates. this can be done in `/etc/letsencrypt/cli.ini`

add the following line:

```
# configuration for automailing the new cert by ♞ Raffael.Willems@scheer-group.com
  post-hook = /usr/local/bin/certmail
```

please note that the parameters in conf and cli differ. post\_hook is used for conf files and post-hook for cli files

 [ Previous Post](https://willifix.net/blog/add-onlyoffice-to-seafile) [Next Post ](https://willifix.net/blog/grav-spotiplay-plugin-first-try)

---

## Navigation

- Parent: [Blog](https://willifix.net/blog.md)
- Previous: [grav spotiplay plugin (Spotify currently listening)](https://willifix.net/blog/grav-spotiplay-plugin-first-try.md)
- Next: [add onlyoffice to seafile](https://willifix.net/blog/add-onlyoffice-to-seafile.md)
