diff --git a/cmd/embedded-cluster/admin_console.go b/cmd/embedded-cluster/admin_console.go
new file mode 100644
index 000000000..0aafa37b9
--- /dev/null
+++ b/cmd/embedded-cluster/admin_console.go
@@ -0,0 +1,56 @@
+package main
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/sirupsen/logrus"
+	"github.com/urfave/cli/v2"
+
+	"github.com/replicatedhq/embedded-cluster/pkg/defaults"
+	"github.com/replicatedhq/embedded-cluster/pkg/kotscli"
+)
+
+func adminConsoleCommand() *cli.Command {
+	return &cli.Command{
+		Name:  "admin-console",
+		Usage: fmt.Sprintf("Manage the %s Admin Console", defaults.BinaryName()),
+		Subcommands: []*cli.Command{
+			adminConsoleResetPassswordCommand(),
+		},
+	}
+}
+
+func adminConsoleResetPassswordCommand() *cli.Command {
+	return &cli.Command{
+		Name:  "reset-password",
+		Usage: "Reset the Admin Console password",
+		Before: func(c *cli.Context) error {
+			if os.Getuid() != 0 {
+				return fmt.Errorf("reset-password command must be run as root")
+			}
+			if len(c.Args().Slice()) != 1 {
+				return fmt.Errorf("expected admin console password as argument")
+			}
+			return nil
+		},
+		Action: func(c *cli.Context) error {
+			provider, err := getProviderFromCluster(c.Context)
+			if err != nil {
+				return err
+			}
+
+			password := c.Args().Get(0)
+			if !validateAdminConsolePassword(password, password) {
+				return ErrNothingElseToAdd
+			}
+
+			if err := kotscli.ResetPassword(provider, password); err != nil {
+				return err
+			}
+
+			logrus.Info("Admin Console password reset successfully")
+			return nil
+		},
+	}
+}
diff --git a/cmd/embedded-cluster/k0s.go b/cmd/embedded-cluster/k0s.go
index d8b729573..fd2a35e28 100644
--- a/cmd/embedded-cluster/k0s.go
+++ b/cmd/embedded-cluster/k0s.go
@@ -3,9 +3,12 @@ package main
 import (
 	"context"
 	"encoding/json"
+	"fmt"
+	"os"
 	"os/exec"
 
 	k0sv1beta1 "github.com/k0sproject/k0s/pkg/apis/k0s/v1beta1"
+	"github.com/replicatedhq/embedded-cluster/pkg/defaults"
 )
 
 var (
@@ -27,6 +30,10 @@ type k0sVars struct {
 
 // getK0sStatus returns the status of the k0s service.
 func getK0sStatus(ctx context.Context) (*k0sStatus, error) {
+	if _, err := os.Stat(k0s); err != nil {
+		return nil, fmt.Errorf("%s does not seem to be installed on this node", defaults.BinaryName())
+	}
+
 	// get k0s status json
 	out, err := exec.CommandContext(ctx, k0s, "status", "-o", "json").Output()
 	if err != nil {
diff --git a/cmd/embedded-cluster/main.go b/cmd/embedded-cluster/main.go
index 8474b668b..333ce38c3 100644
--- a/cmd/embedded-cluster/main.go
+++ b/cmd/embedded-cluster/main.go
@@ -37,6 +37,7 @@ func main() {
 			materializeCommand(),
 			updateCommand(),
 			restoreCommand(),
+			adminConsoleCommand(),
 		},
 	}
 	if err := app.RunContext(ctx, os.Args); err != nil {
diff --git a/e2e/install_test.go b/e2e/install_test.go
index 386b12851..f5f89667f 100644
--- a/e2e/install_test.go
+++ b/e2e/install_test.go
@@ -60,6 +60,16 @@ func TestSingleNodeInstallation(t *testing.T) {
 		t.Fatalf("fail to check postupgrade state: %v: %s: %s", err, stdout, stderr)
 	}
 
+	t.Logf("%s: resetting admin console password", time.Now().Format(time.RFC3339))
+	newPassword := "newpass"
+	line = []string{"embedded-cluster", "admin-console", "reset-password", newPassword}
+	_, _, err := tc.RunCommandOnNode(0, line)
+	require.NoError(t, err, "unable to reset admin console password")
+
+	t.Logf("%s: logging in with the new password", time.Now().Format(time.RFC3339))
+	_, _, err = tc.RunPlaywrightTest("login-with-custom-password", newPassword)
+	require.NoError(t, err, "unable to login with the new password")
+
 	t.Logf("%s: test complete", time.Now().Format(time.RFC3339))
 }
 
diff --git a/e2e/playwright/tests/login-with-custom-password/test.spec.ts b/e2e/playwright/tests/login-with-custom-password/test.spec.ts
new file mode 100644
index 000000000..9922cdc95
--- /dev/null
+++ b/e2e/playwright/tests/login-with-custom-password/test.spec.ts
@@ -0,0 +1,8 @@
+import { test, expect } from '@playwright/test';
+import { login } from '../shared';
+
+test('login with custom password', async ({ page }) => {
+  test.setTimeout(30 * 1000); // 30 seconds
+  await login(page, process.env.ADMIN_CONSOLE_PASSWORD);
+  await expect(page.locator('.NavItem').getByText('Cluster Management')).toBeVisible();
+});
diff --git a/e2e/playwright/tests/shared/login.ts b/e2e/playwright/tests/shared/login.ts
index 09c24847c..0b90c0189 100644
--- a/e2e/playwright/tests/shared/login.ts
+++ b/e2e/playwright/tests/shared/login.ts
@@ -1,6 +1,6 @@
-export const login = async (page) => {
+export const login = async (page, password = 'password') => {
   await page.goto('/');
   await page.getByPlaceholder('password').click();
-  await page.getByPlaceholder('password').fill('password');
+  await page.getByPlaceholder('password').fill(password);
   await page.getByRole('button', { name: 'Log in' }).click();
 };
diff --git a/e2e/scripts/playwright.sh b/e2e/scripts/playwright.sh
index f6d69551f..59498395e 100755
--- a/e2e/scripts/playwright.sh
+++ b/e2e/scripts/playwright.sh
@@ -18,6 +18,8 @@ main() {
   elif [ "$test_name" == "deploy-upgrade" ]; then
     export APP_UPGRADE_VERSION="$2"
     export SKIP_CLUSTER_UPGRADE_CHECK="${3:-}"
+  elif [ "$test_name" == "login-with-custom-password" ]; then
+    export ADMIN_CONSOLE_PASSWORD="$2"
   fi
 
   export BASE_URL="${BASE_URL:-http://10.0.0.2:30003}"
diff --git a/pkg/helpers/command.go b/pkg/helpers/command.go
index 86c5185f3..54d8d49a6 100644
--- a/pkg/helpers/command.go
+++ b/pkg/helpers/command.go
@@ -14,6 +14,8 @@ type RunCommandOptions struct {
 	Writer io.Writer
 	// Env is a map of additional environment variables to set for the command.
 	Env map[string]string
+	// Stdin is the standard input to be used when running the command.
+	Stdin io.Reader
 }
 
 // RunCommandWithOptions runs a the provided command with the options specified.
@@ -28,6 +30,9 @@ func RunCommandWithOptions(opts RunCommandOptions, bin string, args ...string) e
 	if opts.Writer != nil {
 		cmd.Stdout = io.MultiWriter(opts.Writer, stdout)
 	}
+	if opts.Stdin != nil {
+		cmd.Stdin = opts.Stdin
+	}
 	cmd.Stderr = stderr
 	cmdEnv := cmd.Environ()
 	for k, v := range opts.Env {
diff --git a/pkg/kotscli/kotscli.go b/pkg/kotscli/kotscli.go
index d3ab8c8c1..1f152cc88 100644
--- a/pkg/kotscli/kotscli.go
+++ b/pkg/kotscli/kotscli.go
@@ -84,6 +84,27 @@ func Install(provider *defaults.Provider, opts InstallOptions, msg *spinner.Mess
 	return nil
 }
 
+func ResetPassword(provider *defaults.Provider, password string) error {
+	materializer := goods.NewMaterializer(provider)
+	kotsBinPath, err := materializer.InternalBinary("kubectl-kots")
+	if err != nil {
+		return fmt.Errorf("unable to materialize kubectl-kots binary: %w", err)
+	}
+	defer os.Remove(kotsBinPath)
+
+	runCommandOptions := helpers.RunCommandOptions{
+		Env:   map[string]string{"KUBECONFIG": provider.PathToKubeConfig()},
+		Stdin: strings.NewReader(fmt.Sprintf("%s\n", password)),
+	}
+
+	resetArgs := []string{"reset-password", "kotsadm"}
+	if err := helpers.RunCommandWithOptions(runCommandOptions, kotsBinPath, resetArgs...); err != nil {
+		return fmt.Errorf("unable to reset admin console password: %w", err)
+	}
+
+	return nil
+}
+
 type AirgapUpdateOptions struct {
 	AppSlug      string
 	Namespace    string