Version 1.44.0

Released: 2013-09-26

Inodes (SKINS) new

Count and display the inode limits for Users.

Can be set in packages.

An inode is a file or folder, so setting up a limit will prevent the User from having too many files/folders in their account.

Default for packages/Reseller/Users is unlimited.

directadmin.conf options set as default:


The inodes will use the hard limit multiplier, just like the disk usage.

Meaning, the value you set will be the soft limit, and the hard limit will be 1.1x that value.


changes in:


For each, basically just copy your quota line, and swap "quota" with "inode", while maintaining the case and variable name formatting.

Add innodb_file_per_table to /etc/my.cnf for new intalls new

The new default /etc/my.cnf will also have:


in the [mysqld] section to break up the innodb ibdata files into per-database files.

Code is in the

Will not affect existing installs, it is only done for fresh installs.

file check before ftp_upload.php new

Extra check added before ftp_upload.php to ensure the tar.gz exists and is greater than 0 bytes.

If not, an error is reported.

Will help rule out if a file on the backup server is 0 bytes due to a 0 byte file being uploaded, or the ftp server having the issues.

Related debug guide:

CMD_API_POP to include per-email sent/limit if enabled new

If this option is enabled:


Per-Email send limit (SKINS)

Then this API: CMD_API_POP with type=quota

API for email quotas

will also include "sent" and "limit"

SPF type dns records (SKINS)(TEMPLATES) new


RFC 7208 - SPF DNS RR is now deprecated.

Using dns_spf=1 is not recommended as it would likely not be checked anyway.

DirectAdmin will continue to add the SPF records to the TXT RR, so no a major loss.


Previously spf records were added within a TXT record.

This change allows actual SPF dns records to be used.

The catch is, it requires that the version of bind (named) you're using to support it.

We've not yet found a simple/reliable check for this, so this feature is off by default.

Internal default is:


To enable the feature, add:


to the directadmin.conf and restart DA.

Also, if you're using the Multi-Server Setup (MSS), it will copy the raw zone file to the remote boxes.

Ensure that the remote boxes also support the SPF record type, or named will crash.



dns_spf.conf: which is an exact copy of the dns_txt.conf.


named.db: added tokens for SPF records.



|*if DNS_SPF="yes"|
<form action='/CMD_DNS_ADMIN' method='POST'>
<input type=hidden name=action value=add>
<input type=hidden name=domain value="|DOMAIN|">
<input type=hidden name=type value="SPF">
<tr><td class=list2><input type=text name=name value=""></td><td class=list2 align=center>SPF</td><td class=list2><input type=text name=value size=26 ></td>
<td class=list2 align=center><input type=submit name=add value="|LANG_ADD|" ></td></tr>


remove the |*endif| at the end of the USERDNSCONTROL section, and replace it with this:


|*if DNS_SPF="yes"|
<form action='/CMD_DNS_CONTROL' method='POST'>
<input type=hidden name=action value=add>
<input type=hidden name=domain value="|domain|">
<input type=hidden name=ptr_val value="|ptr_val|">
<input type=hidden name=type value="SPF">
<tr><td class=list2><input type=text name=name |DNS_DISABLED|></td><td class=list2 align=center>SPF</td><td class=list2><input type=text name=value size=26 |DNS_DISABLED|></td>
<td class=list2 align=center><input type=submit name=add value="|LANG_ADD|" |DNS_DISABLED|></td></tr>


Related error message from /var/log/messages:

found SPF/TXT record but no SPF/SPF record found, add matching type SPF record

turn on dns_spf=1, so that new zones have the "SPF" record added.

Added SESSION_ID env variable to and new

The session ID variable is now available from the environment in both the and scripts.

Note that API calls (with http auth basic) will make the value blank, so don't assume it will be set.


Security: Changes to mysql restore process new

Slightly changed how the mysql restore works to improve security.

Thanks to for this report.

Security: improvements to strict_backup_permissions - MAJOR CHANGES new

Related features:

Pre-backup check for hardlinks to resolve backup errors

backup/restore apache owned files

These changes apply when the apache owned files list is enabled (which it is by default)

and when the strict_backup_permissions are enabled.

The new internal default will now be:


for better security.

You can set it to 0 if you need to go back to the old backup method, but adding the following to your directadmin.conf:


Previous issues with the strict_backup_permissions feature were that it could not read apache owned files or folders if they were not chmod to 644 or 755.

For these reasons, the strict_backup_permissions option was not made default.

Changes to this feature will go through all data in:


and will do check to see which ones the username (DA account) cannot read.

A second data list will be created:


DA passes this file to tar with:

--exclude-from backup/non_readable_files.list

so that the files are not included, thus won't throw an error during the backup.

DA then copies these files to a new data location:


which sits next to "backup" and "domains" at the top level.

note that paths in non_readable_files.list have domains/ as a prefix, so that the tar option:

--exclude-from FILE

doesn't match the files DA manually copied... else DA's files in backup/non_readable_files would also be excluded from the tar.gz.

If any file is chmod to 0, when the file is copied, it will be set to 600 (directories to 700).. as the backup needs this as a minimum to read the file as a non-root backup.

The restore will not reset these files/folders to chmod 0. They'll be left as 600 (700 for dirs).

Since this feature copies files to a 2nd location before backup, significant amounts of disk usage will be used if the files being backed up (eg: apache owned files) are not readable by the User.

You can avoid the overhead from this feature by setting your apache owned files to 644, so tar can read them and they won't get copied.

Other cleaner options are to use suPhp or mod_ruid2 (or equivalent) so that you don't have any files owned by apache (as apache owned files really just makes a mess of things)

All of these extra steps are run if this option is enabled.

This is the internal default (enabled):


it can be disabled by adding it to the directadmin.conf and setting it to 0:


This feature will only be used if the following settings are enabled:


This related option will use the same tree parsing:


so either add_non_readable_files_to_strict_backup or backup_apache_files_list will cause a full /home/user/domains directory traversal.

Credit to for reporting the issues with the backup system, and need for improvements to this feature.

Changes and new tokens in user_limit.txt new

Relating to the new inode feature, a new token is available in the user_limit.txt template.

The token will be:


where REASON will be set to one of the above 3 values.

This allows up to customize the output based on the trigger of the notification.

Similar to quota and bandwidth, these tokens will also be available:


Major changes have been done to the user_limit.txt to make it more clear as to what the message is for.

Old or custom templates will still work... but may be confusing if no inode variables are present (inode limit could trigger the message, without any reference to inodes) version 14 new


is now available.

Fixed a minor bug with conditions, eg:

  • limit is 200

  • the count is at 200

  • retries don't increase the count, but do send warnings

when exim processed a batch of emails to retry, it caused a large number of noticed to be sent in DA:

Warning: 200 emails have just been sent by usenrame

all at once.

The version 14 fix is to not send notifications on retries, since they're not counted.

This prevents the pile of notifications.

Syntax Check with custom httpd config (SKINS) new

Syntax check will be displayed at the bottom of the page:

Admin Level -> Custom Httpd Config ->

booscript called:


service httpd configtest

/etc/init.d/httpd configtest

/usr/local/etc/rc.d/httpd configtest

nginx, all OSs:

/usr/sbin/nginx -t -c /etc/nginx/nginx.conf


Near the top, just before the custom entry textarea table:

|*if CONFIG_ERROR="1"|
<br><br><b>Configuration Syntax Error - See below for details</b><br><br><br>

Near the bottom, after the httpd.conf display:

<table class=list>
<td class=listtitle>
Configuration Check
<td class=list align=center>
<textarea cols=120 rows=5 wrap=off readonly>|CONFIG_TEST|</textarea>

Tokenizer newline reducer new

As the templates get more if-then-else statements, many left-over newlines remain behind, leaving many blank spaces in the httpd.conf files.

Since these newlines are not helping anything, and will actually slow down the apache restart process (bigger files == more to read in), a reducing algorithm was added for the User httpd.conf files.

It will remove any repeated \\ characters, along with any spaces or tabs in between two \ characters.

Subdomain MX records (SKINS) (API) new

Ability to specify a subdomain for an MX name.

Enbaled by default, set internally:


to disable it, set it to 0 in the directadmin.conf and restart DA.

This will add a 3rd variable to the skins called mx_value, for the actual smtp server field.

This also means that the "name" is going to be the domain part on the left, as it should have been originally, instead of the smtp server, like before.

The value remains the number.

If no mx_value is passed, DA will notice and assume it's an old skin, so the "name" would be used as the smtp server (right part after the number).


Retrieval and submissions will remain in the old method, fully backwards compatible.. where the "name" is the smtp server, and the "value" is only the number.

However, if you include:


in your requests, then DA will give you the "name" as the domain/subdomain (left) and the value will be the combined "10", with the number and domain together.

For deletion, you can delete MX records with either the old or new format, DA will figure it out for you.

Clearly, if the value is just a number, you're using the old method and DA will handle is accordingly.


the MX variable will still hold the older mail=10 format, so it's backwards compatible.

However, a new variable will show up full_mx_records is on:


which will old mail


echo $MX_FULL

gives you, eg: mail&


echo $MX

gives you:



The MX name now looks like:

<input type=text name=name |*if HAVE_FULL_MX_RECORDS="1"|value='|DOMAIN|.'|*endif|>

Immediately after the select box, add:

<input type=text name=mx_value size=18>


Getting things into this one is ugly, because the form is already within an |*if| and it doesn't support double embedded ifs.. as such, we need this:

|?FULL_MX_VALUE=<input type=text name=mx_value size=18>|

and then make the MX name:

<input type=text name=name value="|FULL_MX_NAME|" |DNS_DISABLED|>

and right after the </select> add:



changes similar to dns_admin_control.html

per-User php-mail.log (TEMPLATES) new

Php will now log all sends from mail() to:


Internal default:


to disable it, set:


in the directadmin.conf, restart DA, and issue a:

./build rewrite_confs

The log will be rotated by the tally.

The number of logs is the same as for apache, set in:

Admin Level -> Admin Settings -> Number of logs to keep

This log is a per-User log because the php-fpm.conf does not have a domain configured in it.. it's per-user.

If you're using suPhp, you'd need to manually add the per-user php.ini:

mail.log = /home/username/.php/php-mail.log

CLI (mod_php), fastcgi and php-fpm will all set this up automatically via other means.

User creation, IP no longer needed to be known new

This is mainly for the call:


but will work fine however passed to DA..

When creating a User, previously the ip= value needed to be known.

This change allows an alternate set of values to be used:


where you can specify any of those 3 values if you don't feel like doing an API call to get the list of IPs before creating the account.

If any of those 3 values are used for the ip variable (shared, sever or assign), DA will go hunting for an IP to use that matches that status (ip=assign will actually look for an IP of status=free)

This will save the need to do a pre-lookup for the IPs before creating a User.

If an IP of the desired type does not exist, DA will fallback to a shared IP, if it exists in the ip.list.

If not, it will fallback to the server IP, if it exists in the ip.list.

If the ip.list is competely empty, you'll get a creation error. new

Script to be called after the backup of each User only if the backup succeeded.

Any failures in the process will prevent this script from being called.

Same variables as for and

Ability to specify https for webapps scripts (SKINS) new

Some older browsers ignored the difference between port 2222 and 443, which caused issues where the browser would assume the same cert/key were used, when they were not (a reload would be required), so in the past, we got around this browser issue by forcing the webapps scripts (squirrelmail, phpmyadmin, roundcube) to use only http://, hardcoded in the skins.

This change will add a new directadmin.conf variable (internal default)


where you can set it to 1 in the directadmin.conf, restart DA, and it will put https:// in front of those webapps links.

If it's found that this option can be set to 1 all the time, and 99% of browsers are not affected by the issue anymore, then we'll set this to 1 by default in a future release.






for any location that has http:// pointing to a local webapps script.

This does not apply to the help button, (, so leave that one alone.

Links to phpMyAdmin are already set to use |HTTP| which is controlled by SSL=1|0 in the directadmin.conf, so we will leave that alone.

CMD_API_POP? for full output new

New option for CMD_API_POP which basically dumps out the same thing that's in the E-Mail Accounts table (less the System Account)

Each virtual user entry will have the following variables:

sent:   number of emails sent.
limit:   limit that can be sent. Will not be present if user_can_set_email_limit=1 is not set: [Per-Email send limit (SKINS)](/changelog/version-1.42.0.html#per-email-send-limit-skins)
usage: current disk usage, in MB. Will not be present if count_pop_usage=1 is not set.
quota:  current quota limit, in MB.
suspended: yes|no. This is based on the ! character in front of the password crypt in /etc/virtual/

Ability to add multiple access_host values upon DB creation new

Related to the existing access_host feature for the conf/mysql.conf file:

option in mysql.conf for default access_host for remote mysql servers

this new option allows you to add more access_hosts to the mysql.conf file.

You can do this by adding new lines with a numbered name, eg:


you can add as many lines as you'd like.

where the following are required:

  • the host= value must be set to some value other than localhost. (usually the IP of a remote box), as per id=818 (can be host= if needed)

  • the first access_host= entry must already exist. If you don't have the first one, then the numbered values won't be loaded.

  • the number you use after the access_hostX= value doesn't matter.. it can be any number, any order, out of order, eg: access_host76345834= is allowed.

Any database created with these values set will have the listed access_hosts added to their database (eg,,,

This also applies to the restores of the databases, even if the backed up tar.gz DBs do not not have any access_hosts listed in the backup.

mysql.conf to support "port" new

You can now put:


in the file:


to have DA use some other port for database connections.

Set new domain pointers as non-local for mail to match main domain fixed

Continuation of this fix:

new domain pointer to mail local mail settings

Where, a newly added pointer should not be local, if the domain it's on is not local.

The id=1086 would only remove the pointer from the domains file when the main domain was changed to be non-local.

Workaround until this fix is available is to go to:

User Level -> MX Records

and make the main domain local, then not local.

This will re-sync the values in the /etc/virtual/domains file for the pointers.

nginx check for secure_access_group fixed

When checking if the secure_access_group exists, also check to see if nginx is in that group.

If not, add it.

Wrong language for some internal text files fixed

The "internal" text files for language packs are loaded in with the class they belong to, at the time that class comes into existence.

For some classes, these language files are loaded in before the lang= setting from the user.conf is loaded, thus they're set to "en" when the user may be set to something else.

The affected language files are:


It will check to see if the user lang does not match the intenal lang, and will reload all lang files which are already loaded.

The fix will handle any lang files, if they were missed or get loaded ahead of the user.conf in any future DA cod changes.

Restore with add_userdb_quota option using Bytes instead of Meg fixed

Relating to this newly added feature:

add_userdb_quota for dovecot quotas

The backups always store bytes, as per the quota files:


this quota file is used during the restore to set the M value in the passwd file, if add_userdb_quota=1 is set.

The bug was the the value was not divided by (1024*1024), thus the value was much larger than intended.

Changed ftp_upload.php ncftpput to have longer timeout: "data transfer timed out" fixed

In the ftp_upload.php, it was previous set to use:

-t 25

I've changed this to be:

-t 120

as we were seeing a few backup errors, similar to this:

User user has been backed up. <5:00:10>
Remote write timed out after 98304 bytes had been sent.
Could not read reply from control connection -- timed out.
ncftpput /home/tmp/admin/user.admin.user.tar.gz: data transfer timed out.

I wasn't able to completely confirm that this was the cause, but the backup output of 5:00:10 vs 5:00:42 would imply that the timeout happened very shortly after the 25 second timeout was hit.. so for now, we can assume that the 25 seconds was the issue.

If this is true, then it may be an ncftpput bug, as if the timeout is proceeding, one would assume that the timeout should no longer apply..

Perhaps the transfer has stalled, and the timeout gets hit.. in which case it's not a bug, and increasing to 2 minutes should help out.

php-fpm not restarting with nginx fixed

Fixed the dataskq to issue a stop to php-fpm if either httpd or nginx are stopped, else php-fpm will be gracefully restarted for all other httpd/nginx cases.

httpd was already handled.. this fix simply invited nginx to the party.

Error inserting host: Field 'ssl_cipher' doesn't have a default value fixed

When adding an access host, this new error appear for new versions of MySQL.

Fix was to specify these 3 values as '' when adding access hosts to the mysql.user table.


Named::readDB: Can't open /var/named/.db for reading fixed

Caused by a "for" loop going one step too far, caused by <= instead of <.

Caused segfaults in the dataskq in some cases, but the memory corruption ended up in a manner where DA didn't log the segfault in the errortaskq.log.

Change default connect_timeout from 5 to 10 seconds fixed

If anyone is getting the error:

Your connection has timed out Details: No input given before connect_timeout reached

It means that you connection to DirectAdmin is slower than expected where the initial request headers have not yet come in, after the connection was made.

The initial headers after the connection shouldn't take more than few seconds.. but reports indicate that the 5 second default might be too low for some people.

The default has been changed to 10 seconds.

If you want to increase it, add:


in the directadmin.conf and restart DA, to set it to a higher timeout (20 seconds, in that example)

da-popb4smtp recording negative bytes for large dovecot logging fixed

If a User is connected for a long time, or simply downloads a very large amount of bandwidth in a given session, the da-popb4smtp may record a negative number if the byte are too large for the given number type.

Sample log entry:

Aug 17 01:56:02 box dovecot[3026]: imap( Disconnected: Disconnected in IDLE in=759446 out=2347246624

Which would end up logging the following in the log:



note the negative sign.

The value on the left was an "unsigned long", which wasn't large enough to hold inbound+outbound, thus flipped negative.

Solution: unsigned long long so it can hold a larger value.

Check group on new User creation fixed

DA will now check the group list for a requested username, instead of just the user list.

This will block the creation of a User if group already exists with that name.

Default directadmin.conf option:


This also applies when external boxes call:


If box A has this option off, box B has the option on, and A checks B if the user exists, B will check the group.

It's up to the individual box to decide if the group should be checked.

Linked-ips not adding DNS for pointers and subdomains fixed

Adding a linked IP to existing domain wasn't adding the IP to the dns for domain pointers at all, and also wasn't adding the IP to subdomains.

Oversized disk usage limits caused negative hard limit fixed

If a 2TB limit is assigned, DA needs to convert that from MB to KB (system quotas work in KBs)

1024 x 2000000 (we'll use 2000000 to keep it simple)

And then above that, we need to set the hard limit as the 1.1 multiplier to the soft (real) value.

1024 x 2000000 x 1.1 = 2252800000

This is a bug number, larger than an "integer", thus it flips to be negative.

The fix was to use an unsigned long long, which will be plenty large. to update linked ips fixed

The linked-ip feature set a list of IPs to be linked against a given IP.

The has been updated to make a task.queue.cb call to swap the IPs in the IP setup file.

We had to pass the data to the dataskq and get it to do it, because the url encoded data is not perl regex friendly.

The call is:

echo "action=ipswap&value=linked_ips&old=$OLD_IP&new=$NEW_IP" >> /usr/local/directadmin/data/task.queue.cb

/usr/local/directadmin/dataskq d100 --custombuild

Force all subdomains to lower case fixed

Subdomain creation previousl allowed

Fix is automatic forcing of full lower case.

Last Updated: