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.
Official Documentation Reference
Section titled “Official Documentation Reference”- ISPConfig documentation: the vendor’s documentation index, covering installation and the update process.
- ISPConfig developer documentation: reference for the panel’s modules and concepts.
- MariaDB
ALTER USER: the statement that changes a database account’s password, and the privileges it requires. - MariaDB environment variables: documents
MYSQL_PWD, used below to keep a password out of the process list.
Prerequisites
Section titled “Prerequisites”- 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.
Which password this is
Section titled “Which password this is”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:
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:
grep -rl dbispconfig /etc /usr/local/ispconfig 2>/dev/null | grep -v '~$' | wc -lOn a Debian 12 host running web, mail and FTP that returns 25, made up of:
- twenty map files under
/etc/postfix/, ownedroot:postfixat mode0640, 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.phpand/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.
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.
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.
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:
printf "ALTER USER 'ispconfig'@'localhost' IDENTIFIED BY '%s';\n" "$NEWPW" | mysqlFLUSH 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.
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"doneThe 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:
stat -c '%A %U:%G %n' /usr/local/ispconfig/server/lib/config.inc.php \ /usr/local/ispconfig/interface/lib/config.inc.phpExpect -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:
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 donedoneAny 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:
php -l /usr/local/ispconfig/server/lib/config.inc.phpphp -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:
awk -F"'" '/ISPC_APP_VERSION/{print $2}' /usr/local/ispconfig/server/lib/config.inc.phpcurl -s https://www.ispconfig.org/downloads/ispconfig3_version.txtIf 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:
/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.
Step 7: Verify, before you disconnect
Section titled “Step 7: Verify, before you disconnect”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:
postmap -q yourdomain.com mysql:/etc/postfix/mysql-virtual_domains.cfdoveadm 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:
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 0journalctl -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 302tail -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:
grep -rl dbispconfig /etc | grep -v '~$' | xargs -r stat -c '%y %n' | sort | head -5These 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:
grep -rl dbispconfig /etc | grep '~$' | xargs -r shred -uThe 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:
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:
unset NEWPW OLDPWBe 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.
If you have already broken it by hand
Section titled “If you have already broken it by hand”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.
- Put the current working password into both keys of both files under
/usr/local/ispconfig, as in step 4. - 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.
- Run the updater from step 6 and answer
yesto 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.
Rollback
Section titled “Rollback”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:
if [ -z "$OLDPW" ]; then echo "OLDPW is empty. Refusing: this would remove the password entirely." >&2else printf "ALTER USER 'ispconfig'@'localhost' IDENTIFIED BY '%s';\n" "$OLDPW" | mysqlfiRolling 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.
Troubleshooting
Section titled “Troubleshooting”| 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 |
Related guides
Section titled “Related guides”- How to log in to the ISPConfig control panel
- Understanding ISPConfig user levels: admin, reseller, and client
- How to create a database and database user in ISPConfig
- How to back up your websites, databases and mailboxes in ISPConfig
- How to monitor website usage and logs in ISPConfig
Getting help
Section titled “Getting help”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.

