Skip to content

How to Rotate the ISPConfig Database Password

If the ISPConfig database password has been exposed, it needs to be replaced. There is a supported way to do that, and it is one command. There is also an obvious-looking way, editing ISPConfig’s two configuration files by hand, and it will take your mail server down while the control panel carries on working perfectly. That combination is what makes the mistake so easy to miss, and it is the reason this guide exists.

This is an article for administrators of self-managed servers. If your ISPConfig hosting is managed by Noiz, none of this is yours to run, and you should open a ticket instead.

Last reviewed: 15 August 2026, against ISPConfig 3.3.1p1, the version running on Noiz servers, on Debian 12. Every command below was run on a live ISPConfig host while writing this guide. This guide is written for Noiz hosting and is kept current against ISPConfig. It complements, and does not replace, the official ISPConfig documentation linked below.

  • Root shell access to the ISPConfig server. Every command here is run as root.
  • A single-server ISPConfig install. Step 1 checks this, and tells you to stop if it is not.
  • A password manager or vault ready to receive the new value before you start.
  • Plan for an interruption to email and FTP. From the moment step 3 runs until step 6 finishes restarting the services, no mailbox can be collected, no authenticated message can be sent and no FTP account can log in. Inbound mail is deferred with a temporary error and remote senders retry, so nothing is lost, but nobody can use email for the duration. That duration is the length of a full ISPConfig reinstall including a release download, not a few seconds. Pick a quiet window, and stay connected until the verification in step 7 passes.

ISPConfig holds three unrelated secrets that are easy to confuse. Changing the wrong one causes a different outage each time.

Secret Where it lives What breaks if it is wrong
Panel logins for admin and other users dbispconfig.sys_user, stored hashed Nobody can sign in to the web panel
The ispconfig@localhost database account Cleartext, in ISPConfig’s own config files and in every service config that reads the database The panel, the minute cron, email and FTP, depending on which copies you updated
The MySQL administrative account /usr/local/ispconfig/server/lib/mysql_clientdb.conf Creating and deleting customer databases fails

This guide covers the middle one only. Rotating it changes nobody’s panel login. To change a panel login instead, see how to change your ISPConfig password and interface language.

Leave mysql_clientdb.conf alone. Several community guides tell you to rewrite it as part of this job. They are wrong. It holds a different account entirely, the update in step 6 reads it to verify its own administrative connection, and rewriting it breaks customer database provisioning.

Why editing the two config files by hand breaks email

Section titled “Why editing the two config files by hand breaks email”

ISPConfig’s installer does not create a separate database account for each service. It creates exactly one, ispconfig@localhost, and writes that same username and password into every service that needs to read the ISPConfig database. Confirm it on your own server:

Terminal window
mysql -N -B -e "SELECT CONCAT(user,'@',host) FROM mysql.user WHERE user LIKE 'ispc%'"

One row comes back on a single-server install. Now count the files that hold its password:

Terminal window
grep -rl dbispconfig /etc /usr/local/ispconfig 2>/dev/null | grep -v '~$' | wc -l

On a Debian 12 host running web, mail and FTP that returns 25, made up of:

  • twenty map files under /etc/postfix/, owned root:postfix at mode 0640, which Postfix uses for every domain, mailbox, alias, transport and sender lookup it performs
  • /etc/dovecot/dovecot-sql.conf, which is how IMAP, POP3 and local delivery authenticate every mailbox
  • /etc/pure-ftpd/db/mysql.conf, which is every FTP login
  • /etc/vlogger-dbi.conf, used by the web server’s traffic log writer
  • ISPConfig’s own two files, /usr/local/ispconfig/server/lib/config.inc.php and /usr/local/ispconfig/interface/lib/config.inc.php

Change only the last two and the panel loads, the minute cron keeps reconciling, and every check an administrator normally runs comes back clean. Meanwhile this repeats in /var/log/mail.log every few seconds:

dovecot: auth-worker: Error: mysql(localhost): Connect failed to database (dbispconfig):
Access denied for user 'ispconfig'@'localhost' (using password: YES)

No mailbox can be collected, no authenticated message can be sent, and no FTP account can log in. Because the panel is healthy, an outage like this can run for hours before anyone connects the two events.

The fix is not to hunt those files down by hand. ISPConfig will rewrite all of them for you.

Step 1: Confirm this is a single-server install

Section titled “Step 1: Confirm this is a single-server install”

Both commands run as the database administrator over the local socket, so they work before anything changes.

Terminal window
mysql -N -B -e "SELECT COUNT(*) FROM dbispconfig.server"
mysql -N -B -e "SELECT CONCAT(user,'@',host) FROM mysql.user WHERE user LIKE 'ispcsrv%'"

Stop here if the first returns anything above 1, or if the second returns any rows. A multi-server install gives each slave its own ispcsrv<N> account and its own copy of the master credentials, and this procedure does not cover them. Rotate on the master with maintenance mode enabled, then work through each slave.

Step 2: Capture the current password before anything changes

Section titled “Step 2: Capture the current password before anything changes”

The rollback at the end of this guide depends entirely on this. Step 4 overwrites the value, and perl -pi leaves no backup file behind, so once you have run step 4 the old password is no longer readable from these files.

Terminal window
OLDPW=$(perl -ne "print \$1 if /^\s*\\\$conf\['db_password'\]\s*=\s*'([^']*)'/" \
/usr/local/ispconfig/server/lib/config.inc.php)
[ -n "$OLDPW" ] || echo "OLDPW is empty. Stop, and read the value out of the file by hand."

Do not echo $OLDPW. If the guard prints its message, open the file and copy the value into your password manager before going any further.

Step 3: Generate the new password and apply it in MySQL

Section titled “Step 3: Generate the new password and apply it in MySQL”

Use hexadecimal. It contains no character that needs escaping inside PHP single quotes, a shell, or the substitutions in step 4.

Terminal window
NEWPW=$(openssl rand -hex 24)

Apply it, passing the statement on standard input rather than as an argument. printf is a shell builtin, so the value never appears as a separate process in ps:

Terminal window
printf "ALTER USER 'ispconfig'@'localhost' IDENTIFIED BY '%s';\n" "$NEWPW" | mysql

FLUSH PRIVILEGES is not needed. Account management statements take effect immediately, and adding it here is a habit carried over from editing the grant tables directly.

The interruption starts now. The database account has a new password and nothing on the server knows it yet. Email and FTP authentication fail from this point until step 6 restarts the services.

Step 4: Write it into ISPConfig’s two configuration files

Section titled “Step 4: Write it into ISPConfig’s two configuration files”

Both files define two keys, not one:

$conf['db_password'] = '...';
$conf['dbmaster_password'] = '...';

On a single-server install they hold the same value. Updating only db_password leaves the panel able to read but not write, which presents as a panel where nothing you change ever takes effect.

Terminal window
for f in /usr/local/ispconfig/server/lib/config.inc.php \
/usr/local/ispconfig/interface/lib/config.inc.php; do
NEWPW="$NEWPW" perl -pi -e '
s/^(\s*\$conf\[\x27db_password\x27\]\s*=\s*).*$/$1\x27$ENV{NEWPW}\x27;/;
s/^(\s*\$conf\[\x27dbmaster_password\x27\]\s*=\s*).*$/$1\x27$ENV{NEWPW}\x27;/;
' "$f"
done

The value is passed in through the environment rather than on the command line, so it stays out of ps output and out of your shell history.

These substitutions match the exact line shape ISPConfig’s installer writes. If the file has been hand-edited at some point, for example into double-quoted array keys, they will match nothing, change nothing, and report success. That is precisely what step 5 exists to catch, so do not skip it.

On file ownership. Widely repeated advice says that in-place editing strips ownership and permissions, and that you must restore them afterwards. Tested on Debian 12 as root against a file owned ispconfig:ispconfig at mode 0600, both perl -i and sed -i preserved owner and mode exactly. The advice is not baseless, it is simply aimed at a different route: editing the file over SFTP as a non-root user, redirecting output over it from the wrong account, or copying a replacement in from elsewhere will all get it wrong. Checking costs nothing:

Terminal window
stat -c '%A %U:%G %n' /usr/local/ispconfig/server/lib/config.inc.php \
/usr/local/ispconfig/interface/lib/config.inc.php

Expect -rw------- root:root for the first and -rw------- ispconfig:ispconfig for the second.

Step 5: Prove the files hold the new password

Section titled “Step 5: Prove the files hold the new password”

This is the most important check in the guide, and it is the one that is easy to get wrong. Testing the database with $NEWPW only re-proves step 3. What matters is the value stored in the files, because that is what step 6 reads and copies into all twenty-five configurations. If step 4 silently matched nothing, and you verify with the shell variable, every check passes and step 6 then rewrites the whole server with the old password.

Read it back out of the files instead:

Terminal window
for f in /usr/local/ispconfig/server/lib/config.inc.php \
/usr/local/ispconfig/interface/lib/config.inc.php; do
for k in db_password dbmaster_password; do
v=$(perl -ne "print \$1 if /^\s*\\\$conf\['$k'\]\s*=\s*'([^']*)'/" "$f")
if [ "$v" = "$NEWPW" ]; then echo "ok $k $f"; else echo "STALE $k $f"; fi
done
done

Any line reading STALE means step 4 did not take. Stop, correct the file by hand, and run this again. Do not continue to step 6.

With all four lines reading ok, authenticate using the value taken from the file, over both transports, because the services do not all use the same one. The Postfix map files connect over TCP and the panel connects over the local socket:

Terminal window
php -l /usr/local/ispconfig/server/lib/config.inc.php
php -l /usr/local/ispconfig/interface/lib/config.inc.php
FILEPW=$(perl -ne "print \$1 if /^\s*\\\$conf\['db_password'\]\s*=\s*'([^']*)'/" \
/usr/local/ispconfig/server/lib/config.inc.php)
MYSQL_PWD="$FILEPW" mysql -u ispconfig -h localhost -N dbispconfig -e "SELECT COUNT(*) FROM server"
MYSQL_PWD="$FILEPW" mysql -u ispconfig -h 127.0.0.1 -N dbispconfig -e "SELECT COUNT(*) FROM server"

MYSQL_PWD keeps the value out of the process list, which -p<value> does not. It is still readable from the process environment by root, so treat it as an improvement on the command line rather than as a secure channel.

If any of those checks fail, fix that before continuing. Step 6 cannot run without a working connection.

Step 6: Let ISPConfig rewrite every service configuration

Section titled “Step 6: Let ISPConfig rewrite every service configuration”

This is the step the hand-editing approach misses. ISPConfig’s updater reads the password back out of /usr/local/ispconfig/server/lib/config.inc.php and regenerates every service configuration file from it, then restarts the services.

Check the version first. This matters more than it looks:

Terminal window
awk -F"'" '/ISPC_APP_VERSION/{print $2}' /usr/local/ispconfig/server/lib/config.inc.php
curl -s https://www.ispconfig.org/downloads/ispconfig3_version.txt

If those two differ, stop and treat that as a separate job. The command below does not pin a version. It fetches whatever release the vendor currently publishes, so when a newer one exists this stops being a reconfiguration and becomes a full version upgrade with schema changes, performed unattended, in the middle of a security task. Schedule that properly and read the vendor’s release notes first.

With the versions matching:

Terminal window
/usr/local/ispconfig/server/scripts/ispconfig_update.sh --force --update-source=stable

--force removes the updater’s refusal to run when no newer release is available, which is what lets you reinstall the version you are already on. --update-source=stable keeps you off the development branches, which must never be used on a live server.

It is interactive. Every default is correct for this job, so you can accept each one, but read them rather than holding down the return key:

Prompt Answer
Shall the script create a ISPConfig backup in /var/backup/ now? yes
Reconfigure Permissions in master database? no
Reconfigure Services? yes
ISPConfig Port leave as it is
Create new ISPConfig SSL certificate no. Answering yes starts a fresh certificate request, which is a separate job covered in how to secure the ISPConfig control panel with an SSL certificate
Reconfigure Crontab? yes

“Reconfigure Services?” is the one that matters. Answering no there completes the update and leaves email and FTP holding the old password, which is the outage this whole guide is about. Answering yes prints a line per subsystem as it regenerates each configuration, covering the mail server, the IMAP and POP3 server, the FTP server, the DNS server, the web server, the traffic log writer and the spam filtering components, and then restarts them.

Be aware that this regenerates service configuration from ISPConfig’s templates. Hand edits made directly to the files ISPConfig manages will be replaced. Settings held in the ISPConfig database, including per-site directives entered through the panel, are not affected.

Negative checks are not enough on their own. On a quiet server nothing may have tried to authenticate in the last few minutes, so an empty error log proves only that nobody tried. Start with tests that force the two lookups that were broken. Use a real mail domain from your own panel in place of the example:

Terminal window
postmap -q yourdomain.com mysql:/etc/postfix/mysql-virtual_domains.cf
doveadm user '*'

The first should return the domain. The second lists the mailboxes Dovecot can resolve. If the credential is still wrong, both print an explicit database error naming dbispconfig. Note that on a server with no mailboxes configured doveadm user '*' correctly prints nothing and exits 0, so read the absence of an error rather than the absence of output.

Then confirm nothing has failed since the services came back. Count from each service’s own restart time rather than a fixed window, because a fixed window still covers the outage you created between steps 3 and 6:

Terminal window
systemctl is-active postfix@- dovecot pure-ftpd-mysql apache2 mariadb
journalctl -u dovecot --since "$(systemctl show -p ActiveEnterTimestamp --value dovecot)" \
| grep -ci "access denied" || true # want 0
journalctl -u postfix@- --since "$(systemctl show -p ActiveEnterTimestamp --value postfix@-)" \
| grep -ci "access denied" || true # want 0
curl -sk -o /dev/null -w '%{http_code}\n' https://127.0.0.1:8080/ # want 302
tail -2 /var/log/ispconfig/cron.log # want a recent "finished server.php."

A 302 from the panel is correct: it is the redirect to the sign-in form. The cron log is the check most people skip, and it is the one that catches a half-applied rotation, because the minute cron is what writes your panel changes out to the server.

Finally, confirm every service file really was rewritten. Sort oldest first, because a file the updater missed is by definition one of the oldest:

Terminal window
grep -rl dbispconfig /etc | grep -v '~$' | xargs -r stat -c '%y %n' | sort | head -5

These are the five oldest files holding the credential, so this is where a missed one shows up. Every timestamp here should be from the minute the updater ran. An older timestamp is a file still holding the previous password, and the rotation is not finished.

Step 8: Clean up the copies of the old password

Section titled “Step 8: Clean up the copies of the old password”

Three things still hold the old value in cleartext, and the rotation is not finished until each is dealt with.

ISPConfig’s own backups of the files it replaced. It leaves one beside each configuration file it rewrites:

Terminal window
grep -rl dbispconfig /etc | grep '~$' | xargs -r shred -u

The backup the updater took in step 6. It is taken before anything changes, so its etc.tar.gz contains every service configuration with the old password in it. Identify it, keep it until you are satisfied the rotation held, then remove it by name rather than by glob:

Terminal window
ls -d /var/backup/ispconfig_*
rm -rf /var/backup/ispconfig_<the directory from above>

Your own notes. Move the new password into your password manager and clear both values from the shell:

Terminal window
unset NEWPW OLDPW

Be realistic about what deletion achieves. shred cannot be relied on to make a file unrecoverable on a journalling filesystem such as ext4, or on any copy-on-write or flash-backed storage, and anything already swept into a system backup still holds the old credential. If the old value was exposed through a support bundle, a screen share, a log, or an automated tool that read a configuration file, treat it as compromised everywhere it went, not only on this server.

The recovery is the same procedure, joined partway through. You do not need to work out which of the twenty-five files are stale, and you should not try to.

  1. Put the current working password into both keys of both files under /usr/local/ispconfig, as in step 4.
  2. Run the read-back check in step 5. If those files were hand-edited, the substitution in step 4 may match nothing, and step 5 is what tells you so.
  3. Run the updater from step 6 and answer yes to Reconfigure Services?.

That regenerates every service configuration from the working value, whatever state they were left in. Mail authentication recovers as soon as the services restart.

If verification fails and you want the previous state back, set the account to the value captured in step 2. Guard it, because an empty variable here does not fail, it removes the password from the account altogether:

Terminal window
if [ -z "$OLDPW" ]; then
echo "OLDPW is empty. Refusing: this would remove the password entirely." >&2
else
printf "ALTER USER 'ispconfig'@'localhost' IDENTIFIED BY '%s';\n" "$OLDPW" | mysql
fi

Rolling the account back without also restoring the files leaves you in the same split state the guide exists to prevent, so follow it with steps 4 to 6 using the old value.

If $OLDPW is gone, the old password is still inside the etc.tar.gz the updater wrote to /var/backup/ in step 6, provided you have not yet removed it in step 8.

Symptom Cause
Mailboxes cannot be collected and authenticated sending fails, but the panel is fine Step 6 was skipped, or Reconfigure Services? was answered no
FTP logins fail, email works As above, on a server where the FTP daemon was not restarted
The panel loads but nothing you change takes effect dbmaster_password was not updated, so the reconciler cannot write
The panel returns a blank page or a 500 The interface config file lost its ispconfig:ispconfig ownership, most often after being edited over SFTP
Everything breaks at once, immediately after step 6 Step 5 was skipped and the files still held the old password, so the updater propagated it
Customer database creation fails, everything else works mysql_clientdb.conf was rewritten. Restore it, it holds a different account
The updater exits saying no updates are available --force was omitted
The updater performed a version upgrade The version check at the start of step 6 was skipped
Everything works, then breaks after the next ISPConfig update A multi-server install, where the slave credentials were never rotated

Noiz manages this for you on managed ISPConfig plans, including the rotation itself and the verification afterwards. If you run a self-managed server and a rotation has left email or FTP broken, open a ticket with the output of step 7 and the last twenty lines of /var/log/mail.log, and it can be put right quickly.