Skip to content

Bug: enable_starttls_auto passed as String instead of Boolean — breaks all SMTP delivery #126

@robertsauer-ernst

Description

@robertsauer-ernst

Bug: enable_starttls_auto is passed as String instead of Boolean in production.rb

Description

In the Docker image ghcr.io/decidim/decidim:0.31.0.rc2, the SMTP setting enable_starttls_auto is passed as a String ("true") instead of a Boolean (true) to ActionMailer. This causes mail delivery to fail silently — mails appear to be queued but never arrive.

Root Cause

In /code/config/environments/production.rb:

:enable_starttls_auto => Decidim::Env.new("SMTP_STARTTLS_AUTO").to_boolean_string,

The method to_boolean_string returns "true" (a String), but Ruby's Net::SMTP / the mail gem expects a Boolean true for :enable_starttls_auto. While the String "true" is truthy in Ruby, the mail gem's STARTTLS negotiation fails when receiving a String instead of a Boolean — particularly when mail is sent via ActiveJob/background jobs.

Expected

:enable_starttls_auto => true  # Boolean (TrueClass)

Actual

:enable_starttls_auto => "true"  # String

Steps to Reproduce

  1. Start Decidim using ghcr.io/decidim/decidim:0.31.0.rc2 with Docker Compose:
services:
  pg:
    image: postgres:15
    environment:
      - POSTGRES_USER=decidim
      - POSTGRES_PASSWORD=<password>
      - POSTGRES_DB=decidim
    volumes:
      - pg-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U decidim"]
      interval: 5s
      timeout: 5s
      retries: 5
  redis:
    image: redis:7
    volumes:
      - redis-data:/data
  decidim:
    image: ghcr.io/decidim/decidim:0.31.0.rc2
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://decidim:<password>@pg:5432/decidim
      - REDIS_URL=redis://redis:6379
      - RAILS_ENV=production
      - SECRET_KEY_BASE=<generated_key>
      - RAILS_SERVE_STATIC_FILES=true
      - RAILS_LOG_TO_STDOUT=true
      - SMTP_ADDRESS=smtp.example.com
      - SMTP_PORT=587
      - [email protected]
      - SMTP_PASSWORD=<password>
      - SMTP_DOMAIN=example.com
      - SMTP_AUTHENTICATION=plain
      - SMTP_STARTTLS_AUTO=true
volumes:
  pg-data:
  redis-data:
  1. Reverse proxy with Caddy for automatic HTTPS
  2. Create an organization via /system
  3. Try to invite an admin or trigger any mail delivery
  4. Mail delivery fails — no error in container logs, mail simply doesn't arrive

Verification in Rails console

docker compose exec decidim bundle exec rails console
settings = Rails.application.config.action_mailer.smtp_settings
settings[:enable_starttls_auto].class
# => String  (should be TrueClass or FalseClass)

settings[:enable_starttls_auto]
# => "true"  (should be true)

What We Tried Before Finding the Root Cause

  1. Setting SMTP only via System Panel (no env vars) → same issue, authentication and enable_starttls_auto fields are not exposed in the System Panel UI

  2. Rails Console fix for Organization SMTP settings:

    org = Decidim::Organization.first
    org.smtp_settings = org.smtp_settings.merge({
      "authentication" => "plain",
      "enable_starttls_auto" => true
    })
    org.save!

    → Direct ActionMailer::Base.mail(...) worked in console, but ActiveJob delivery still failed

  3. Initializer approach (created inside container):

    # /code/config/initializers/smtp_fix.rb
    Rails.application.config.after_initialize do
      settings = Rails.application.config.action_mailer.smtp_settings
      if settings && settings[:enable_starttls_auto].is_a?(String)
        settings[:enable_starttls_auto] = settings[:enable_starttls_auto] == "true"
      end
    end

    → Did not fix background job delivery

  4. Direct patch of production.rb(the fix that worked):

    docker compose exec decidim sed -i \
      's/Decidim::Env.new("SMTP_STARTTLS_AUTO").to_boolean_string/ENV["SMTP_STARTTLS_AUTO"] == "true"/' \
      /code/config/environments/production.rb
    docker compose restart decidim

    → Mail delivery works correctly after this fix

Suggested Fix

In config/environments/production.rb, change:

# Before (broken):
:enable_starttls_auto => Decidim::Env.new("SMTP_STARTTLS_AUTO").to_boolean_string,

# After (fixed):
:enable_starttls_auto => ENV["SMTP_STARTTLS_AUTO"] == "true",

Alternatively, the Decidim::Env#to_boolean_string method itself should be reviewed — a method named to_boolean_string that is used where a Boolean is expected is inherently error-prone.

Note on Persistence

The sed fix inside the container is not persistent — it is lost on docker compose down && up because a fresh container is created from the image. A proper fix needs to be in the image itself.

Environment

  • Docker image: ghcr.io/decidim/decidim:0.31.0.rc2
  • Host OS: Ubuntu 24.04 on Hetzner VPS (CX33)
  • Reverse proxy: Caddy (automatic HTTPS)
  • SMTP provider: securemail.name (port 587, STARTTLS)

Impact

This bug likely affects every Docker deployment that relies on environment variables for SMTP configuration. Without the patch, no transactional emails (invitations, confirmations, notifications) are delivered.

Note: This bug report was drafted by Claude AI (Anthropic, Opus 4.6) based on the full debugging history across multiple sessions. The underlying investigation, testing, and workaround were done by the reporter (Robert Sauer-Ernst) with Claude's assistance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions