Issues sending emails

Hey guys,

after reading through the setup docs, the gitlab issues and this thread, along a bunch of other guides and documents, I’ve managed to set up the entire baserow stack based on the no-caddy compose files. Everything apart from the emails works perfectly. The debug and test instructions outlined here don’t seem to apply to me, as I will describe in a second.

The error I get

[2023-03-24 13:19:06,569: WARNING/ForkPoolWorker-2] djcelery_email_send_multiple[1f09f2f6-c9f0-4e63-9f01-ddcd86e1526a]: Failed to send email message to ['inbox@mail.com'], retrying. (SMTPServerDisconnected('Connection unexpectedly closed'))
[2023-03-24 13:19:06,572: INFO/ForkPoolWorker-2] Task djcelery_email_send_multiple[1f09f2f6-c9f0-4e63-9f01-ddcd86e1526a] succeeded in 240.145206590998s: 0
[2023-03-24 13:19:06,573: INFO/MainProcess] Task djcelery_email_send_multiple[1f09f2f6-c9f0-4e63-9f01-ddcd86e1526a] received
[2023-03-24 13:24:07,104: ERROR/ForkPoolWorker-2] djcelery_email_send_multiple[1f09f2f6-c9f0-4e63-9f01-ddcd86e1526a]: Cannot reach CELERY_EMAIL_BACKEND django.core.mail.backends.smtp.EmailBackend
Traceback (most recent call last):
  File "/baserow/venv/lib/python3.9/site-packages/djcelery_email/tasks.py", line 40, in send_emails
    conn.open()
  File "/baserow/venv/lib/python3.9/site-packages/django/core/mail/backends/smtp.py", line 62, in open
    self.connection = self.connection_class(self.host, self.port, **connection_params)
  File "/usr/lib/python3.9/smtplib.py", line 253, in __init__
    (code, msg) = self.connect(host, port)
  File "/usr/lib/python3.9/smtplib.py", line 341, in connect
    (code, msg) = self.getreply()
  File "/usr/lib/python3.9/smtplib.py", line 398, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed")
smtplib.SMTPServerDisconnected: Connection unexpectedly closed

environment variables from compose.yml

  # Set these to enable Baserow to send emails.
  EMAIL_SMTP: "true" #putting true without quotes makes no difference
  EMAIL_SMTP_HOST: "server.mail.com"
  EMAIL_SMTP_PORT: 465
  EMAIL_SMTP_USE_TLS: #setting or not setting this makes no difference
  EMAIL_SMTP_USER: "vps"
  EMAIL_SMTP_PASSWORD: "password"
  FROM_EMAIL: "baserow@mail.com"

I’m running a bunch of other services from this server, which all use the same mailserver, credentials and port. Therefore im certain that these are correct.
Since I’m using the no-caddy compose file (/baserow/-/blob/develop/docker-compose.no-caddy.yml), it is too long to post here. I will supply needed sections from it when asked for.

Regarding the debug steps described in this post

  • The instructions from post #9 don’t seem to be applicable in my case. The baserow.sh file exists nowhere in the baserow-backend container. Running ./baserow shell from the backend directory, as instructed, returns /usr/bin/env: 'python': No such file or directory.
  • The instructions from post #4 also don’t seem to apply, because the error already states that the django.core.mail yada yada backend is being used. I did not try to change the backend to the CeleryMailBackend, mainly because I am not too sure how to in my case.

An issue on gitlab talking about SSL ports (/baserow/-/issues/1646) also has caught my attention. Since I’m forced to use port 465, I really hope there is no arbitrary limit which mail ports are usable.

I’d love to get some advice what to do here. I’m so close to deploying Baserow and wouldn’t wanna be stopped by emails not working! Thanks in advance.

It’s been a couple days, so to break the silence I’ll ping the two prominent helpers in this thread - @joffcom or @nigel, do any of you have an idea how to probe or solve this issue further?

Thanks in advance.

Hi @angelfish i wonder if this issue is the problem you are hitting No overriding django default EMAIL_USE_SSL in base.py (#1646) · Issues · Baserow / baserow · GitLab

Edit: you’ve already pointed this out above my bad!

@angelfish Would you be comfortable deploying a version of Baserow straight from our CI Docker registry with the fix discussed in that issue? Essentially I would make the fix and post an alternative docker-compose.yml with overriden docker images containing the fix for you. Then you would confirm if it has fixed the issue and we would continue and properly release this fix in the next version of Baserow.

Hey Nigel, thanks for the lightning fast response! Yes, I’d be comfortable deploying that image with potential fix youre talking about. Will that be a all-in-one image, or one with the split containers?

In any case, update me once you have something you need me to do, and I’ll get to it promptly.

Sorry I didn’t see a notification in time but @nigel is all over it :slight_smile:

Hi @angelfish

FYI these new images are built off our latest develop branch and so will be also upgrading you to version 1.16.0. As with any upgrade i’d recommend making a backup of your Baserow volumes/database first.

Could you try:

  1. Stop all of your Baserow services docker-compose down

  2. Update your docker-compose.no-caddy.yml file (I believe you are using this one?) to have the following contents:

version: "3.4"
########################################################################################
#
# This compose file runs every service separately without any reverse proxy or http
# server to serve user uploaded files. It is intended as a starting point for people
# who want to use their own reverse proxy and run Baserow with one service per
# container. The web-frontend service is available by default at http://localhost:3000
# and the backend service is available by default at http://localhost:8000 .
#
# To use this file you need to:
#   1. Set the PUBLIC_BACKEND_URL to the URL that the user's browser can access the
#      backend service on.
#   2. Set the PUBLIC_WEB_FRONTEND_URL to the URL that the user's browser can access the
#      web-frontend service on.
#   3. Host the media files in the media volume on a HTTP server and set the MEDIA_URL
#      to the URL the user's browser can access the files on.
#   4. Ensure requests sent to the backend in /ws path are upgraded to websocket
#      connections.
#
# See the following guides for more details and example configurations:
#   1. https://baserow.io/docs/installation%2Finstall-behind-nginx
#   2. https://baserow.io/docs/installation%2Finstall-behind-apache
#
# Also More documentation can be found in:
# https://baserow.io/docs/installation%2Finstall-with-docker-compose
#
########################################################################################

# See https://baserow.io/docs/installation%2Fconfiguration for more details on these
# backend environment variables, their defaults if left blank etc.
x-backend-variables: &backend-variables
  # Most users should only need to set these first four variables.
  SECRET_KEY: ${SECRET_KEY:?}
  BASEROW_JWT_SIGNING_KEY: ${BASEROW_JWT_SIGNING_KEY:-}
  DATABASE_PASSWORD: ${DATABASE_PASSWORD:?}
  REDIS_PASSWORD: ${REDIS_PASSWORD:?}

  PUBLIC_BACKEND_URL: ${PUBLIC_BACKEND_URL-http://localhost:8000}
  PUBLIC_WEB_FRONTEND_URL: ${PUBLIC_WEB_FRONTEND_URL-http://localhost:3000}

  # Set these if you want to use an external postgres instead of the db service below.
  DATABASE_USER: ${DATABASE_USER:-baserow}
  DATABASE_NAME: ${DATABASE_NAME:-baserow}
  DATABASE_HOST:
  DATABASE_PORT:
  DATABASE_URL:

  # Set these if you want to use an external redis instead of the redis service below.
  REDIS_HOST:
  REDIS_PORT:
  REDIS_PROTOCOL:
  REDIS_URL:
  REDIS_USER:

  # Set these to enable Baserow to send emails.
  EMAIL_SMTP:
  EMAIL_SMTP_HOST:
  EMAIL_SMTP_PORT:
  EMAIL_SMTP_USE_TLS:
  EMAIL_SMTP_USE_SSL:
  EMAIL_SMTP_USER:
  EMAIL_SMTP_PASSWORD:
  EMAIL_SMTP_SSL_CERTFILE_PATH:
  EMAIL_SMTP_SSL_KEYFILE_PATH:
  FROM_EMAIL:

  # Set these to use AWS S3 bucket to store user files.
  AWS_ACCESS_KEY_ID:
  AWS_SECRET_ACCESS_KEY:
  AWS_STORAGE_BUCKET_NAME:
  AWS_S3_REGION_NAME:
  AWS_S3_ENDPOINT_URL:
  AWS_S3_CUSTOM_DOMAIN:

  # Misc settings see https://baserow.io/docs/installation%2Fconfiguration for info
  BASEROW_AMOUNT_OF_WORKERS:
  BASEROW_ROW_PAGE_SIZE_LIMIT:
  BATCH_ROWS_SIZE_LIMIT:
  INITIAL_TABLE_DATA_LIMIT:
  BASEROW_FILE_UPLOAD_SIZE_LIMIT_MB:

  BASEROW_EXTRA_ALLOWED_HOSTS:
  ADDITIONAL_APPS:
  BASEROW_PLUGIN_GIT_REPOS:
  BASEROW_PLUGIN_URLS:

  BASEROW_ENABLE_SECURE_PROXY_SSL_HEADER:
  MIGRATE_ON_STARTUP: ${MIGRATE_ON_STARTUP:-true}
  SYNC_TEMPLATES_ON_STARTUP: ${SYNC_TEMPLATES_ON_STARTUP:-true}
  DONT_UPDATE_FORMULAS_AFTER_MIGRATION:
  BASEROW_TRIGGER_SYNC_TEMPLATES_AFTER_MIGRATION:
  BASEROW_SYNC_TEMPLATES_TIME_LIMIT:

  BASEROW_BACKEND_DEBUG:
  BASEROW_BACKEND_LOG_LEVEL:
  FEATURE_FLAGS:
  BASEROW_ENABLE_OTEL:
  BASEROW_DEPLOYMENT_ENV:
  OTEL_EXPORTER_OTLP_ENDPOINT:
  OTEL_RESOURCE_ATTRIBUTES:

  PRIVATE_BACKEND_URL: http://backend:8000
  BASEROW_PUBLIC_URL:
  MEDIA_URL:
  MEDIA_ROOT:

  BASEROW_AIRTABLE_IMPORT_SOFT_TIME_LIMIT:
  HOURS_UNTIL_TRASH_PERMANENTLY_DELETED:
  OLD_ACTION_CLEANUP_INTERVAL_MINUTES:
  MINUTES_UNTIL_ACTION_CLEANED_UP:
  BASEROW_GROUP_STORAGE_USAGE_QUEUE:
  DISABLE_ANONYMOUS_PUBLIC_VIEW_WS_CONNECTIONS:
  BASEROW_WAIT_INSTEAD_OF_409_CONFLICT_ERROR:
  BASEROW_DISABLE_MODEL_CACHE:
  BASEROW_PLUGIN_DIR:
  BASEROW_JOB_EXPIRATION_TIME_LIMIT:
  BASEROW_JOB_CLEANUP_INTERVAL_MINUTES:
  BASEROW_MAX_ROW_REPORT_ERROR_COUNT:
  BASEROW_JOB_SOFT_TIME_LIMIT:
  BASEROW_FRONTEND_JOBS_POLLING_TIMEOUT_MS:
  BASEROW_INITIAL_CREATE_SYNC_TABLE_DATA_LIMIT:
  BASEROW_WEBHOOKS_ALLOW_PRIVATE_ADDRESS:
  BASEROW_WEBHOOKS_IP_BLACKLIST:
  BASEROW_WEBHOOKS_IP_WHITELIST:
  BASEROW_WEBHOOKS_URL_REGEX_BLACKLIST:
  BASEROW_WEBHOOKS_URL_CHECK_TIMEOUT_SECS:
  BASEROW_WEBHOOKS_MAX_CONSECUTIVE_TRIGGER_FAILURES:
  BASEROW_WEBHOOKS_MAX_RETRIES_PER_CALL:
  BASEROW_WEBHOOKS_MAX_PER_TABLE:
  BASEROW_WEBHOOKS_MAX_CALL_LOG_ENTRIES:
  BASEROW_WEBHOOKS_REQUEST_TIMEOUT_SECONDS:
  BASEROW_ENTERPRISE_AUDIT_LOG_CLEANUP_INTERVAL_MINUTES:
  BASEROW_ENTERPRISE_AUDIT_LOG_RETENTION_DAYS:
  BASEROW_ALLOW_MULTIPLE_SSO_PROVIDERS_FOR_SAME_ACCOUNT:
  BASEROW_ROW_COUNT_JOB_CRONTAB:
  BASEROW_STORAGE_USAGE_JOB_CRONTAB:
  BASEROW_SEAT_USAGE_JOB_CRONTAB:
  BASEROW_PERIODIC_FIELD_UPDATE_CRONTAB:
  BASEROW_PERIODIC_FIELD_UPDATE_TIMEOUT_MINUTES:
  BASEROW_PERIODIC_FIELD_UPDATE_QUEUE_NAME:

services:
  backend:
    image: registry.gitlab.com/bramw/baserow/ci/backend:ci-latest-1646-no-overriding-django-default-email_use_ssl-in-base-py
    restart: unless-stopped
    ports:
      - "${HOST_PUBLISH_IP:-127.0.0.1}:8000:8000"
    environment:
      <<: *backend-variables
    depends_on:
      - db
      - redis
    volumes:
      - media:/baserow/media
    networks:
      local:

  web-frontend:
    image: registry.gitlab.com/bramw/baserow/ci/web-frontend:ci-latest-1646-no-overriding-django-default-email_use_ssl-in-base-py
    restart: unless-stopped
    ports:
      - "${HOST_PUBLISH_IP:-127.0.0.1}:3000:3000"
    environment:
      BASEROW_PUBLIC_URL:
      PRIVATE_BACKEND_URL: ${PRIVATE_BACKEND_URL:-http://backend:8000}
      PUBLIC_BACKEND_URL:
      PUBLIC_WEB_FRONTEND_URL:
      BASEROW_DISABLE_PUBLIC_URL_CHECK:
      INITIAL_TABLE_DATA_LIMIT:
      DOWNLOAD_FILE_VIA_XHR:
      BASEROW_DISABLE_GOOGLE_DOCS_FILE_PREVIEW:
      HOURS_UNTIL_TRASH_PERMANENTLY_DELETED:
      DISABLE_ANONYMOUS_PUBLIC_VIEW_WS_CONNECTIONS:
      FEATURE_FLAGS:
      ADDITIONAL_MODULES:
      BASEROW_MAX_IMPORT_FILE_SIZE_MB:
      BASEROW_MAX_SNAPSHOTS_PER_GROUP:
      BASEROW_ENABLE_OTEL:
      BASEROW_DEPLOYMENT_ENV:
    depends_on:
      - backend
    networks:
      local:

  celery:
    image: registry.gitlab.com/bramw/baserow/ci/backend:ci-latest-1646-no-overriding-django-default-email_use_ssl-in-base-py
    restart: unless-stopped
    environment:
      <<: *backend-variables
    command: celery-worker
    # The backend image's baked in healthcheck defaults to the django healthcheck
    # override it to the celery one here.
    healthcheck:
      test: [ "CMD-SHELL", "/baserow/backend/docker/docker-entrypoint.sh celery-worker-healthcheck" ]
    depends_on:
      - backend
    volumes:
      - media:/baserow/media
    networks:
      local:

  celery-export-worker:
    image: registry.gitlab.com/bramw/baserow/ci/backend:ci-latest-1646-no-overriding-django-default-email_use_ssl-in-base-py
    restart: unless-stopped
    command: celery-exportworker
    environment:
      <<: *backend-variables
    # The backend image's baked in healthcheck defaults to the django healthcheck
    # override it to the celery one here.
    healthcheck:
      test: [ "CMD-SHELL", "/baserow/backend/docker/docker-entrypoint.sh celery-exportworker-healthcheck" ]
    depends_on:
      - backend
    volumes:
      - media:/baserow/media
    networks:
      local:

  celery-beat-worker:
    image: registry.gitlab.com/bramw/baserow/ci/backend:ci-latest-1646-no-overriding-django-default-email_use_ssl-in-base-py
    restart: unless-stopped
    command: celery-beat
    environment:
      <<: *backend-variables
    # See https://github.com/sibson/redbeat/issues/129#issuecomment-1057478237
    stop_signal: SIGQUIT
    depends_on:
      - backend
    volumes:
      - media:/baserow/media
    networks:
      local:

  db:
    image: postgres:11
    restart: unless-stopped
    environment:
      - POSTGRES_USER=${DATABASE_USER:-baserow}
      - POSTGRES_PASSWORD=${DATABASE_PASSWORD:?}
      - POSTGRES_DB=${DATABASE_NAME:-baserow}
    healthcheck:
      test: [ "CMD-SHELL", "su postgres -c \"pg_isready -U ${DATABASE_USER:-baserow}\"" ]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      local:
    volumes:
      - pgdata:/var/lib/postgresql/data

  redis:
    image: redis:6
    command: redis-server --requirepass ${REDIS_PASSWORD:?}
    healthcheck:
      test: [ "CMD", "redis-cli", "ping" ]
    networks:
      local:

  # By default, the media volume will be owned by root on startup. Ensure it is owned by
  # the same user that django is running as, so it can write user files.
  volume-permissions-fixer:
    image: bash:4.4
    command: chown 9999:9999 -R /baserow/media
    volumes:
      - media:/baserow/media
    networks:
      local:

volumes:
  pgdata:
  media:
  caddy_data:
  caddy_config:

networks:
  local:
    driver: bridge
  1. Now make sure you set this new env var (in your .env file or however else you set them) EMAIL_SMTP_USE_SSL to a non blank value and also make sure to completely unset the EMAIL_SMTP_USE_TLS env var.

  2. Now you can start back up Baserow, it should now be able to send emails using an implicit TLS (secure) connection when talking to the SMTP server. In most email documentation this type of TLS connection is referred to as SSL and is generally on port 465.

1 Like

Hey @nigel ,

I am beyond words. It just works! And it’s very fast as well if I may add. For reference, these are the env vars as I set them:

EMAIL_SMTP: true
EMAIL_SMTP_HOST: "base.domain.com"
EMAIL_SMTP_PORT: 465
EMAIL_SMTP_USE_TLS:
EMAIL_SMTP_USE_SSL: true
EMAIL_SMTP_USER: "base"
EMAIL_SMTP_PASSWORD: "78asfa81sd0"
EMAIL_SMTP_SSL_CERTFILE_PATH:
EMAIL_SMTP_SSL_KEYFILE_PATH:
FROM_EMAIL: "base@domain.com"

Since this is from the dev branch, is it safe for me to run this build in prod until its changes are pushed to a stable branch? I’m eager to get this service deployed and would love to get started with transferring my databases ASAP, now that the mail issue seems to have been fixed. Or is changing from a dev to a stable build not a recommended thing to do?

Oh, and if I can do any further testing or probing to ensure stability of this feature, I’d love to. Consider it a thank you for the assistance!

Hi, on this topic, I wanted to share an anecdote. I run a self-hosted customized instance on Hetzner cloud servers. They are EXTREMELY strict about spam particularly email spam. I had an issue which kept me puzzled for weeks. It turns out their firewall blocks outgoing email ports, though 587 works. I use postmarkapp.com for outgoing email. If your baserow instance seems to freeze when running the test email, you can suspect a potential firewall block. You can troubleshoot using some network connectivity tools such as netcat.

Hello @nigel , nice to meet you.
I have installed baserow from docker on my ubuntu server. It’s installed successfully, but when I try to invite my staff, email doenst work.
I found your blog here, so I did below as you mentioned.

cd /baserow
export DJANGO_SETTINGS_MODULE='baserow.config.settings.base'
export EMAIL_SMTP='TRUE'
export EMAIL_SMTP_HOST='smtp.gmail.com'
export EMAIL_SMTP_USE_TLS='TRUE'
export EMAIL_SMTP_PORT=587
export EMAIL_SMTP_USER='my@email.com'
export EMAIL_SMTP_PASSWORD='mypassword'
export FROM_EMAIL='my@email.com'

When I ran this,

from django.core.mail import EmailMessage
from django.conf import settings

email = EmailMessage(
    'title',
    'msg',
    settings.FROM_EMAIL,
    ['REPLACE_WITH_TO_EMAIL_ADDRESS'],
)
email.send(fail_silently=False)

I keep getting error below. :point_down:

“raise ImproperlyConfigured(
django.core.exceptions.ImproperlyConfigured: Requested setting FROM_EMAIL, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.”

Of course, I replaced REPLACE_WITH_TO_EMAIL_ADDRESS to my email.

One question, why do I need to set FROM_EMAIL?
baserow document didn’t mention it.

My domain is not with https yet.

Please help me, thank you.

Oh it’s working now.

Hello!
Email works only when I change
EMAIL_BACKEND = "djcelery_email.backends.CeleryEmailBackend" to EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
in /baserow/baserow/backend/src/baserow/config/settings/base.py file.

Could you help me to debug?