Version 1.57.0

Released: 2019-05-25

Ability to brand skin with custom override logo (SKINS)(LANG) new


Method: POST


Where the file can be any image file.

DA compares the extension to /etc/mime.types to ensure it exists and starts with "image/" else it's rejected.

You can include "which=2" to the POST or other related GET/POST queries to work on logo2.* instead of logo.*



  • css now has "pencil" and "edit_cog" class to show IMG_PENCIL and IMG_EDIT_COG icons, shown in the |SKINS| table in skins.conf.


  • files_user.conf:



  • files_reseller.conf:



  • new file:


  • skin.conf

add the IMG file you want to use for the logo. For enhanced, it's:



where any User call to IMG_SKIN_HEADER is swapped with the custom logo.


API && json=yes

View custom settings for a skin:


dumps values similar to the skin tokens:

    "HAS_CUSTOM_LOGO": "1",
    "HAS_CUSTOM_LOGO2": "1",
    "name": "enhanced"

Show skins/settings:


        "enhanced": "./data/skins/enhanced",
        "evolution": "../../../home/admin/skins/evolution"
        "default": "./data/skins/default",
        "enhanced": "./data/skins/enhanced",
        "evolution": "../../../home/admin/skins/evolution",
        "power_user": "./data/skins/power_user"
        "default": "server",
        "enhanced": "server",
        "evolution": "admin",
        "power_user": "server"

View custom logo (image response)


Clear Custom Logo:


Upload custom image:

method: POST
file=name (via file select)

You can include "which=2" to the POST or other related queries to work on logo2.* instead of logo.*



  • new file:


  • updates to:



Custom partition location for email new

New directadmin.conf option, NULL/blank by default (disabled):


Where, setting it to a specific path, say /mail, where you might want to use a mail-only SSD, or something specific, to manage email data.

After restarting DA, be sure to sync any live data via task.queue commands below.


MUST use dovecot with dovecot=1 in the directadmin.conf.

This should be the default, and most people will already have this set.


There will be a command to convert accounts from /home/user/imap/* over to $mail_partition/username/imap.

By default, that User's default disk usage limit will be set for the quotas (possibly another setting to allow xfs/ext4, etc.. in case the quota format is different).



cd /usr/local/directadmin
echo 'action=convert&value=mail_partition&user=fred' > data/task.queue; ./dataskq d200

All Users

cd /usr/local/directadmin
echo 'action=convert&value=mail_partition' > data/task.queue; ./dataskq d200

The conversion is "smart" in that it will check the new location for any matching filename.

If they are the same, and the sizes and timestamps are the same, they're assumed to be the same data, and will not be copied.

This way you can, more easily run this a few times if you need to, while being quicker on subsequent runs.

However, with imap, if you flag or set a file as read.. etc.. it changes the actual file name.

So multiple runs can in theory cause duplicate data, with the "read" message and the "unread" message possibly both showing up (for example).

Hence it's best to avoid multiple runs where possible.

If you do a per-User conversion (at first to test), cleanup that User first, before doing the "all users" run, so the test User doesn't get any duplicates.

However, another option can be added:


which simply check for the new /partition/username/imap folder.

If that imap folder exists, then it's skipped without any per-file even faster.


You can also add:


if you want to see what it's going to do, before you do it, and it will be shown in the dataskq output.

The above action=convert does do the passwd rewrite for you, but if you need to do it manually (Eg: to quickly go back to the original, assuming you've not deleted the data yet)

echo "action=rewrite&value=email_passwd&user=fred" >> /usr/local/directadmin/data/task.queue

echo "action=rewrite&value=email_passwd" >> /usr/local/directadmin/data/task.queue


Once things are running, and you're 100% sure the new mail path is working as expected, you'll want to eventually delete the ~/imap data.

To do this, again it can be done per-User or for everyone:


cd /usr/local/directadmin
echo 'action=delete&value=old_mail_partition&user=fred' > data/task.queue; ./dataskq d200

All Users:

cd /usr/local/directadmin
echo 'action=delete&value=old_mail_partition' > data/task.queue; ./dataskq d200

and as before, there is the optiont of:


to show what it's up to, without it actually doing anything


DirectAdmin will simply be altering the path inside the file:


as well as any internal path access for /home/user/imap/* to be /mail_partition/user/imap/*

as both exim's LMTP and imap/pop usage will use these settings already.

Note: Exim must use LMTP for delivery for this to work (newer 4.x exim.conf files already do this)


If you want DA to set/manage quotas for this new partition, and if it's not on the same partition as /home, then add the new partition path to:


NOTE: the mail_partition does not need to be a "partition", but just the new path that DirectAdmin will use, instead of /home.

So you could say, have:


and id /var is it's own partition, add:


and ensure that quotas are setup on this partition.

Quotas on a 2nd /home partition to be checked




OpenLiteSpeed (TEMPLATES) new

Support for OpenLiteSpeed. BETA.

New internal default option:


where you can turn it on with:


and set it in custombuild:

./build set webserver openlitespeed

More info to come.


















you can issue writes for the 2 files:



with a standard ips rewrite:

echo "action=rewrite&value=ips" >> /usr/local/directadmin/data/task.queue



skin.conf creator override new

The Evolution skin uses a skin.conf at:


This new feature will check for the existence of a creator's skin.conf in this custom location:


if this custom skin.conf path exists, it will be read in, overriding any variable that exists.

For example, if you want to reduce the number of widgets available to a User, you'd need to only add the line:


with the desired widgets.

Any User created under "CREATOR" (eg: admin), will have this custom list, instead of the default list.

You do not need to copy the whole skin.conf, just the line(s) you wish to override.

Webalizer and Awstats links, often created as /stats or /awstats are simple for Users to create.

But once the public_html/stats link is created, the User might not know how to remove them.

Added a new button:

<input type=submit name=unset value="Un-Set">

beside the Set button, on the CMD_USER_STATS? page.

That being said, similar to stats link creation, you can unset the link with this command:

method: POST

The actual code to remove the path was already present, previously running the unlink before link creation.

The unset code simply skips the creation step, but also adds a check to confirm the link has been removed.

system information page service list rewrite new

Changed how DirectAdmin manages the list of services obtained for the System Information page.

Triggered by the addition of openlitespeed, now have a cleaner unified function to know which services to be checking for, rather than a large mess of if-then-else for each service possibility.

Plugins: headers_to_env=yes to get all request headers new

Plugins now have the ability to get all request headers.

To do so, the following GET variable must be sent:


you can still make a POST request, but the above must be part of the GET data (done this way to avoid parsing huge amounts of data that can be POSTed)

Data will set to variable:


where <urlencodedheaders> is a URL string of all headers and their values.

Installer: ability to skip mysql install new

With this release, if you touch:

touch /root/.skip_mysql_install

the script at:


will abort quite early and exit status 0 with echo: found /root/.skip_mysql_install: skipping mysql install. Ensure your da_admin account is setup in /usr/local/directadmin/conf/mysql.conf

If you use this option (set it before you run the, then it will be your responsibility to ensure that the database is setup and running prior to the being run,

and that the da_admin has been setup in the mysql.conf, ready for DA to use.


There are directadmin.conf options that control how DA does queries, as queries can vary wildly depending on the type and version of the database server.

MySQL 5.5, 5.6



MySQL 5.7



MySQL 8.0+



MariaDB 5.*, 10.0, 10.1, 10.2:



MariaDB 10.3:



LetsEncrypt: disable auto-renew after 5 failed attempts, with failure message new

New internal directadmin.conf option, disabled by default:


where you can change it to 1:


which will mean that, after the 5 failed renewal attempts, specified by:


when enabled, the LE data files will be removed, and no further renewal attempts will be made.


This only works if letsencrypt_renewal_failure_notice_after_attempt is a value greater than 0,

and of course, if you have this in the directadmin.conf:



When a renewal failure notice is sent to the User (or whoever is setup to get it),

DA will append the following string to the end of the failure message:

"There have been %d renew failures, thus no more attempts will be made."

where %d is replaced with 5 (from letsencrypt_renewal_failure_notice_after_attempt).


This DOES also apply to the hostname certificate.

If it's renewal fails, DA will remove the hostname LE setup info specified in /usr/local/diretadmin/conf/*


This functionality was added to aid with Admins which have many Users/Domains, and where a User might move a domain to a some other server,

and the renewals previously needed to be disabled with the "Disable Auto-Renew" button.

This will automate removal of LE setup, so an expired and renew-failed cert (after 5 attempts), will no longer renew again.


Although it's mentioned that the disabling of the auto-renew happens at the same time the renew failure happened, if you've got your LE setup such that it does not message anyone,

this feature will still have an effect, and will still disable the auto-renew, even without the message being delivered.

Custom hook script: new

Called after the night LetsEncrypt renewal checks.

Environmental vars:


if 1, it means something was renewed and Apache needs to be reloaded.

Note that the tally will already restart after the nightly tally, so this action isn't needed UNLESS you specifically set:


in which case, you'd need something, like:

if [ "$renewed" = "1" ]; then
    echo "action=httpd&value=reload&affect_php_fpm=no" >> /usr/local/directadmin/data/task.queue
exit 0;

However, not restarting after a tally means that any suspended Users might not have their configs set correctly..

so we cannot really recommend restart_apache_after_tally=0 unless you have other checks, that trigger restarts of everything that needs a restart, via other means.

PHP path set in ~/.bash_profile new

Optional new directadmin.conf option, by default disabled, set to:


If you set it to 1:


Then anytime the php version is changed, DirectAdmin will try and set the PATH to contain that given /usr/local/php72/bin path.

There are restrictions allowing the feature to be enabled, but prevent DA from changing things.

The files that DirectAdmin will use are:

CentOS: ~/.bash_profile

Debian: ~/.bash_profile

FreeBSD: ~/.profile


Once enabled, when making a php version change, DA will check:

  • If the profile does not exist, DA will add the default:


export PATH

  • If it does exist, DA will go through each line, if PATH is found, it's replaced with the new value, and the export line is assumed to already be set.


Rules for new value:

If there is an existing PATH set in the profile, if the first value is not /usr/local/phpXX/bin, then DA will not touch it.

This is assumed to be a custom entry, and how Users can have it left alone.

If the existing PATH does start with /usr/local/phpXX/bin, then the XX values are updated to the new version, accordingly and all other variables should remain untouched.


The rule for profiles now also considers the default values of:


If either of these are present, DirectAdmin will insert the /usr/local/phpXX/bin path in front of them.

Set default items per page in tables new

Moved the hardcoded default number of rows in tables (50) into a directadmin.conf option:


which is the internal default.

You can set a different number of rows in the table by adding this setting to the directadmin.conf and using a new value.

Note that any skin which has a skin.conf, eg:


and has this value set, eg:


will override the directadmin.conf setting.

add_domain_to_domainips=2 for rDNS in /etc/virtual/helo_data new

New option for the add_domain_to_domainips variable:


When you set it to 2, the /etc/virtual/domainips is unaffected.

However, the /etc/virtual/helo_data file will use the rDNS for that given IP, instead of using the owned IP's user's main domain.

This means you can now use shared IPs in the helo_data file.

Once setting the directadmin.conf variable to 2, you can issue a rewrite to create a fresh file, if needed:

echo 'action=rewrite&value=helo_data' >> /usr/local/directadmin/data/task.queue

and confirm the changes in:


after the dataskq has run.

The benefit of this is that:

  1. Shared IPs can be use for the HELO value in smtp.

  2. You can more easily control the forward and reverse IP lookups which are added to the file.


The same action triggers addition of the domain to the file.

As such, the value is only added when a User is created on this IP, but now it sets the value no matter what.

If you change the rDNS, simply add another User for this IP, and it will update the entry in the helo_data file.

Priority order: lang.po, lang.json, custom/lang.po, custom/lang.json, etc new

The evolution skin would previously provide a .po file.

DirectAdmin would parse it into json, and send it to the skin.

In the future, .json file will be used, to save the need for parsing.

This feature auto-converts and saves .po files into .json files, so they don't need to be parsed again.

Assuming both files are more than 0 bytes, the newer timestamp wins.

If the .po file is newer than the .json, it's parsed, and the .json is overwritten.

If the .json is newer, then .json is used, and .po file not touched.

If there is no .json, one will be created from the .po.

If there is no .po but there is a .json, the .json is sent, no parsing, no timestamp checks.

Beyond that, you may want to have a custom translation.

We'll use nl.po as an example.

The priority order will be:





For each of those paths, the related lang.json or nl.json file is also used.

They're checked in pairs, so for example if lang/custom/nl.po is used, then lang/custom/nl.json created.

Either file can exist for the priority match to trigger.

As well, the login files nl/login.po, nl/login.json, login-nl.po, login-nl.json, can be used for all 4 of those aboves paths, custom or not.


Using Chrome's load time, and smashing F5 on:


the presence of both files, where the json is used and not parsed, yielded anywhere from 75ms - 80ms.

The presence of just the .po file, where the json needed to be created, took about 600ms, which includes the creation of the json file.

Just to compare, I disabled the write of the json file, to see how fast just the parsing is, and it was about the same at 595ms.. so the write is neglibile. The parsing is the pig.

Optimized the code to only sort the assembled data after it was all loaded into the container, and it dropped to 147ms for the parse, BUT this means it does not check for duplicates.

So the code now requires that your .po files not contain duplicates.. and if they do, they'll both be shown... so best to avoid this. The gain is significant enough that we'll keep this optimization of near 0.5s.

Of course, once the json is saved, the typical 75ms would resume for all subsequent requests, until a newer .po file comes along.

Widget .po translations new

Plugins which have widgets may want to offer translations for those widgets.

For example, if you were to call:


and you had files at:


where "/admin/" can be replaced with "/reseller/" or "/user/", as needed.

DA would include this under the "languages" index in the result, eg:

                ... translations ...
        "main": "<h3>main html</h3>"

If you want to override the nl.po, there are other listing options.

The same priority options are available to widget lang directory, the same as for skins:

Priority order: lang.po, lang.json, custom/lang.po, custom/lang.json, etc

DA just overrides the document root, so be the WGT dir instead

Also, it does not convert/save a json file for caching purposes.

The reason is that the return value is a sub-set of a container, so cannot just be dumped directly to the "nl" index in the same manner, else everything would be escaped, littered with \ etc.

Plugin Widgets: vue: main.js template.html (vue.html replaced) new

Plugin widgets, which live in a path, eg:


can now have 2 new files:

vue = main.js
template = template.html

and they'll show up in this call:


along with "main", and "languages" (if present)


vue.html has been replaced by main.js

and is no longer tokenized (template.html is also not tokenized)

Login page: /images or /assets now support custom override paths new

If you, for example, have a custom background.jpg, where the original lives at:

  • /assets:



  • /login_images:



You can now create a file in a "custom" directory beside that file, eg:


where the call to:


will check for the existence of the assets/custom/background.jpg automatically and use it, if it exists.

For paths with more levels thatn just within assets, say:


the custom path would be:


where custom is inserted at the last level, as it's added dynamically from the last slash.



  1. The original assets/background.jpg must exist in order for the custom file to be checked for.

  2. To use the Evolution login page, use this in your directadmin.conf:



www vs no www domain/pointer redirection (SKIN)(TEMPLATES) new

Relating to the previous feature here:

Domain Pointers: option to not redirect to www (SKINS)(TEMPLATES)

where it's mentioned it will be changed, this is that change.

Instead of just redirecting pointers, it will also apply to the main domain itself.

Naming will change from:


to now be one of:


where both the, and all pointers would be affected by it.

The previous change only applied to pointers.

The "force ssl" checkbox also plays into this setting.


Although it may seem that www_pointers=yes can convert to force_redirect=www, this would be false, as it affects domains too (not just pointers)

In testing, some domains had redirects in their .htaccess file, causing loops.

As a result, the auto-conversion has been disabled, so the old www_pointer feature is dropped, and the new feature is just off by default (not redirection happens)

Users can go to:

User Level -> Domain Setup -> -> Force Redirect

to enable it.

Pointers will also use this setting.

The old feature allows setting &www=no to the domains/ file, which will still be respected (overriding the domain's setting)

Use the current FORCE_SSL_REDIRECT tokens in the virtual_host2*.conf files:

Force www:

RewriteCond %{HTTP_HOST} !^www\\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} \[L,R=301\]

Force non-www:

RewriteCond %{HTTP_HOST} ^www\\.
RewriteRule ^ http://|DOMAIN|%{REQUEST_URI} \[L,R=301\]


follow the current "force ssl" rules.

Note: the RewriteRule would go to https instead of http, but only for VH:80, because force_ssl option only applies to VH:80.

The VH:443 doesn't need to force ssl.. since it's already ssl.



replace the www_pointer with:

<td class=list>www redirect</td>
<td class=list align=left>
<input type="radio" name="force_redirect" value='none' |FORCE_REDIRECT_NONE_CHECKED|> No redirection<br>
<input type="radio" name="force_redirect" value='www' |FORCE_REDIRECT_WWW_CHECKED|> www.|DOMAIN|<br>
<input type="radio" name="force_redirect" value='non-www' |FORCE_REDIRECT_NON_WWW_CHECKED|> |DOMAIN|
<td class=list>Redirection for domain, pointers,<br>and sub-domains.</td>





added |FORCE_SSL_REDIRECT| token.

Note that the 443 VirtualHosts will not fill the token with the force_ssl redirect, but will fill it with the www/non-www redirects.

For 80 VirtualHosts, the FORCE_SSL_REDRECT token is filled with both redirections for force_ssl/www/non-www redirects.

For any one given VirtualHost (eg:, there are 6 possible combinations using force_ssl, and the 3x force_redirect options.

12 if you include 80+443.

pointers redirects and aliases, are 24 more, totaling 36 combinations.

Subdomains, multiply that by 2 (subdomain or non-subdomain): 72 combinations.

Apache, nginx, openlitespeed, nginx_proxy, multiplies that by 4, so we have a grand total of 288 combinations of test cases.

We do have a test scripts, to show us all possible combinations, but it's unlikely all combinations will be actually tested (we'll do as much as is reasonable)

Please try out this new feature and let us know if you run into any issues.

dns_ttl=1 enabled by default new

New DirectAdmin installs will have dns_ttl=1 enabled by default.

FileManager: JSON: Check for multiple files/directories at the same time new

Related to:

Exists Check: CMD_FILE_MANAGER/some/file.txt?action=exists&json=yes

but now supports multiple checks in 1 call, as Evolution supports uploading multiple files in the same request.

Sample call:


Sample JSON output:

    "error": "1",
    "num_directories_exist": "1",
    "num_exists": "2",
    "num_files_exist": "1",
            "exists": "0"
            "exists": "1",
            "type": "directory"
            "exists": "1",
            "type": "file"
            "error": "Invalid path.  Must start with /"

Where the "error" is the number of times an error occurred during the checks. Can be any integer, 0 up to the number of errors (not always just "1")

num_directories_exist, num_files_exist, total count for the respective exists.

num_exists: num_directories_exist + num_files_exist

Restore requires a license new

The dataskq task.queue restores now require a license.

rspamd wildcard whitelist/blacklist fixed

Unlike exim and SpamAssassin, rspamd doesn't use * in wildcards for whitelist/blacklist settings.

Instead, it's a direct string match, so if you're wanting to blacklist:


the rspamd configs would actually need to use:

from = ""

and with this fix, DA will automatically hunt for the leading * character and remove it for the rspamd config (other configs are left unaltered with *


If you were trying to whitelist/blacklist based on other things like:


this fix does not work, as the * cannot be removed in a way such that it would work.

DATASKQ reload

Also, rspamd needed a reload after these changes, as it's not a realtime config file like SpamAssassin's user_prefs file.

The rpsamd user config needs a full reload of rspamd.

The reload was being called, but the dataskq didn't recognize rspamd as a valid service, so the reload was ignored.

LetsEncrypt: always restore fixed

When restoring User accounts on a new server, the Admin might not have set:


in the directadmin.conf yet, so the restore was previously not checking/restoring the LE data.

Changed behavior to always restore that data if present, regardless of the directadmin.conf letsencrypt=0|1 setting.

php_selector: restore selections fixed

With more common use of the php_selector, the selected php1_select=# value will now be restored in the domain's file.

All previous checks are done, meaning if there is only 1 installed php version, or something doesn't line up correctly, the entry is not transferred over to the file.

named_service_override: dataskq allow task.queue calls fixed

Relating to the feature added in 1.55.0:




bug where the dataskq didn't respect calls made to that service.

Real-time quotas: not showing for CMD_USER_STATS fixed

Although it was not part of the original design, the CMD_USER_STATS now shows the live quotas for the User.

LetsEncrypt: multi-domain subdomains wrong list fixed

When creating a multi-domain LetsEncrypt non-wildcard certificate, subdomains on 'other' domains end up getting the subdomain list from the current domain's subdomain list.

Full rewrite of code to assemble which items should be listed/selected, using cleaner syntax.

FileManager missing br between copy and reset fixed

Typo causing:


instead of:


thus showing:

Rename | CopyReset Owner | Recursively

instead of:

Rename | Copy

Reset Owner | Recursively

Was introduced with this feature:

Ability to shut off certain features of the File Manager

DNS Restore: subdomain: don't alter custom records fixed

If you've created a subdomain on your DA box.. but manually changed the A record to something external, DA will now respect that custom value during the restore.


Backup box:

Domain is set to

but custom sub A record manually changed

sub A

where is some external IP, not on this box.

Usually, it would make more sense to just delete the subdomain in DA to prevent this, and just add the remote record manually, but DA will handle it now anyway.

Issue was that after restoring to new box on, it would end up with 2 records:

sub A
sub A

DA was correctly respecting the custom IP first during the zone restore, but the domain's IP was being added when DA "restored" the subdomain (which creates the subdomain using the domain's IP).



  • If the User is being created during a restore (an not a merge)

  • and a subdomain record has a custom IP A/AAAA or CNAME value

  • and for A/AAAA, the value of that subdomain is not part of the local list of restored IPs ( is not in the list)

then add the DNS during subdomain creation, during a creation restore will not happen.

The original "sub A" will remain in the zone, and "sub A" will not be added.

Mail SNI: only allow subdomains of main domain, if cert is missing main domain fixed

Related to this change in 1.54.0:

Allow absence of root domain in dovecot SNI setup

The contents of a valid certificate can still be added to the /etc/virtual/snidomains, if the main is missing from the certificate, but only if a subdomain of that domain exists in the certificate.

As before, self-signed certificates are never allowed, into the snidomains file.

Php-FPM: suspended User needs to use creator's selected php version fixed

Say you have User "suspend" created by Reseller "creator".

Your CustmoBuild 2.0 options.conf might have:


Where "suspended" had selected php 5.6 as the primary php version for their domain.

Where "creator" has selected php 7.2 (or using the default) php version for their domain.

When User suspended is suspended, the DocumentRoot points to /home/creator/domains/suspended.

The bug was that the httpd.conf was trying to use "suspend"'s php version for the creator.sock, rather than whatever creator might have actually had available, eg:

/home/creator/domains/suspended = /usr/local/php56/sockets/creator.sock

where the logic would imply that the ~/domains/supsended DocumentRoot really should be using the creator's primary selected php version, eg:

/home/creator/domains/suspended = /usr/local/php72/sockets/creator.sock

DA bases the primary selected php version on any account's:


if present.

If no php selection overrides have been chosen, the interval selection is still reset to 0, so the User's overrides are not used.

Note, if "creator" has not created any domains, it wouldn't have a data/users/creator/php/php-fpm72.conf file.

For this reason, creators will now always have 1 php-fpmXX.conf for either their selected main domain, or the first php version (as specified in the php1_release from options.conf)

This is to ensure that the suspended User has some php .sock file to point to.

May require:

./build rewrite_confs

to create those missing creator's php72/sockets/creator.sock

Adding Additional IPs: duplicate existing values, not from default templates fixed

Adding additional IPs to a domain would previously duplicate the zone to the new IP using the dns_*.conf templates. This logic was not correct.

Instead, the feature will now go through all A/AAAA records which match the default IP of the domain, and duplicate those records to the newly added IP for the domain.

RoundCube: backup/restore: parse_ini_file INI_SCANNER_RAW fixed

The backup_roundcube.php and restore_roundcube.php files try to parse the mysql.conf using parse_ini_file.

However, newer installs can contain special characters which require the INI_SCANNER_RAW option to be passed to this function.

There is a fallback, in case that fails, so it would read the RC directly instead,

but the main method (preferred) would login to the mysql server to confirm if the da_roundcube database exists..

which also account for a possible remote host being used. (the fallback login method does not account for remote da_roundcube DBs.

Buffer overrun if named.conf line too long fixed

The code to read the named.conf files automatically expands buffers as needed.

The line buffer was doing this correctly, but when working with data from the line buffer, the associated temp buffer was not accordingly grown, causing the overrun during a copy.

Rspamd: directadmin-users.conf: read/write as root fixed

yum updates of rspamd resets the /etc/rspamd folder to be owned by root.

As a result, the read/write as "_rspamd" that DA uses no longer works.

Changed the read/write to run as root.

Last Updated: