docker-mailserver: addmailuser is slow when number of mailboxes increases (especially with NFS)

Bug Report

Context

Firstly thanks for a great container. The email software stack must be one of the most complex to implement !

I notice that when calling addmailuser it becomes very slow when number of mailboxes increases.

I did not notice slowness too much when using non-NFS e.g. local HDD or AWS EBS storage

Now I am using NFS (actually AWS EFS) storage since deploying as docker in AWS ECS.

For example I have 2500 mailboxes and a call to addmailuser takes 60 seconds.

I have removed some old inactive mailboxes and now have 500 mailboxes but a call to addmailuser now still takes 30 seconds. Seems too slow to add a single user.

I think the slowness is in the design of check-for-changes.sh. It detects change in postfix-accounts.cf and then loops through all lines in this file, trying to add each user or update its password for each line. This may give good consistency of user data across dovecot etc… but is very inefficient. If it saved a copy of the last successful processing of file postfix-accounts.cf, then it could diff the new file against that copy and detect only one line is new and then create the new user. That would probably be 0.5 secs or less.

There is a slight risk that the copy of postfix-accounts.cf becomes incorrect, through script crashing. To help fix this the start on the container could process the entire postfix-accounts.cf (current behaviour) to fix any config “drift”.

Arguably this is not a Bug but a Feature Request.

What is affected by this bug?

Speed of addmailuser command.

When does this occur?

When number of mailboxes is high (>=500) and storage is NFS.

How do we replicate the issue?

  1. NFS storage
  2. create > 500 mailboxes
  3. time execution of addmailuser

Behavior

Actual Behavior

Expected Behavior

Your Environment

Using latest EDGE docker image from dockerhub. AWS ECS docker on EC2 nodes. EFS storage from all volumes e.g. /var/mail and /tmp/docker-mailserver

Environment Variables

From a docker ECS service spec :

     { "name" : "SSL_TYPE", "value" : "manual" },
      { "name" : "SSL_CERT_PATH", "value" : "/tmp/ssl/mail.qa-mail.tradingapps.com.crt" },
      { "name" : "SSL_KEY_PATH", "value" : "/tmp/ssl/mail.qa-mail.tradingapps.com.key" },
      { "name" : "DMS_DEBUG", "value" : "0" },
      { "name" : "ENABLE_CLAMAV", "value" : "0" },
      { "name" : "ONE_DIR", "value" : "1" },
      { "name" : "ENABLE_POP3", "value" : "1" },
      { "name" : "ENABLE_FAIL2BAN", "value" : "0" },
      { "name" : "ENABLE_MANAGESIEVE", "value" : "" },
      { "name" : "OVERRIDE_HOSTNAME", "value" : "mail.qa-mail.tradingapps.com" },
      { "name" : "POSTMASTER_ADDRESS", "value" : "" },
      { "name" : "POSTSCREEN_ACTION", "value" : "enforce" },
      { "name" : "REPORT_RECIPIENT", "value" : "0" },
      { "name" : "REPORT_INTERVAL", "value" : "daily" },
      { "name" : "SMTP_ONLY", "value" : "" },
      { "name" : "TLS_LEVEL", "value" : "" },
      { "name" : "SPOOF_PROTECTION", "value" : "" },
      { "name" : "ENABLE_SRS", "value" : "0" },
      { "name" : "PERMIT_DOCKER", "value" : "network" },
      { "name" : "VIRUSMAILS_DELETE_DELAY", "value" : "" },
      { "name" : "ENABLE_POSTFIX_VIRTUAL_TRANSPORT", "value" : "" },
      { "name" : "POSTFIX_DAGENT", "value" : "" },
      { "name" : "ENABLE_SPAMASSASSIN", "value" : "0" },
      { "name" : "SA_TAG", "value" : "2.0" },
      { "name" : "SA_TAG2", "value" : "6.31" },
      { "name" : "SA_KILL", "value" : "6.31" },
      { "name" : "SA_SPAM_SUBJECT", "value" : "***SPAM*****" },
      { "name" : "ENABLE_FETCHMAIL", "value" : "0" },
      { "name" : "FETCHMAIL_POLL", "value" : "300" },
      { "name" : "ENABLE_LDAP", "value" : "" },
      { "name" : "LDAP_START_TLS", "value" : "" },
      { "name" : "LDAP_SERVER_HOST", "value" : "" },
      { "name" : "LDAP_SEARCH_BASE", "value" : "" },
      { "name" : "LDAP_BIND_DN", "value" : "" },
      { "name" : "LDAP_BIND_PW", "value" : "" },
      { "name" : "LDAP_QUERY_FILTER_USER", "value" : "" },
      { "name" : "LDAP_QUERY_FILTER_GROUP", "value" : "" },
      { "name" : "LDAP_QUERY_FILTER_ALIAS", "value" : "" },
      { "name" : "DOVECOT_TLS", "value" : "" },
      { "name" : "DOVECOT_USER_FILTER", "value" : "" },
      { "name" : "DOVECOT_PASS_FILTER", "value" : "" },
      { "name" : "ENABLE_POSTGREY", "value" : "0" },
      { "name" : "POSTGREY_DELAY", "value" : "300" },
      { "name" : "POSTGREY_MAX_AGE", "value" : "35" },
      { "name" : "POSTGREY_TEXT", "value" : "Delayed by postgrey" },
      { "name" : "ENABLE_SASLAUTHD", "value" : "" },
      { "name" : "SASLAUTHD_MECHANISMS", "value" : "1" },
      { "name" : "SASLAUTHD_MECH_OPTIONS", "value" : "rimap" },
      { "name" : "SASLAUTHD_LDAP_SERVER", "value" : "127.0.0.1" },
      { "name" : "SASLAUTHD_LDAP_SSL", "value" : "" },
      { "name" : "SASLAUTHD_LDAP_BIND_DN", "value" : "" },
      { "name" : "SASLAUTHD_LDAP_PASSWORD", "value" : "" },
      { "name" : "SASLAUTHD_LDAP_SEARCH_BASE", "value" : "" },
      { "name" : "SASLAUTHD_LDAP_FILTER", "value" : "" },
      { "name" : "SASL_PASSWD", "value" : "" },
      { "name" : "SRS_EXCLUDE_DOMAINS", "value" : "" },
      { "name" : "SRS_SECRET", "value" : "" },
      { "name" : "RELAY_HOST", "value" : "" },
      { "name" : "RELAY_PORT", "value" : "" },
      { "name" : "RELAY_USER", "value" : "" },
      { "name" : "RELAY_PASSWORD", "value" : "" }

Relevant Stack Traces

None.

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 1
  • Comments: 15 (13 by maintainers)

Most upvoted comments

Sorry, hit the wrong button and closed this…

check-for-changes.sh is not directly involved when using addmailuser.

What I think slows down the process of adding users is this:

https://github.com/docker-mailserver/docker-mailserver/blob/45345b2f49ba52b6ef9efe6492fa3b99bd4f89ae/target/bin/addmailuser#L68-L75

That part is skipped, when not using a running container to add users.

That will wait for check-for-changes to complete. So it’s related, but still points to slowness with check-for-changes. I agree with Casper that avoiding use of the container is a possible solution to get better speed.

As you are assigned a Team Higher than “read” it should always bei possible to assign and remove labels:

Hmm, looks like a mobile app bug. Thanks!

For some reason I don’t have permission to remove labels. @wernerfred, can you assist?

Hey @davidcallen ,

To achieve the automation I extend the docker image by adding a small python webservice which can create new mailboxes by calling addmailuser. The tests make curl http requests to my “mail-admin” webservice

That’s awesome. There is a repo that was started to add this functionality, though it might look different in the end. You can check it out in https://github.com/docker-mailserver/docker-mailserver-admin if you have some free time.

Maybe check-for-changes.sh has simplicitly but inefficiency. My proposed “diff-ing” approach of the postfix-accounts.cf is maybe a bit awkward to achieve. Maybe either :

I’m not 100% but it seems like this would cause problems with the way we do testing. We can of course experiment and add new tests that prepare everything, wait for the check-for-changes and then look for all of the entries in all of the files/execute all of the list or lookup commands to make sure everything was added properly. That sounds like a daunting task personally, but not impossible 😅

The more I think about it, it seems like the admin/api project I linked to above sort of needs this though. You can find a discussion of achieving this here: https://github.com/docker-mailserver/docker-mailserver-admin/issues/3#issuecomment-877733258 and if a core queue existed for the docker-mailserver, then the API would just use it instead of doing its own thing. Very interesting!


Regardless, I opened a ticket for this (see the mentioned issue). In the meantime, I’ve added and am testing some optimizations for check-for-changes.sh which might help a bit. They can be found in https://github.com/NorseGaud/docker-mailserver/blob/check-for-changes-performance/target/scripts/check-for-changes.sh

NorseGaud is correct and that the “while loop” causes addmailuser to wait until check-for-changes.sh (unless running from a new container with setup.sh).

I’ll explain my use-case since that is why I call addmailuser from within the container. I am using docker-mailserver for a testing/QA mailserver and need automation to create new mailboxes for isolated test runs. To achieve the automation I extend the docker image by adding a small python webservice which can create new mailboxes by calling addmailuser. The tests make curl http requests to my “mail-admin” webservice. This seems like a valid approach IMHO.

Maybe check-for-changes.sh has simplicitly but inefficiency. My proposed “diff-ing” approach of the postfix-accounts.cf is maybe a bit awkward to achieve. Maybe either :

  • addmailuser/delmailuser perform the action for new mail user immediately and in-process (but without the restart of systemd services and the find+chown). And check-for-changes.sh then only does the restart of systemd services and the find+chown (not even sure if these are necessary in my setup). Not ideal perhaps since a time gap between the addmailuser and possibly a critical service restart.
  • addmailuser/delmailuser put their action into a “queue” for processing by check-for-changes.sh. This queue could be a simple file like called “add-user-<datetimestamp>.change” . It contains a single line with the user details. check-for-changes.sh reads these files in FIFO order and actions them in a batch. Its a more targeted approach - no need to check every mailbox user on each run.

However I dont know the full complexity of docker-mailserver e.g. the dovecot.quotas and postfix-virtual.cf so am reluctant to start coding a fix, at this time

check-for-changes.sh is not directly involved when using addmailuser.

What I think slows down the process of adding users is this: https://github.com/docker-mailserver/docker-mailserver/blob/45345b2f49ba52b6ef9efe6492fa3b99bd4f89ae/target/bin/addmailuser#L68-L75

That part is skipped, when not using a running container to add users.