diff --git a/cmd/config/config.go b/cmd/config/config.go
index 0300fd76e..4aefe62c0 100644
--- a/cmd/config/config.go
+++ b/cmd/config/config.go
@@ -36,6 +36,7 @@ func init() {
 	configCommand.AddCommand(configReconnectCommand)
 	configCommand.AddCommand(configDisconnectCommand)
 	configCommand.AddCommand(configUserInfoCommand)
+	configCommand.AddCommand(configEncryptionCommand)
 }
 
 var configCommand = &cobra.Command{
@@ -518,3 +519,91 @@ system.
 		return nil
 	},
 }
+
+func init() {
+	configEncryptionCommand.AddCommand(configEncryptionSetCommand)
+	configEncryptionCommand.AddCommand(configEncryptionRemoveCommand)
+	configEncryptionCommand.AddCommand(configEncryptionCheckCommand)
+}
+
+var configEncryptionCommand = &cobra.Command{
+	Use:   "encryption",
+	Short: `set, remove and check the encryption for the config file`,
+	Long: `This command sets, clears and checks the encryption for the config file using
+the subcommands below.
+`,
+}
+
+var configEncryptionSetCommand = &cobra.Command{
+	Use:   "set",
+	Short: `Set or change the config file encryption password`,
+	Long: strings.ReplaceAll(`This command sets or changes the config file encryption password.
+
+If there was no config password set then it sets a new one, otherwise
+it changes the existing config password.
+
+Note that if you are changing an encryption password using
+|--password-command| then this will be called once to decrypt the
+config using the old password and then again to read the new
+password to re-encrypt the config.
+
+When |--password-command| is called to change the password then the
+environment variable |RCLONE_PASSWORD_CHANGE=1| will be set. So if
+changing passwords programatically you can use the environment
+variable to distinguish which password you must supply.
+
+Alternatively you can remove the password first (with |rclone config
+encryption remove|), then set it again with this command which may be
+easier if you don't mind the unecrypted config file being on the disk
+briefly.
+`, "|", "`"),
+	RunE: func(command *cobra.Command, args []string) error {
+		cmd.CheckArgs(0, 0, command, args)
+		config.LoadedData()
+		config.ChangeConfigPasswordAndSave()
+		return nil
+	},
+}
+
+var configEncryptionRemoveCommand = &cobra.Command{
+	Use:   "remove",
+	Short: `Remove the config file encryption password`,
+	Long: strings.ReplaceAll(`Remove the config file encryption password
+
+This removes the config file encryption, returning it to un-encrypted.
+
+If |--password-command| is in use, this will be called to supply the old config
+password.
+
+If the config was not encrypted then no error will be returned and
+this command will do nothing.
+`, "|", "`"),
+	RunE: func(command *cobra.Command, args []string) error {
+		cmd.CheckArgs(0, 0, command, args)
+		config.LoadedData()
+		config.RemoveConfigPasswordAndSave()
+		return nil
+	},
+}
+
+var configEncryptionCheckCommand = &cobra.Command{
+	Use:   "check",
+	Short: `Check that the config file is encrypted`,
+	Long: strings.ReplaceAll(`This checks the config file is encrypted and that you can decrypt it.
+
+It will attempt to decrypt the config using the password you supply.
+
+If decryption fails it will return a non-zero exit code if using
+|--password-command|, otherwise it will prompt again for the password.
+
+If the config file is not encrypted it will return a non zero exit code.
+`, "|", "`"),
+	RunE: func(command *cobra.Command, args []string) error {
+		cmd.CheckArgs(0, 0, command, args)
+		config.LoadedData()
+		if !config.IsEncrypted() {
+			return errors.New("config file is NOT encrypted")
+		}
+		return nil
+	},
+}
diff --git a/docs/content/docs.md b/docs/content/docs.md
index 629bd84e8..f8f5153d9 100644
--- a/docs/content/docs.md
+++ b/docs/content/docs.md
@@ -1924,7 +1924,7 @@ Suffix length limit is 16 characters.
 
 The default is `.partial`.
 
-### --password-command SpaceSepList ###
+### --password-command SpaceSepList {#password-command}
 
 This flag supplies a program which should supply the config password
 when run. This is an alternative to rclone prompting for the password
@@ -1943,6 +1943,11 @@ Eg
     --password-command 'echo "hello with space"'
     --password-command 'echo "hello with ""quotes"" and space"'
 
+Note that when changing the configuration password the environment
+variable `RCLONE_PASSWORD_CHANGE=1` will be set. This can be used to
+distinguish initial decryption of the config file from the new
+password.
+
 See the [Configuration Encryption](#configuration-encryption) for more info.
 
 See a [Windows PowerShell example on the Wiki](https://github.com/rclone/rclone/wiki/Windows-Powershell-use-rclone-password-command-for-Config-file-password).
@@ -2546,6 +2551,12 @@ encryption from your configuration.
 
 There is no way to recover the configuration if you lose your password.
 
+You can also use
+
+- [rclone config encryption set](/commands/rclone_config_encryption_set/) to set the config encryption directly
+- [rclone config encryption remove](/commands/rclone_config_encryption_remove/) to remove it
+- [rclone config encryption check](/commands/rclone_config_encryption_check/) to check that it is encrypted properly.
+
 rclone uses [nacl secretbox](https://godoc.org/golang.org/x/crypto/nacl/secretbox)
 which in turn uses XSalsa20 and Poly1305 to encrypt and authenticate
 your configuration with secret-key cryptography.
@@ -2578,7 +2589,7 @@ An alternate means of supplying the password is to provide a script
 which will retrieve the password and print on standard output.  This
 script should have a fully specified path name and not rely on any
 environment variables.  The script is supplied either via
-`--password-command="..."` command line argument or via the
+[`--password-command="..."`](#password-command) command line argument or via the
 `RCLONE_PASSWORD_COMMAND` environment variable.
 
 One useful example of this is using the `passwordstore` application
diff --git a/fs/config/crypt.go b/fs/config/crypt.go
index f84b3dda1..4a68d4159 100644
--- a/fs/config/crypt.go
+++ b/fs/config/crypt.go
@@ -41,6 +41,11 @@ var (
 	PassConfigKeyForDaemonization = false
 )
 
+// IsEncrypted returns true if the config file is encrypted
+func IsEncrypted() bool {
+	return len(configKey) > 0
+}
+
 // Decrypt will automatically decrypt a reader
 func Decrypt(b io.ReadSeeker) (io.Reader, error) {
 	ctx := context.Background()
@@ -313,6 +318,11 @@ func ClearConfigPassword() {
 //
 // This will use --password-command if configured to read the password.
 func changeConfigPassword() {
+	// Set RCLONE_PASSWORD_CHANGE to "1" when calling the --password-command tool
+	_ = os.Setenv("RCLONE_PASSWORD_CHANGE", "1")
+	defer func() {
+		_ = os.Unsetenv("RCLONE_PASSWORD_CHANGE")
+	}()
 	pass, err := GetPasswordCommand(context.Background())
 	if err != nil {
 		fmt.Printf("Failed to read new password with --password-command: %v\n", err)
@@ -329,3 +339,22 @@ func changeConfigPassword() {
 		return
 	}
 }
+
+// ChangeConfigPasswordAndSave will query the user twice
+// for a password. If the same password is entered
+// twice the key is updated.
+//
+// This will use --password-command if configured to read the password.
+//
+// It will then save the config
+func ChangeConfigPasswordAndSave() {
+	changeConfigPassword()
+	SaveConfig()
+}
+
+// RemoveConfigPasswordAndSave will clear the config password and save
+// the unencrypted config file.
+func RemoveConfigPasswordAndSave() {
+	configKey = nil
+	SaveConfig()
+}
diff --git a/fs/config/crypt_internal_test.go b/fs/config/crypt_internal_test.go
index 9aa813cb3..2d19643c5 100644
--- a/fs/config/crypt_internal_test.go
+++ b/fs/config/crypt_internal_test.go
@@ -2,6 +2,8 @@ package config
 
 import (
 	"context"
+	"os"
+	"path/filepath"
 	"testing"
 
 	"github.com/rclone/rclone/fs"
@@ -64,8 +66,33 @@ func TestChangeConfigPassword(t *testing.T) {
 	// Get rid of any config password
 	ClearConfigPassword()
 
-	// Set correct password using --password command
-	ci.PasswordCommand = fs.SpaceSepList{"echo", "asdf"}
+	// Return the password, checking the state of the environment variable
+	checkCode := `
+package main
+
+import (
+	"fmt"
+	"os"
+	"log"
+)
+
+func main() {
+	v := os.Getenv("RCLONE_PASSWORD_CHANGE")
+	if v == "" {
+		log.Fatal("Env var not found")
+	} else if v != "1" {
+		log.Fatal("Env var wrong value")
+	} else {
+		fmt.Println("asdf")
+	}
+}
+`
+	dir := t.TempDir()
+	code := filepath.Join(dir, "file.go")
+	require.NoError(t, os.WriteFile(code, []byte(checkCode), 0777))
+
+	// Set correct password using --password-command
+	ci.PasswordCommand = fs.SpaceSepList{"go", "run", code}
 	changeConfigPassword()
 	err = Data().Load()
 	require.NoError(t, err)
diff --git a/fs/config/crypt_test.go b/fs/config/crypt_test.go
index 5cb3fa016..73c3bf6f6 100644
--- a/fs/config/crypt_test.go
+++ b/fs/config/crypt_test.go
@@ -6,6 +6,8 @@ package config_test
 
 import (
 	"context"
+	"os"
+	"path/filepath"
 	"testing"
 
 	"github.com/rclone/rclone/fs"
@@ -24,8 +26,10 @@ func TestConfigLoadEncrypted(t *testing.T) {
 	}()
 
 	// Set correct password
+	assert.False(t, config.IsEncrypted())
 	err = config.SetConfigPassword("asdf")
 	require.NoError(t, err)
+	assert.True(t, config.IsEncrypted())
 	err = config.Data().Load()
 	require.NoError(t, err)
 	sections := config.Data().GetSectionList()
@@ -138,4 +142,31 @@ func TestGetPasswordCommand(t *testing.T) {
 	ci.PasswordCommand = fs.SpaceSepList{"XXX non-existent command XXX", ""}
 	_, err = config.GetPasswordCommand(ctx)
 	assert.ErrorContains(t, err, "not found")
+
+	// Check the state of the environment variable in --password-command
+	checkCode := `
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	if _, found := os.LookupEnv("RCLONE_PASSWORD_CHANGE"); found {
+		fmt.Println("Env var set")
+	} else {
+		fmt.Println("OK")
+	}
+}
+`
+	dir := t.TempDir()
+	code := filepath.Join(dir, "file.go")
+	require.NoError(t, os.WriteFile(code, []byte(checkCode), 0777))
+
+	// Check the environment variable unset when called directly
+	ci.PasswordCommand = fs.SpaceSepList{"go", "run", code}
+	pass, err = config.GetPasswordCommand(ctx)
+	require.NoError(t, err)
+	assert.Equal(t, "OK", pass)
 }
diff --git a/fs/config/ui.go b/fs/config/ui.go
index ff8acda0f..b08e9481c 100644
--- a/fs/config/ui.go
+++ b/fs/config/ui.go
@@ -797,13 +797,11 @@ func SetPassword() {
 			what := []string{"cChange Password", "uUnencrypt configuration", "qQuit to main menu"}
 			switch i := Command(what); i {
 			case 'c':
-				changeConfigPassword()
-				SaveConfig()
+				ChangeConfigPasswordAndSave()
 				fmt.Println("Password changed")
 				continue
 			case 'u':
-				configKey = nil
-				SaveConfig()
+				RemoveConfigPasswordAndSave()
 				continue
 			case 'q':
 				return
@@ -815,8 +813,7 @@ func SetPassword() {
 			what := []string{"aAdd Password", "qQuit to main menu"}
 			switch i := Command(what); i {
 			case 'a':
-				changeConfigPassword()
-				SaveConfig()
+				ChangeConfigPasswordAndSave()
 				fmt.Println("Password set")
 				continue
 			case 'q':