• Home
  • Projects
  • Substack
Substack

Django: Sending emails with a third-party email service

Notes on sending emails from a Django app deployed on App Engine Standard using Mailjet and django-anymail.
By Przemek, July 2020

In this note we discuss how to configure a Django application running on App Engine Standard – or in any other deployment environment which doesn’t offer a default email server. (If you’re just getting started with Django on App Engine see also the general introduction here .)

Disclosure: as I write these notes, I work as a Software Engineer at Google. In this post (like everywhere on this site) I share learnings in a personal capacity and speak only for myself.

Email and App Engine Standard

The original (“first generation”) Python App Engine runtime launched back in 2008 had a built-in, App Engine-specific API for sending emails. However, the current generation of App Engine Standard runtimes (including the Python 3.7 runtime), goes away from any App Engine-specific APIs, instead offering a genric, unopinionated hosting service (You write a standard Python app, not an App Engine Python app. ).

This means that there is no built-in email API, but as we will see in this note, we can easily set up the email functionality using a third-party email service provider.

You can learn more about the evolution of the App Engine runtimes from a blog post by Zdenko Hrček and the official docs .

Picking a third-party provider

The official docs note a few third-party mailing providers that can be used to send emails from App Engine:

  • SendGrid
  • Mailgun
  • Mailjet

Personally I’ve had good experience with Mailjet, which I picked because of their free tier of 200 emails per day unlimited in time (which is less than my personal project needs, so it’s essentially a free service) and because like me they’re based in Paris – so it almost feels like giving a shout-out to a local bakery 🥖.

Sign up and configure Mailjet

After signing up for Mailjet, we can configure the domain for the outgoing email that our app willl be sending – we can we add the domain under Sender domains & addresses .

Then, we need to configure domain authentication, so that Mailjet can prove to the computer systems on the receiving end that our application is legitimately authorized to send emails on behalf of the domain owner. This is achieved via two mechanisms: SPF and DKIM.

Mailjet interface displays instructions for setting up domain authentication

Mailjet interface displays instructions for setting up domain authentication

Both SPF and DKIM need to be configured in DNS records which we can set using our domain provider, according to guidance displayed in the Sending Domain Authentication interface of Mailjet.

  • Sender Policy Framework (SPF) allows the domain owner to configure which servers are allowed to send emails on behalf of the domain
  • DomainKeys Identified Mail (DKIM) allows Mailjet to sign each outgoing message with a private key. The corresponding public key is set in the DNS record, so that each receiver can validate the signature

django-anymail

If the application is using the regular Django APIs for mailing , actually integrating the email provider in the app is the easiest part, esp. with django-anymail – a library which can neatly bridge the Django mailing API to transparently use any of the most common email service providers, including Mailjet.

pip install django-anymail

After adding the dependency, we only need to add a few settings:

if os.getenv('GAE_APPLICATION', None):
    EMAIL_BACKEND = "anymail.backends.mailjet.EmailBackend"
    ANYMAIL = {
        "MAILJET_API_KEY": os.getenv('MYAPP_MAILJET_API_KEY'),
        "MAILJET_SECRET_KEY": os.getenv('MYAPP_MAILJET_API_SECRET'),
    }
else:
    EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
    EMAIL_FILE_PATH = '/tmp/django-emails'

DEFAULT_FROM_EMAIL = '<email in the domain of the app>'

In the snippet above, we configure the emails to be written to /tmp/django-email for local development (when not running on App Engine).

See Django: Hosting on App Engine Standard for how environment variables (os.getenv) can be used to manage private credentials.

Conclusion

And that’s it, outgoing email should “just work”!

    Topics

    • Django
    • GCP

    Outline

    • Email and App Engine Standard
    • Picking a third-party provider
    • Sign up and configure Mailjet
    • django-anymail
    • Conclusion

    If you liked this and want more ...

    People trying to get along with computers. Things we can do with AI, things we better do ourselves. An occasional segway to Steinbeck's post-rodeo hangover 💫.

    ... check out my weekly column