snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
This commit is contained in:
238
docs/production/email.md
Normal file
238
docs/production/email.md
Normal file
@@ -0,0 +1,238 @@
|
||||
# Outgoing email
|
||||
|
||||
Zulip needs to be able to send email so it can confirm new users'
|
||||
email addresses and send notifications.
|
||||
|
||||
:::{important}
|
||||
|
||||
If you are using [Docker](docker.md), see
|
||||
{doc}`docker:how-to/compose-settings` for `SETTING_EMAIL_*`
|
||||
configuration and {doc}`docker:how-to/compose-secrets` for the
|
||||
`email_password` secret.
|
||||
|
||||
:::
|
||||
|
||||
## How to configure
|
||||
|
||||
1. Identify an outgoing email (SMTP) account where you can have Zulip
|
||||
send mail. If you don't already have one you want to use, see
|
||||
[Email services](#email-services) below.
|
||||
|
||||
1. Fill out the section of `/etc/zulip/settings.py` headed "Outgoing
|
||||
email (SMTP) settings". This includes the hostname and typically
|
||||
the port to reach your SMTP provider, and the username to log in to
|
||||
it. If your SMTP server does not require authentication, leave
|
||||
`EMAIL_HOST_USER` empty.
|
||||
|
||||
You'll also want to fill out the noreply email section, taking into
|
||||
account the inline documentation for the proper values for those
|
||||
settings, e.g., `NOREPLY_EMAIL_ADDRESS` and `TOKENIZED_NOREPLY_EMAIL_ADDRESS`.
|
||||
Note that you can also configure `INSTALLATION_NAME` to customize the
|
||||
email sender display name for notification emails from your Zulip
|
||||
server.
|
||||
|
||||
1. Put the password for the SMTP user account in
|
||||
`/etc/zulip/zulip-secrets.conf` by setting `email_password`. For
|
||||
example: `email_password = abcd1234`.
|
||||
|
||||
Like any other change to the Zulip configuration, be sure to
|
||||
[restart the server](settings.md) to make your changes take
|
||||
effect.
|
||||
|
||||
1. Configure your SMTP server to allows your Zulip server to send
|
||||
emails originating from the email addresses listed in
|
||||
`/etc/zulip/settings.py` as `ZULIP_ADMINISTRATOR`,
|
||||
`NOREPLY_EMAIL_ADDRESS` and if `ADD_TOKENS_TO_NOREPLY_ADDRESS=True`
|
||||
(the default), `TOKENIZED_NOREPLY_EMAIL_ADDRESS`.
|
||||
|
||||
If you don't know how to do this, we recommend using a [free
|
||||
transactional email service](#free-outgoing-email-services); they
|
||||
will guide you through everything you need to do, covering details
|
||||
like configuring DKIM/SPF authentication so your Zulip emails won't
|
||||
be spam filtered.
|
||||
|
||||
1. Use Zulip's email configuration test tool, documented in the
|
||||
[Troubleshooting section](#troubleshooting), to verify that your
|
||||
configuration is working.
|
||||
|
||||
1. Once your configuration is working, restart the Zulip server with
|
||||
`su zulip -c '/home/zulip/deployments/current/scripts/restart-server'`.
|
||||
|
||||
## Email services
|
||||
|
||||
### Free outgoing email services
|
||||
|
||||
For sending outgoing email from your Zulip server, we highly recommend
|
||||
using a "transactional email" service like
|
||||
[Mailgun](https://documentation.mailgun.com/docs/mailgun/user-manual/sending-messages/send-smtp),
|
||||
[SendGrid](https://www.twilio.com/docs/sendgrid/for-developers/sending-email/integrating-with-the-smtp-api),
|
||||
or, for AWS users,
|
||||
[Amazon SES](https://docs.aws.amazon.com/ses/latest/dg/send-email-smtp.html).
|
||||
These services are designed to send email from servers, and are by far
|
||||
the easiest way to get outgoing email working reliably (Mailgun has
|
||||
the best documentation).
|
||||
|
||||
If you don't have an existing outgoing SMTP provider, don't worry!
|
||||
Each of the options we recommend above (as well as dozens of other
|
||||
services) have free options. Once you've signed up, you'll want to
|
||||
find the service's provided "SMTP credentials", and configure Zulip as
|
||||
follows:
|
||||
|
||||
- The hostname like `EMAIL_HOST = 'smtp.mailgun.org'` in `/etc/zulip/settings.py`
|
||||
- The username like `EMAIL_HOST_USER = 'username@example.com'` in
|
||||
`/etc/zulip/settings.py`.
|
||||
- The TLS setting as `EMAIL_USE_TLS = True` in
|
||||
`/etc/zulip/settings.py`, for most providers
|
||||
- The port as `EMAIL_PORT = 587` in `/etc/zulip/settings.py`, for most
|
||||
providers
|
||||
- The password like `email_password = abcd1234` in `/etc/zulip/zulip-secrets.conf`.
|
||||
|
||||
If your SMTP provider uses implicit SSL/TLS on port 465 (and not `STARTTLS` on
|
||||
port 587), you need to set `EMAIL_PORT = 465`, as well as replacing
|
||||
[`EMAIL_USE_TLS = True`](https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-EMAIL_USE_TLS)
|
||||
with [`EMAIL_USE_SSL = True`](https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-EMAIL_USE_SSL).
|
||||
|
||||
### Using system email
|
||||
|
||||
If you'd like to send outgoing email using the local operating
|
||||
system's email delivery configuration (e.g., you have `postfix`
|
||||
configuration on the system that forwards email sent locally into your
|
||||
corporate email system), you will likely need to use something like
|
||||
these setting values:
|
||||
|
||||
```python
|
||||
EMAIL_HOST = 'localhost'
|
||||
EMAIL_PORT = 25
|
||||
EMAIL_USE_TLS = False
|
||||
EMAIL_HOST_USER = ""
|
||||
```
|
||||
|
||||
We should emphasize that because modern spam filtering is very
|
||||
aggressive, you should make sure your downstream email system is
|
||||
configured to properly sign outgoing email sent by your Zulip server
|
||||
(or check your spam folder) when using this configuration. See
|
||||
[documentation on using Django with a local postfix server][postfix-email]
|
||||
for additional advice.
|
||||
|
||||
[postfix-email]: https://stackoverflow.com/questions/26333009/how-do-you-configure-django-to-send-mail-through-postfix
|
||||
|
||||
### Using Gmail for outgoing email
|
||||
|
||||
We don't recommend using an inbox product like Gmail for outgoing
|
||||
email, because Gmail's anti-spam measures make this annoying. But if
|
||||
you want to use a Gmail account to send outgoing email anyway, here's
|
||||
how to make it work:
|
||||
|
||||
- Create a totally new Gmail account for your Zulip server; you don't
|
||||
want Zulip's automated emails to come from your personal email address.
|
||||
- If you're using 2-factor authentication on the Gmail account, you'll
|
||||
need to use an
|
||||
[app-specific password](https://support.google.com/accounts/answer/185833).
|
||||
- If you're not using 2-factor authentication, read this Google
|
||||
support answer and configure that account as
|
||||
["less secure"](https://support.google.com/accounts/answer/6010255);
|
||||
Gmail doesn't allow servers to send outgoing email by default.
|
||||
- Note also that the rate limits for Gmail are also quite low
|
||||
(e.g., 100 / day), so it's easy to get rate-limited if your server
|
||||
has significant traffic. For more active servers, we recommend
|
||||
moving to a free account on a transactional email service.
|
||||
|
||||
### Logging outgoing email to a file for prototyping
|
||||
|
||||
For prototyping, you might want to proceed without setting up an email
|
||||
provider. If you want to see the emails Zulip would have sent, you
|
||||
can log them to a file instead.
|
||||
|
||||
To do so, add these lines to `/etc/zulip/settings.py`:
|
||||
|
||||
```python
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
|
||||
EMAIL_FILE_PATH = '/var/log/zulip/emails'
|
||||
```
|
||||
|
||||
Then outgoing emails that Zulip would have sent will just be written
|
||||
to files in `/var/log/zulip/emails/`.
|
||||
|
||||
Remember to delete this configuration (and restart the server) if you
|
||||
later set up a real SMTP provider!
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
You can quickly test your outgoing email configuration using:
|
||||
|
||||
```bash
|
||||
su zulip -c '/home/zulip/deployments/current/manage.py send_test_email user@example.com'
|
||||
```
|
||||
|
||||
If it doesn't throw an error, it probably worked; you can confirm by
|
||||
checking your email. You should get two emails: One sent by the
|
||||
default From address for your Zulip server, and one sent by the
|
||||
"noreply" From address.
|
||||
|
||||
If it doesn't work, check these common failure causes:
|
||||
|
||||
- Your hosting provider may block outgoing SMTP traffic in its default
|
||||
firewall rules. Check whether the port `EMAIL_PORT` is blocked in
|
||||
your hosting provider's firewall.
|
||||
|
||||
- Your SMTP server's permissions might not allow the email account
|
||||
you're using to send email from the `noreply` email addresses used
|
||||
by Zulip when sending confirmation emails.
|
||||
|
||||
For security reasons, Zulip sends confirmation emails (used for
|
||||
account creation, etc.) with randomly generated from addresses
|
||||
starting with `noreply-`.
|
||||
|
||||
If necessary, you can set `ADD_TOKENS_TO_NOREPLY_ADDRESS` to `False`
|
||||
in `/etc/zulip/settings.py` (which will cause these confirmation
|
||||
emails to be sent from a consistent `noreply@` address). Disabling
|
||||
`ADD_TOKENS_TO_NOREPLY_ADDRESS` is generally safe if you are not
|
||||
using Zulip's feature that allows anyone to create an account in
|
||||
your Zulip organization if they have access to an email address in a
|
||||
certain domain. See [this article][helpdesk-attack] for details on
|
||||
the security issue with helpdesk software that
|
||||
`ADD_TOKENS_TO_NOREPLY_ADDRESS` helps protect against.
|
||||
|
||||
- Make sure you set the password in `/etc/zulip/zulip-secrets.conf`.
|
||||
|
||||
- Check the username and password for typos.
|
||||
|
||||
- Be sure to restart your Zulip server after editing either
|
||||
`settings.py` or `zulip-secrets.conf`, using
|
||||
`/home/zulip/deployments/current/scripts/restart-server` .
|
||||
Note that the `manage.py` command above will read the latest
|
||||
configuration from the config files, even if the server is still
|
||||
running with an old configuration.
|
||||
|
||||
### Advanced troubleshooting
|
||||
|
||||
Here are a few final notes on what to look at when debugging why you
|
||||
aren't receiving emails from Zulip:
|
||||
|
||||
- Most transactional email services have an "outgoing email" log where
|
||||
you can inspect the emails that reached the service, whether an
|
||||
email was flagged as spam, etc.
|
||||
|
||||
- Zulip logs an entry in `/var/log/zulip/send_email.log` whenever it
|
||||
attempts to send an email. The log entry includes whether the
|
||||
request succeeded or failed.
|
||||
|
||||
- If attempting to send an email throws an exception, a traceback
|
||||
should be in `/var/log/zulip/errors.log`, along with any other
|
||||
exceptions Zulip encounters.
|
||||
|
||||
- Zulip's email sending configuration is based on the standard Django
|
||||
[SMTP backend](https://docs.djangoproject.com/en/5.0/topics/email/#smtp-backend)
|
||||
configuration. So if you're having trouble getting your email
|
||||
provider working, you may want to search for documentation related
|
||||
to using your email provider with Django.
|
||||
|
||||
The one thing we've changed from the Django defaults is that we read
|
||||
the email password from the `email_password` entry in the Zulip
|
||||
secrets file, as part of our policy of not having any secret
|
||||
information in the `/etc/zulip/settings.py` file. In other words,
|
||||
if Django documentation references setting `EMAIL_HOST_PASSWORD`,
|
||||
you should instead set `email_password` in
|
||||
`/etc/zulip/zulip-secrets.conf`.
|
||||
|
||||
[helpdesk-attack]: https://medium.com/intigriti/how-i-hacked-hundreds-of-companies-through-their-helpdesk-b7680ddc2d4c
|
||||
Reference in New Issue
Block a user