How to Update a MySQL User Password Non-Interactively (Scripts & Automation)

Most guides show you how to change a MySQL password by typing commands into an interactive prompt. That works fine when you are sitting at a terminal. It falls apart the moment you need to update a user password in MySQL from a script — inside a deploy pipeline, a cron-driven rotation job, or a configuration-management run where no human is present to type anything.

This guide is about the non-interactive path: running `ALTER USER` programmatically, the real security risks of passwords on the command line, and the safer credential-handling patterns that keep secrets out of process lists, shell history, and source control.

Key Takeaways
• To update a MySQL user password from a script, run `ALTER USER ‘user’@’host’ IDENTIFIED BY ‘newpass’;` non-interactively with `mysql -e`.
Never put the connecting password directly in the command — it leaks through `ps` and shell history. Use a `0600` `~/.my.cnf` or `–login-path` instead.
• Read the *new* password from a secrets manager or environment variable, never a hard-coded literal.
• `ALTER USER` does not require `FLUSH PRIVILEGES`; the change applies immediately.
• Make rotation scripts idempotent so re-runs are safe in CI/CD and config management.

How do you update a MySQL user password from a script?

The modern, supported statement for changing any account’s password is `ALTER USER`. To run it without an interactive prompt, pass it to the client with the `-e` (execute) flag:

“`bash mysql -u root -p -e “ALTER USER ‘appuser’@’localhost’ IDENTIFIED BY ‘N3w-Str0ng-Pass’;” “`

The `-p` with no value still prompts for the *connecting* user’s password, so this is only “semi” non-interactive. For full automation you must remove that prompt too — covered below in the security section.

A few important notes:

  • `ALTER USER` is the canonical method on MySQL 5.7.6+ and MariaDB 10.2+. The old `SET PASSWORD` and `UPDATE mysql.user` approaches are deprecated or fragile, and directly editing the grant tables means you *would* need `FLUSH PRIVILEGES`. With `ALTER USER`, the change takes effect immediately — no `FLUSH PRIVILEGES` required.
  • The `’host’` part matters. `’appuser’@’localhost’` and `’appuser’@’%’` are *different accounts*. Your script must target the exact host pattern the account was created with, or you will silently update the wrong account (or none).

This post is the automation-focused member of a small family of guides. If you want the interactive walkthroughs instead, see the related posts on , , and .

Why is putting the password on the command line dangerous?

This is the single most important thing to understand about scripting database password changes.

When you run:

“`bash mysql -u root -pSuperSecret -e “ALTER USER ‘appuser’@’localhost’ IDENTIFIED BY ‘newpass’;” “`

two separate leaks happen:

  1. Process list exposure. While the command runs, *any* user on the machine can see the full command — including `-pSuperSecret` — by running `ps aux` or reading `/proc`. The MySQL client even prints a warning about this for a reason.
  2. Shell history. The command, secret and all, is written to `~/.bash_history` (or your shell’s equivalent) in plaintext, where it persists long after the process exits.

So the password you typed to *authenticate* leaks. And in a naive rotation script, the *new* password sits inside the SQL string, leaking the same way.

The rule that prevents most database-credential leaks in automation: never put a database password directly in a command-line script. It will leak via `ps` and shell history regardless of how careful the rest of your code is. Instead, authenticate using a `–login-path` created with `mysql_config_editor`, or a `~/.my.cnf` file locked to `chmod 600`, so the connecting password never appears in a process list or history file at all. This one habit eliminates an entire class of accidental exposure that no amount of “be careful” can.

What are the safer ways to authenticate non-interactively?

You have two battle-tested options for letting a script connect *without* a plaintext password in the command.

Option 1: A locked-down `~/.my.cnf`

The MySQL client reads connection defaults from `~/.my.cnf`. Put credentials there once, protect the file, and your commands never carry a password:

“`bash cat > ~/.my.cnf <<'EOF' [client] user=root password=SuperSecret EOF chmod 600 ~/.my.cnf ```

Now the script is clean:

“`bash mysql -e “ALTER USER ‘appuser’@’localhost’ IDENTIFIED BY ‘newpass’;” “`

No `-u`, no `-p`, nothing sensitive in `ps` or history. The `chmod 600` is non-negotiable — it restricts the file to the owning user. The password is still plaintext *on disk*, so this file’s permissions are your security boundary.

Option 2: `mysql_config_editor` and `–login-path`

MySQL ships a tool that stores credentials in an encrypted (obfuscated) file, `~/.mylogin.cnf`, removing even the plaintext-on-disk problem:

“`bash mysql_config_editor set –login-path=admin –host=localhost –user=root –password

“`

After that one-time setup, scripts authenticate by name:

“`bash mysql –login-path=admin -e “ALTER USER ‘appuser’@’localhost’ IDENTIFIED BY ‘newpass’;” “`

This is the cleanest pattern for automation: no password in the command, no plaintext password in history, and no readable plaintext on disk. (Note: `mysql_config_editor` is a MySQL tool; MariaDB users typically rely on the `0600` `~/.my.cnf` approach.)

How do these methods compare?

Method Connecting password in `ps`? In shell history? Plaintext on disk? Best for
`-pPASS` inline Yes (leaks) Yes (leaks) No Never — avoid
`-p` (prompt) No No No Manual, one-off runs
`~/.my.cnf` (chmod 600) No No Yes (file-protected) Scripts, cron, single host
`mysql_config_editor` / `–login-path` No No No (obfuscated) Automation, CI agents, MySQL
Secrets manager + env var No No No CI/CD, dynamic credentials

The pattern is clear: anything that keeps the connecting password out of both `ps` and shell history is acceptable; the inline `-pPASS` form is the only one that fails on both counts.

Where does the *new* password come from?

Authenticating safely is half the job. The other half is sourcing the new password without hard-coding it.

Hard-coding `IDENTIFIED BY ‘literal’` in a committed script is the same leak in a different place — now it lives in your Git history forever. Instead, inject it at runtime:

“`bash

NEW_PASS=”$(vault kv get -field=password secret/db/appuser)”

mysql –login-path=admin \ -e “ALTER USER ‘appuser’@’localhost’ IDENTIFIED BY ‘${NEW_PASS}’;”

unset NEW_PASS “`

Key points:

  • Read from a secrets manager or injected environment variable (a vault, cloud secret store, or CI secret), never a literal.
  • Beware that even `NEW_PASS` as an environment variable can be visible in `/proc//environ` to privileged users; on shared machines, prefer feeding the statement via stdin or a transient file with tight permissions.
  • Quote and escape carefully. Passwords with `$`, backticks, or quotes can break the SQL or trigger shell expansion. Generate rotation passwords from a safe character set, or pass them through a here-document to avoid surprises.

How do you automate password rotation safely?

A rotation routine generally does three things: generate a new secret, apply it with `ALTER USER`, and store it where the application can retrieve it. The ordering and idempotency matter.

“`bash

#!/usr/bin/env bash set -euo pipefail

USER_NAME=”appuser” HOST=”localhost”

NEW_PASS=”$(openssl rand -base64 24)”

vault kv put secret/db/appuser password=”$NEW_PASS”

mysql –login-path=admin \ -e “ALTER USER ‘${USER_NAME}’@’${HOST}’ IDENTIFIED BY ‘${NEW_PASS}’;”

unset NEW_PASS “`

Design notes for production rotation:

  • Store before you switch. Write the new secret to your secrets store *before* applying it to MySQL. If the database change fails, the app can still be reconfigured; if you switched first and the store write failed, you would lock the app out.
  • Idempotency. `ALTER USER … IDENTIFIED BY …` is naturally idempotent — running it twice with the same password just sets the same value. That makes it safe for config-management tools that re-converge on every run.
  • No `FLUSH PRIVILEGES`. As noted, account-management statements apply immediately. You only need `FLUSH PRIVILEGES` if you bypass `ALTER USER` and edit `mysql.user` directly — which you should not.

For deeper account hardening (expiry, auth plugins, host scoping), see .

How do you do this in CI/CD or configuration management?

In a config-management context you usually should *not* write raw `mysql` commands at all — use the purpose-built module, which handles idempotency and credential sourcing for you.

Ansible ships a `mysql_user` module (`community.mysql.mysql_user`) that sets or updates a password declaratively:

“`yaml

  • name: Ensure appuser has the rotated password

community.mysql.mysql_user: name: appuser host: localhost password: “{{ vaulted_app_db_password }}” login_user: root login_unix_socket: /var/run/mysqld/mysqld.sock state: present “`

Here the secret comes from an Ansible Vault variable, never a literal, and the module is idempotent: it only changes the password when it actually differs. Similar resources exist for other config-management ecosystems.

For CI/CD pipelines, the same principles apply: pull the connecting credential from the runner’s secret store (injected as a protected variable), authenticate via `–login-path` on a long-lived agent or a temporary `0600` config, and never echo the SQL containing the new password into build logs. Disable command echoing around the sensitive step.


Run database automation on infrastructure you control

Scripting password rotation, encrypted credential stores, and `–login-path` setup all assume one thing: root-level access to the server. On shared or restricted database hosting, you often cannot install `mysql_config_editor`, place a `~/.my.cnf` for the service user, or schedule the cron jobs that drive rotation.

DarazHost VPS and dedicated server plans give you full root access to script your database administration exactly the way this guide describes — automate `ALTER USER` rotations, lock down credential files, and run config-management agents without hitting a permissions wall. With reliable infrastructure built for automated workflows and 24/7 expert support, you get a foundation where your security automation can actually run end to end. Explore and to host database workloads you fully control.


Frequently asked questions

Do I need `FLUSH PRIVILEGES` after updating a password with `ALTER USER`? No. `ALTER USER` is an account-management statement that takes effect immediately. `FLUSH PRIVILEGES` is only required if you modify the `mysql.user` grant tables directly with raw `UPDATE` statements, which is the deprecated approach you should avoid in scripts.

How do I update a password without any prompt at all? Remove the interactive `-p` by authenticating through a `0600`-protected `~/.my.cnf` or a `–login-path` created with `mysql_config_editor`. Then `mysql -e “ALTER USER …”` runs with zero prompts and no password in the command line.

Is using `-pPASSWORD` inline ever acceptable? Practically never. Even in a “throwaway” script it leaks the password into the process list (`ps`) and shell history. Use a config file or `–login-path` instead — it is barely more setup and removes the entire exposure.

Can I store the password in an environment variable for the script? You can reference an injected secret via an environment variable for the *new* password, but be aware that environment variables can be read from `/proc//environ` by privileged users. For multi-tenant or shared hosts, prefer feeding the statement via stdin or a tightly permissioned transient file.

What is the difference between this and resetting a forgotten password? This guide assumes you can already authenticate and want to update a password programmatically. If you have *lost* access and cannot log in at all, that is a recovery scenario — see , which uses `–skip-grant-tables` and a different procedure.

About the Author

Leave a Reply