I found that this was probably to the “sandbox mode” issue with mariadb - and after fighting to fix a ton of websites, I came up with this fix.
#!/usr/bin/env bash
HestiaCP MariaDB dump sandbox-header compatibility patch.
Applies to classic .tar and Restic database restores because both use
/usr/local/hestia/func/rebuild.sh::import_mysql_database.
Does not modify saved backup archives or live database contents.
set -euo pipefail
if [[ ${EUID} -ne 0 ]]; then
echo 'ERROR: Run this installer as root (sudo -i).' >&2
exit 1
fi
TARGET='/usr/local/hestia/func/rebuild.sh'
PATCH_COMMAND='/usr/local/sbin/hestia-sandbox-import-fix'
HOOK='/etc/hestiacp/hooks/post_install.sh'
if [[ ! -f "$TARGET" ]]; then
echo "ERROR: Missing Hestia helper: $TARGET" >&2
exit 1
fi
if ! command -v mariadb >/dev/null 2>&1; then
echo 'ERROR: This patch is intended for Hestia installations with a MariaDB client.' >&2
exit 1
fi
if ! mariadb --help 2>&1 | grep -F -- '--sandbox' >/dev/null; then
echo 'ERROR: Installed MariaDB client does not advertise --sandbox.' >&2
echo 'Upgrade the MariaDB client before applying this patch.' >&2
exit 1
fi
python3 - "$TARGET" <<'PY'
import os
import pathlib
import shutil
import subprocess
import sys
import tempfile
from datetime import datetime, timezone
file = pathlib.Path(sys.argv[1])
source = file.read_text(encoding='utf-8')
anchor = 'import_mysql_database() {'
ending = '\n# Import PostgreSQL dump'
marker = '# HESTIA_SANDBOX_HEADER_COMPAT_PATCH'
old_command = 'mariadb -h $HOST -u $USER -p$PASSWORD $DB < $1 > /dev/null 2>&1'
new_command = 'mariadb --sandbox -h $HOST -u $USER -p$PASSWORD $DB < "$1" > /dev/null 2>&1'
if source.count(anchor) != 1:
raise SystemExit('ERROR: Cannot safely find a unique import_mysql_database function. No changes made.')
start = source.index(anchor)
end = source.find(ending, start)
if end < 0:
raise SystemExit('ERROR: Hestia helper layout differs from expected. No changes made.')
body = source[start:end]
if marker not in body:
addon = r'''
HESTIA_SANDBOX_HEADER_COMPAT_PATCH
Only normalize the known MariaDB sandbox comment at line 1 of the temporary
extracted dump. The original backup archive is NOT changed. MariaDB's
--sandbox option below keeps the import sandboxed despite removing the line.
local hestia_sandbox_first_line
if [[ -f /usr/bin/mariadb && -f "$1" ]]; then
IFS= read -r hestia_sandbox_first_line < "$1" || true
case "$hestia_sandbox_first_line" in
'/*!999999- enable the sandbox mode */'|'/*M!999999- enable the sandbox mode */')
sed -i '1d' -- "$1" || return 1
;;
esac
fi
HESTIA_SANDBOX_HEADER_COMPAT_PATCH_END
'''
body = body.replace(anchor, anchor + addon, 1)
if old_command in body:
body = body.replace(old_command, new_command, 1)
elif new_command not in body:
raise SystemExit('ERROR: Hestia import command has changed. No changes made.')
updated = source[:start] + body + source[end:]
if updated != source:
backup_dir = pathlib.Path('/root/hestia-sandbox-fix-backups')
backup_dir.mkdir(mode=0o700, exist_ok=True)
backup_file = backup_dir / ('rebuild.sh.' + datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ'))
shutil.copy2(file, backup_file)
fd, filename = tempfile.mkstemp(prefix='.rebuild-sandbox-', dir=file.parent)
tmp = pathlib.Path(filename)
try:
with os.fdopen(fd, 'w', encoding='utf-8') as handle:
handle.write(updated)
stat = file.stat()
os.chmod(tmp, stat.st_mode)
os.chown(tmp, stat.st_uid, stat.st_gid)
subprocess.run(['bash', '-n', str(tmp)], check=True)
os.replace(tmp, file)
finally:
tmp.unlink(missing_ok=True)
print(f'Patched {file}; original saved at {backup_file}')
else:
print('Hestia import helper is already patched; no changes needed.')
PY
Persist the compatibility patch across HestiaCP updates without replacing
any existing contents of the supported post-install hook.
mkdir -p /etc/hestiacp/hooks
if [[ ! -f "$HOOK" ]]; then
printf '#!/usr/bin/env bash\n' > "$HOOK"
fi
if ! grep -Fq 'hestia-sandbox-import-fix' "$HOOK"; then
cat >> "$HOOK" <<'HOOK_ADDENDUM'
Restore MariaDB sandbox header compatibility fix after HestiaCP upgrades.
if [ -x /usr/local/sbin/hestia-sandbox-import-fix ]; then
/usr/local/sbin/hestia-sandbox-import-fix >> /var/log/hestia-sandbox-import-fix.log 2>&1
|| echo 'WARNING: Hestia sandbox compatibility patch needs review.' >&2
fi
HOOK_ADDENDUM
fi
chmod +x "$HOOK"
bash -n "$HOOK"
echo 'Done. Verify with: bash -n /usr/local/hestia/func/rebuild.sh'
echo 'No backup archive, database, or SQL dump has been restored or imported.'
Save that in /root as hestia-sandbox-import-fix.sh
I sudo into root so I don’t have to keep running sudo, so these commands are all done as root.
Run:
install -m 700 \
/root/hestia_sandbox_import_fix.sh \
/usr/local/sbin/hestia-sandbox-import-fix
Then execute:
/usr/local/sbin/hestia-sandbox-import-fix
The installer checks that your MariaDB client supports the required sandbox option before modifying HestiaCP.
3. Verify the patch
Run:
bash -n /usr/local/hestia/func/rebuild.sh
There should be no output if the modified script passes Bash syntax validation.
Then run:
grep -n 'HESTIA_SANDBOX_HEADER_COMPAT_PATCH' \
/usr/local/hestia/func/rebuild.sh
You should see the patch’s beginning and ending markers.
No MariaDB or HestiaCP restart is required.
4. Protection against HestiaCP updates
The installer also creates or updates:
/etc/hestiacp/hooks/post_install.sh
HestiaCP officially supports post-install hooks for reapplying customizations after updates.
The hook will rerun the patch installer after an update. If HestiaCP changes its import code substantially, the patcher will stop rather than modify an unrecognized function.
So anyone dealing with the dreaded
/*!999999- enable the sandbox mode */
This will fix that. I had to reinstall 90 sites on a server and almost lost my mind when none of the databases installed. I know the HestiaCP team has replied to other posts that it is not their fault, it is mariadb - but this is a simple fix for everyone, and actually should be included in core.
Enjoy!
Chris