Repository: code-ready/crc
Pull request #3075 (2022-03-21 13:58:31)

Use podman system connection to add remote uri to podman config
As of now user have to do `eval $(crc podman-env)` to export podman
connection specific environment variable to current shell session.
Podman cli have `system connection` command which manage the ssh
destination information for podman configuration which consumed
by podman client.

With this PR now if a user have podman-remote client binary to path
then after `crc start` they can directly use podman commands without
executing current `eval` command. `podman-env` command option still
there to support backward compatibility and also exposing moby specific
environment variable. It will still make sense if user don't have podman
remote binary in the path.

```
✗ ./crc start
[...]
podman runtime is currently configured in rootless mode. If your containers
require root permissions, or if you run into compatibility
issues with non-podman clients, you can switch using the following commands:

  $ eval $(crc podman-env --root)
  $ podman COMMAND

✗ podman system connection ls
Name                         URI                                                         Identity                                    Default
crc                          ssh://core@127.0.0.1:2222/run/user/1000/podman/podman.sock  /Users/prkumar/.crc/machines/crc/id_ecdsa   true
crc-root                     ssh://core@127.0.0.1:2222/run/podman/podman.sock            /Users/prkumar/.crc/machines/crc/id_ecdsa   false

✗ ./crc podman-env --root
export PATH="/Users/prkumar/.crc/bin/oc:$PATH"
export DOCKER_HOST="unix:///Users/prkumar/.crc/machines/crc/docker.sock"

✗ podman system connection ls
Name                         URI                                                         Identity                                    Default
crc                          ssh://core@127.0.0.1:2222/run/user/1000/podman/podman.sock  /Users/prkumar/.crc/machines/crc/id_ecdsa   false
crc-root                     ssh://core@127.0.0.1:2222/run/podman/podman.sock            /Users/prkumar/.crc/machines/crc/id_ecdsa   true

✗ podman info
host:
  arch: amd64
  buildahVersion: 1.23.1
  cgroupControllers:
```

praveenkumar: > What happens on linux when this is used, does this affect only podman-remote, or is `podman` itself forcefully switched to use a remote runtime? I assume when the crc VM is not running this errors out?

In linux it is only affect `podman-remote` nothing around `podman` change with this config.


praveenkumar: @cfergeau I removed start message and `podman-env` from this PR, I think we need to discuss what it should be for end user to educate them how to make `rootful` one as default for system connection setting (A doc or start message template or something else).
cfergeau: > @cfergeau I removed start message and `podman-env` from this PR, I think we need to discuss what it should be for end user to educate them how to make `rootful` one as default for system connection setting (A doc or start message template or something else).

Yup, need to decide how we want the cmdline UI to look like. Do we need to keep `podman-env` at all? Or could we just tell the user at the end of start that rootless podman is ready for use by calling `podman-remote`, and if they want to switch to rootful podman, they can use `podman-remote system connection ...` ?
openshift-ci[bot]: @praveenkumar: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
ci/prow/e2e-crc | beb4433c187f9a18c5dc78c9f057f302c7140a74 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/3075/pull-ci-code-ready-crc-main-e2e-crc/1506180059243745280) | true | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=3075). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:praveenkumar).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
praveenkumar: 



> > @cfergeau I removed start message and `podman-env` from this PR, I think we need to discuss what it should be for end user to educate them how to make `rootful` one as default for system connection setting (A doc or start message template or something else).
> 
> Yup, need to decide how we want the cmdline UI to look like. Do we need to keep `podman-env` at all? Or could we just tell the user at the end of start that rootless podman is ready for use by calling `podman-remote`, and if they want to switch to rootful podman, they can use `podman-remote system connection ...` ?

@cfergeau Yes on the UI side I will try to create new PR once we decide what we actually want. We still need `podman-env` to show docker host env but again this is again can be part of `crc start` message where we tell user around rootless/rootful` ?
cfergeau: > We still need podman-env to show docker host env 

If `podman-env` now is docker-only, its name is confusing :(
praveenkumar: > > We still need podman-env to show docker host env
> 
> If `podman-env` now is docker-only, its name is confusing :(

@cfergeau Right now we are not changing anything around `podman-env` so as part of new PR/issue we can check what should be the name. Also `podman-env` still set the podman binary to the path.
praveenkumar: @cfergeau updated PR with all the suggestions
openshift-ci[bot]: @praveenkumar: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
ci/prow/e2e-crc | b7b150e3a328519f3c2e5fd079e46864355d5d10 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/3075/pull-ci-code-ready-crc-main-e2e-crc/1508702847619829760) | true | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=3075). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:praveenkumar).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
openshift-ci[bot]: @praveenkumar: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
ci/prow/e2e-crc | 9bbfa6948524c0cc9d8fa9e3d2e9dfab693710a4 | [link](https://prow.ci.openshift.org/view/gs/origin-ci-test/pr-logs/pull/code-ready_crc/3075/pull-ci-code-ready-crc-main-e2e-crc/1508761399281586176) | true | `/test e2e-crc`

[Full PR test history](https://prow.ci.openshift.org/pr-history?org=code-ready&repo=crc&pr=3075). [Your PR dashboard](https://prow.ci.openshift.org/pr?query=is:pr+state:open+author:praveenkumar).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/cmd/crc/cmd/daemon.go b/cmd/crc/cmd/daemon.go
index 4bf5e79d0..0afb2cc7a 100644
--- a/cmd/crc/cmd/daemon.go
+++ b/cmd/crc/cmd/daemon.go
@@ -19,7 +19,6 @@ import (
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/daemonclient"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	"github.com/code-ready/crc/pkg/crc/preflight"
 	"github.com/containers/gvisor-tap-vsock/pkg/types"
 	"github.com/containers/gvisor-tap-vsock/pkg/virtualnetwork"
 	"github.com/docker/go-units"
@@ -27,7 +26,6 @@ import (
 	"github.com/pkg/errors"
 	log "github.com/sirupsen/logrus"
 	"github.com/spf13/cobra"
-	"k8s.io/client-go/util/exec"
 )
 
 var watchdog bool
@@ -56,13 +54,6 @@ var daemonCmd = &cobra.Command{
 			return errors.New("daemon is already running")
 		}
 
-		if err := preflight.StartPreflightChecks(config); err != nil {
-			return exec.CodeExitError{
-				Err:  err,
-				Code: preflightFailedExitCode,
-			}
-		}
-
 		virtualNetworkConfig := types.Configuration{
 			Debug:             false, // never log packets
 			CaptureFile:       os.Getenv("CRC_DAEMON_PCAP_FILE"),
diff --git a/cmd/crc/cmd/daemon_darwin.go b/cmd/crc/cmd/daemon_darwin.go
index 4d02af8bf..e91c7a933 100644
--- a/cmd/crc/cmd/daemon_darwin.go
+++ b/cmd/crc/cmd/daemon_darwin.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/code-ready/crc/pkg/crc/constants"
 	"github.com/code-ready/crc/pkg/crc/logging"
-	crcversion "github.com/code-ready/crc/pkg/crc/version"
 )
 
 func vsockListener() (net.Listener, error) {
@@ -34,9 +33,6 @@ func checkIfDaemonIsRunning() (bool, error) {
 }
 
 func daemonNotRunningMessage() string {
-	if crcversion.IsInstaller() {
-		return "Is '/Applications/CodeReady Containers.app' running? Cannot reach daemon API"
-	}
 	return genericDaemonNotRunningMessage
 }
 
diff --git a/cmd/crc/cmd/podman_env.go b/cmd/crc/cmd/podman_env.go
index 26d308228..1a9a1c04d 100644
--- a/cmd/crc/cmd/podman_env.go
+++ b/cmd/crc/cmd/podman_env.go
@@ -38,9 +38,9 @@ func runPodmanEnv() error {
 		return err
 	}
 
-	socket := "/run/user/1000/podman/podman.sock"
+	socket := constants.RootlessPodmanSocket
 	if root {
-		socket = "/run/podman/podman.sock"
+		socket = constants.RootfulPodmanSocket
 	}
 	fmt.Println(shell.GetPathEnvString(userShell, constants.CrcOcBinDir))
 	fmt.Println(shell.GetEnvString(userShell, "CONTAINER_SSHKEY", connectionDetails.SSHKeys[0]))
diff --git a/docs/source/topics/proc_changing-the-selected-preset.adoc b/docs/source/topics/proc_changing-the-selected-preset.adoc
index 9c9d2013f..fc9617ff9 100644
--- a/docs/source/topics/proc_changing-the-selected-preset.adoc
+++ b/docs/source/topics/proc_changing-the-selected-preset.adoc
@@ -20,7 +20,7 @@ To enable preset changes, you must delete the existing instance and start a new
 +
 [subs="+quotes,attributes"]
 ----
-$ {bin} config preset __<name>__
+$ {bin} config set preset __<name>__
 ----
 +
 Valid preset names are `openshift` for {ocp} and `podman` for the Podman container runtime.
diff --git a/packaging/windows/product.wxs.template b/packaging/windows/product.wxs.template
index 1171d6409..d082e174e 100755
--- a/packaging/windows/product.wxs.template
+++ b/packaging/windows/product.wxs.template
@@ -83,6 +83,8 @@
         <CustomAction Id="RemoveCrcGroup" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />
         <SetProperty Action="CARemoveCrcUsersGroupRollback" Id="RemoveCrcGroupRollback" Value='"[System64Folder]cmd.exe" /c net localgroup crc-users /del' Before="RemoveCrcGroup" Sequence="execute"/>
         <CustomAction Id="RemoveCrcGroupRollback" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="rollback" Impersonate="no" Return="ignore" />
+        <SetProperty Action="CARemoveCrcDaemonTask" Id="RemoveCrcDaemonTask" Value='"[System64Folder]cmd.exe" /c schtasks /delete /TN crcDaemon /F' Before='RemoveFiles' Sequence="execute"/>
+        <CustomAction Id="RemoveCrcDaemonTask" BinaryKey="WixCA" DllEntry="WixQuietExec" Execute="deferred" Impersonate="no" Return="ignore" />
 
         <SetProperty Action="CAInstallHyperv" Id="InstallHyperv" Value='"[System64Folder]dism.exe" /online /enable-feature /featureName:microsoft-hyper-v-all /NoRestart /quiet' Before="InstallHyperv" Sequence="execute"/>
         <CustomAction Id="InstallHyperv" BinaryKey="WixCA" DllEntry="WixQuietExec64" Execute="deferred" Impersonate="no" Return="ignore" />
@@ -98,6 +100,7 @@
             <Custom Action="RemoveCrcGroup" After="RemoveFolders"> Installed AND NOT PATCH AND REMOVE~="ALL" AND NOT UPGRADINGPRODUCTCODE</Custom>
             <Custom Action="InstallHyperv" Before="CreateCrcGroup"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</Custom>
             <Custom Action="RemoveCrcGroupRollback" After="CreateCrcGroup"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</Custom>
+            <Custom Action="RemoveCrcDaemonTask" Before='RemoveFiles'>Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
             <ScheduleReboot After="InstallFinalize"> NOT Installed AND NOT REMOVE~="ALL" AND NOT WIX_UPGRADE_DETECTED</ScheduleReboot>
         </InstallExecuteSequence>
         <Feature Id="DefaultFeature" Level="1">
@@ -117,6 +120,7 @@
             <ProgressText Action="RemoveCrcGroup">Removing crc-users group</ProgressText>
             <ProgressText Action="InstallHyperv">Installing Hyper-V</ProgressText>
             <ProgressText Action="AddUserToHypervAdminGroup">Adding user: [LogonUser] to Hyper-V Administrators group</ProgressText>
+            <ProgressText Action="RemoveCrcDaemonTask">Removing crcDaemon task</ProgressText>
         </UI>
         <Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
         <!-- this should help to propagate env var changes -->
diff --git a/pkg/crc/constants/constants.go b/pkg/crc/constants/constants.go
index b24023edd..46d0e447b 100644
--- a/pkg/crc/constants/constants.go
+++ b/pkg/crc/constants/constants.go
@@ -30,6 +30,8 @@ const (
 	DefaultContext            = "admin"
 	DaemonHTTPEndpoint        = "http://unix/api"
 	DefaultPodmanNamedPipe    = `\\.\pipe\crc-podman`
+	RootlessPodmanSocket      = "/run/user/1000/podman/podman.sock"
+	RootfulPodmanSocket       = "/run/podman/podman.sock"
 
 	VSockGateway = "192.168.127.1"
 	VsockSSHPort = 2222
diff --git a/pkg/crc/constants/constants_darwin.go b/pkg/crc/constants/constants_darwin.go
index 7a0a21d07..48e8990a5 100644
--- a/pkg/crc/constants/constants_darwin.go
+++ b/pkg/crc/constants/constants_darwin.go
@@ -10,6 +10,7 @@ const (
 	OcExecutableName           = "oc"
 	PodmanRemoteExecutableName = "podman"
 	TrayExecutableName         = "CodeReady Containers.app"
+	DaemonAgentLabel           = "com.redhat.crc.daemon"
 )
 
 var (
diff --git a/pkg/crc/constants/constants_windows.go b/pkg/crc/constants/constants_windows.go
index b213c2eb3..5c7742993 100644
--- a/pkg/crc/constants/constants_windows.go
+++ b/pkg/crc/constants/constants_windows.go
@@ -5,4 +5,6 @@ const (
 	PodmanRemoteExecutableName = "podman.exe"
 	TapSocketPath              = ""
 	DaemonHTTPNamedPipe        = `\\.\pipe\crc-http`
+	DaemonTaskName             = "crcDaemon"
+	AdminHelperServiceName     = "CodeReadyContainersAdminHelper"
 )
diff --git a/pkg/crc/machine/delete.go b/pkg/crc/machine/delete.go
index c78c319c9..7e2f095bb 100644
--- a/pkg/crc/machine/delete.go
+++ b/pkg/crc/machine/delete.go
@@ -4,6 +4,7 @@ import (
 	"os"
 
 	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/podman"
 	"github.com/pkg/errors"
 )
 
@@ -25,6 +26,14 @@ func (client *client) Delete() error {
 		}
 	}
 
+	// Remove the podman system connection for crc
+	if err := podman.RemoveRootlessSystemConnection(); err != nil {
+		return err
+	}
+	if err := podman.RemoveRootfulSystemConnection(); err != nil {
+		return err
+	}
+
 	if err := cleanKubeconfig(getGlobalKubeConfigPath(), getGlobalKubeConfigPath()); err != nil {
 		if !errors.Is(err, os.ErrNotExist) {
 			logging.Warnf("Failed to remove crc contexts from kubeconfig: %v", err)
diff --git a/pkg/crc/machine/start.go b/pkg/crc/machine/start.go
index f87980108..91c0616d4 100644
--- a/pkg/crc/machine/start.go
+++ b/pkg/crc/machine/start.go
@@ -23,6 +23,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/machine/types"
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/oc"
+	"github.com/code-ready/crc/pkg/crc/podman"
 	crcPreset "github.com/code-ready/crc/pkg/crc/preset"
 	"github.com/code-ready/crc/pkg/crc/services"
 	"github.com/code-ready/crc/pkg/crc/services/dns"
@@ -331,6 +332,14 @@ func (client *client) Start(ctx context.Context, startConfig types.StartConfig)
 			return nil, fmt.Errorf("Failed to rotate bearer token for cockpit webconsole: %w", err)
 		}
 
+		c, err := client.ConnectionDetails()
+		if err != nil {
+			return nil, err
+		}
+		if err := addPodmanSystemConnections(c); err != nil {
+			return nil, err
+		}
+
 		return &types.StartResult{
 			Status: vmState,
 		}, nil
@@ -757,3 +766,18 @@ func updateCockpitConsoleBearerToken(sshRunner *crcssh.Runner) error {
 
 	return nil
 }
+
+func addPodmanSystemConnections(c *types.ConnectionDetails) error {
+	rootlessURI := fmt.Sprintf("ssh://%s@%s:%d%s", c.SSHUsername, c.IP, c.SSHPort, constants.RootlessPodmanSocket)
+	rootfulURI := fmt.Sprintf("ssh://%s@%s:%d%s", c.SSHUsername, c.IP, c.SSHPort, constants.RootfulPodmanSocket)
+	if err := podman.AddRootlessSystemConnection(c.SSHKeys[0], rootlessURI); err != nil {
+		return err
+	}
+	if err := podman.MakeRootlessSystemConnectionDefault(); err != nil {
+		return err
+	}
+	if err := podman.AddRootfulSystemConnection(c.SSHKeys[0], rootfulURI); err != nil {
+		return err
+	}
+	return nil
+}
diff --git a/pkg/crc/podman/podman.go b/pkg/crc/podman/podman.go
new file mode 100644
index 000000000..427aa918a
--- /dev/null
+++ b/pkg/crc/podman/podman.go
@@ -0,0 +1,65 @@
+package podman
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+	crcos "github.com/code-ready/crc/pkg/os"
+)
+
+const (
+	rootlessConn = "crc"
+	rootfulConn  = "crc-root"
+)
+
+var podmanExecutablePath = filepath.Join(constants.CrcOcBinDir, constants.PodmanRemoteExecutableName)
+
+func run(args ...string) (string, string, error) {
+	return crcos.RunWithDefaultLocale(podmanExecutablePath, args...)
+}
+
+func addSystemConnection(name, identity, uri string) error {
+	if _, stderr, err := run("system", "connection", "add", "--identity", identity, name, uri); err != nil {
+		return fmt.Errorf("failed to add podman system connection %v: %s", err, stderr)
+	}
+	return nil
+}
+
+func AddRootlessSystemConnection(sshIdentity, uri string) error {
+	return addSystemConnection(rootlessConn, sshIdentity, uri)
+}
+
+func AddRootfulSystemConnection(sshIdentity, uri string) error {
+	return addSystemConnection(rootfulConn, sshIdentity, uri)
+}
+
+func removeSystemConnection(name string) error {
+	if _, stderr, err := run("system", "connection", "remove", name); err != nil {
+		return fmt.Errorf("failed to remove podman system connection %v: %s", err, stderr)
+	}
+	return nil
+}
+
+func RemoveRootlessSystemConnection() error {
+	return removeSystemConnection(rootlessConn)
+}
+
+func RemoveRootfulSystemConnection() error {
+	return removeSystemConnection(rootfulConn)
+}
+
+func makeSystemConnectionDefault(name string) error {
+	if _, stderr, err := run("system", "connection", "default", name); err != nil {
+		return fmt.Errorf("failed to make %s podman system connection as default %v: %s", name, err, stderr)
+	}
+	return nil
+}
+
+func MakeRootlessSystemConnectionDefault() error {
+	return makeSystemConnectionDefault(rootlessConn)
+}
+
+func MakeRootfulSystemConnectionDefault() error {
+	return makeSystemConnectionDefault(rootfulConn)
+}
diff --git a/pkg/crc/preflight/preflight_checks_darwin.go b/pkg/crc/preflight/preflight_checks_darwin.go
index 0d9e6cbd9..c8ecbb670 100644
--- a/pkg/crc/preflight/preflight_checks_darwin.go
+++ b/pkg/crc/preflight/preflight_checks_darwin.go
@@ -15,6 +15,7 @@ import (
 	"github.com/code-ready/crc/pkg/crc/network"
 	"github.com/code-ready/crc/pkg/crc/version"
 	crcos "github.com/code-ready/crc/pkg/os"
+	"github.com/code-ready/crc/pkg/os/launchd"
 	"github.com/klauspost/cpuid/v2"
 	"golang.org/x/sys/unix"
 )
@@ -242,3 +243,65 @@ func stopCRCHyperkitProcess() error {
 	}
 	return nil
 }
+
+func getDaemonConfig() (*launchd.AgentConfig, error) {
+	logFilePath := filepath.Join(constants.CrcBaseDir, ".launchd-crcd.log")
+
+	env := map[string]string{"Version": version.GetCRCVersion()}
+	daemonConfig := launchd.AgentConfig{
+		Label:          constants.DaemonAgentLabel,
+		ExecutablePath: constants.CrcSymlinkPath,
+		StdOutFilePath: logFilePath,
+		StdErrFilePath: logFilePath,
+		Args:           []string{"daemon", "--log-level=debug"},
+		Env:            env,
+	}
+
+	return &daemonConfig, nil
+}
+
+func checkIfDaemonPlistFileExists() error {
+	daemonConfig, err := getDaemonConfig()
+	if err != nil {
+		return err
+	}
+	if err := launchd.CheckPlist(*daemonConfig); err != nil {
+		return err
+	}
+	if !launchd.AgentRunning(daemonConfig.Label) {
+		return fmt.Errorf("launchd agent '%s' is not running", daemonConfig.Label)
+	}
+	return nil
+}
+
+func fixDaemonPlistFileExists() error {
+	daemonConfig, err := getDaemonConfig()
+	if err != nil {
+		return err
+	}
+	return fixPlistFileExists(*daemonConfig)
+}
+
+func removeDaemonPlistFile() error {
+	if err := launchd.UnloadPlist(constants.DaemonAgentLabel); err != nil {
+		return err
+	}
+	return launchd.RemovePlist(constants.DaemonAgentLabel)
+}
+
+func fixPlistFileExists(agentConfig launchd.AgentConfig) error {
+	logging.Debugf("Creating plist for %s", agentConfig.Label)
+	err := launchd.CreatePlist(agentConfig)
+	if err != nil {
+		return err
+	}
+	if err := launchd.LoadPlist(agentConfig.Label); err != nil {
+		logging.Debugf("failed to load launchd agent '%s': %v", agentConfig.Label, err.Error())
+		return err
+	}
+	if err := launchd.RestartAgent(agentConfig.Label); err != nil {
+		logging.Debugf("failed to restart launchd agent '%s': %v", agentConfig.Label, err.Error())
+		return err
+	}
+	return nil
+}
diff --git a/pkg/crc/preflight/preflight_checks_linux.go b/pkg/crc/preflight/preflight_checks_linux.go
index 75e6120ff..0c55b8386 100644
--- a/pkg/crc/preflight/preflight_checks_linux.go
+++ b/pkg/crc/preflight/preflight_checks_linux.go
@@ -700,50 +700,3 @@ func getCPUFlags() (string, error) {
 	}
 	return flags, nil
 }
-
-func runtimeExecutablePath() (string, error) {
-	path, err := exec.LookPath(os.Args[0])
-	if err != nil {
-		// os.Args[0] is not in $PATH, crc must have been started by specifying the path to its binary
-		path = os.Args[0]
-	}
-	path, err = filepath.Abs(path)
-	if err != nil {
-		return "", err
-	}
-	return filepath.EvalSymlinks(path)
-}
-
-func checkCrcSymlink() error {
-	runtimePath, err := runtimeExecutablePath()
-	if err != nil {
-		return err
-	}
-	symlinkPath, err := filepath.EvalSymlinks(constants.CrcSymlinkPath)
-	if err != nil {
-		return err
-	}
-	if symlinkPath != runtimePath {
-		return fmt.Errorf("%s points to %s, not to %s", constants.CrcSymlinkPath, symlinkPath, runtimePath)
-	}
-
-	return nil
-}
-
-func fixCrcSymlink() error {
-	_ = os.Remove(constants.CrcSymlinkPath)
-
-	runtimePath, err := runtimeExecutablePath()
-	if err != nil {
-		return err
-	}
-	logging.Debugf("symlinking %s to %s", runtimePath, constants.CrcSymlinkPath)
-	return os.Symlink(runtimePath, constants.CrcSymlinkPath)
-}
-
-func removeCrcSymlink() error {
-	if crcos.FileExists(constants.CrcSymlinkPath) {
-		return os.Remove(constants.CrcSymlinkPath)
-	}
-	return nil
-}
diff --git a/pkg/crc/preflight/preflight_checks_nonwin.go b/pkg/crc/preflight/preflight_checks_nonwin.go
index 5ca8f3f2e..f99ed4b72 100644
--- a/pkg/crc/preflight/preflight_checks_nonwin.go
+++ b/pkg/crc/preflight/preflight_checks_nonwin.go
@@ -6,6 +6,7 @@ package preflight
 import (
 	"fmt"
 	"os"
+	"os/exec"
 	"path/filepath"
 	"runtime"
 	"syscall"
@@ -70,6 +71,17 @@ func genericPreflightChecks(preset crcpreset.Preset) []Check {
 			fixDescription: fmt.Sprintf("crc requires at least %s to run", units.HumanSize(float64(constants.GetDefaultMemory(preset)*1024*1024))),
 			flags:          NoFix,
 
+			labels: None,
+		},
+		{
+			configKeySuffix:    "check-crc-symlink",
+			checkDescription:   "Checking if crc executable symlink exists",
+			check:              checkCrcSymlink,
+			fixDescription:     "Creating symlink for crc executable",
+			fix:                fixCrcSymlink,
+			cleanupDescription: "Removing crc executable symlink",
+			cleanup:            removeCrcSymlink,
+
 			labels: None,
 		},
 	}
@@ -186,3 +198,50 @@ func checkSupportedCPUArch() error {
 	}
 	return nil
 }
+
+func runtimeExecutablePath() (string, error) {
+	path, err := exec.LookPath(os.Args[0])
+	if err != nil {
+		// os.Args[0] is not in $PATH, crc must have been started by specifying the path to its binary
+		path = os.Args[0]
+	}
+	path, err = filepath.Abs(path)
+	if err != nil {
+		return "", err
+	}
+	return filepath.EvalSymlinks(path)
+}
+
+func checkCrcSymlink() error {
+	runtimePath, err := runtimeExecutablePath()
+	if err != nil {
+		return err
+	}
+	symlinkPath, err := filepath.EvalSymlinks(constants.CrcSymlinkPath)
+	if err != nil {
+		return err
+	}
+	if symlinkPath != runtimePath {
+		return fmt.Errorf("%s points to %s, not to %s", constants.CrcSymlinkPath, symlinkPath, runtimePath)
+	}
+
+	return nil
+}
+
+func fixCrcSymlink() error {
+	_ = os.Remove(constants.CrcSymlinkPath)
+
+	runtimePath, err := runtimeExecutablePath()
+	if err != nil {
+		return err
+	}
+	logging.Debugf("symlinking %s to %s", runtimePath, constants.CrcSymlinkPath)
+	return os.Symlink(runtimePath, constants.CrcSymlinkPath)
+}
+
+func removeCrcSymlink() error {
+	if crcos.FileExists(constants.CrcSymlinkPath) {
+		return os.Remove(constants.CrcSymlinkPath)
+	}
+	return nil
+}
diff --git a/pkg/crc/preflight/preflight_checks_windows.go b/pkg/crc/preflight/preflight_checks_windows.go
index b2f0d59e4..de9f52d04 100644
--- a/pkg/crc/preflight/preflight_checks_windows.go
+++ b/pkg/crc/preflight/preflight_checks_windows.go
@@ -183,3 +183,14 @@ func removeCrcVM() (err error) {
 	logging.Debug("'crc' VM is removed")
 	return nil
 }
+
+func checkIfAdminHelperServiceRunning() error {
+	stdout, stderr, err := powershell.Execute(fmt.Sprintf("(Get-Service %s).Status", constants.AdminHelperServiceName))
+	if err != nil {
+		return fmt.Errorf("%s service is not present %v: %s", constants.AdminHelperServiceName, err, stderr)
+	}
+	if strings.TrimSpace(stdout) != "Running" {
+		return fmt.Errorf("%s service is not running", constants.AdminHelperServiceName)
+	}
+	return nil
+}
diff --git a/pkg/crc/preflight/preflight_daemon_task_check_windows.go b/pkg/crc/preflight/preflight_daemon_task_check_windows.go
new file mode 100644
index 000000000..0c5a76833
--- /dev/null
+++ b/pkg/crc/preflight/preflight_daemon_task_check_windows.go
@@ -0,0 +1,159 @@
+package preflight
+
+import (
+	"bytes"
+	"encoding/xml"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/code-ready/crc/pkg/crc/constants"
+	"github.com/code-ready/crc/pkg/crc/daemonclient"
+	"github.com/code-ready/crc/pkg/crc/logging"
+	"github.com/code-ready/crc/pkg/crc/version"
+	"github.com/code-ready/crc/pkg/os/windows/powershell"
+)
+
+var (
+	// https://docs.microsoft.com/en-us/windows/win32/taskschd/daily-trigger-example--xml-
+	daemonTaskTemplate = `<?xml version="1.0" encoding="UTF-16"?>
+<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
+  <RegistrationInfo>
+    <Description>Run crc daemon as a task</Description>
+    <Version>%s</Version>
+  </RegistrationInfo>
+  <Settings>
+    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
+    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
+    <Hidden>true</Hidden>
+    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
+    <IdleSettings>
+      <Duration>PT10M</Duration>
+      <WaitTimeout>PT1H</WaitTimeout>
+      <StopOnIdleEnd>true</StopOnIdleEnd>
+      <RestartOnIdle>false</RestartOnIdle>
+    </IdleSettings>
+    <UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
+  </Settings>
+  <Triggers />
+  <Actions Context="Author">
+    <Exec>
+      <Command>powershell.exe</Command>
+      <Arguments>-WindowStyle Hidden -Command %s</Arguments>
+    </Exec>
+  </Actions>
+</Task>
+`
+)
+
+func genDaemonTaskInstallTemplate(crcVersion, daemonCommand string) (string, error) {
+	var escapedName bytes.Buffer
+	if err := xml.EscapeText(&escapedName, []byte(daemonCommand)); err != nil {
+		return "", err
+	}
+
+	return fmt.Sprintf(daemonTaskTemplate,
+		crcVersion,
+		escapedName.String(),
+	), nil
+}
+
+func checkIfDaemonTaskInstalled() error {
+	_, stderr, err := powershell.Execute("Get-ScheduledTask", "-TaskName", constants.DaemonTaskName)
+	if err != nil {
+		logging.Debugf("%s task is not installed: %v : %s", constants.DaemonTaskName, err, stderr)
+		return err
+	}
+	if err := checkIfOlderTask(); err != nil {
+		return err
+	}
+	return nil
+}
+
+func fixDaemonTaskInstalled() error {
+	// prepare the task script
+	binPath, err := os.Executable()
+	if err != nil {
+		return fmt.Errorf("unable to find the current executable location: %v", err)
+	}
+	binPathWithArgs := fmt.Sprintf("& '%s' daemon", binPath)
+
+	taskContent, err := genDaemonTaskInstallTemplate(
+		version.GetCRCVersion(),
+		binPathWithArgs,
+	)
+	if err != nil {
+		return err
+	}
+
+	if _, stderr, err := powershell.Execute("Register-ScheduledTask", "-Xml", fmt.Sprintf(`'%s'`, taskContent), "-TaskName", constants.DaemonTaskName); err != nil {
+		return fmt.Errorf("failed to register %s task, %v: %s", constants.DaemonTaskName, err, stderr)
+	}
+
+	return nil
+}
+
+func removeDaemonTask() error {
+	if err := checkIfDaemonTaskRunning(); err == nil {
+		_, stderr, err := powershell.Execute("Stop-ScheduledTask", "-TaskName", constants.DaemonTaskName)
+		if err != nil {
+			logging.Debugf("unable to stop the %s task: %v : %s", constants.DaemonTaskName, err, stderr)
+			return err
+		}
+	}
+	if err := checkIfDaemonTaskInstalled(); err == nil {
+		_, stderr, err := powershell.Execute("Unregister-ScheduledTask", "-TaskName", constants.DaemonTaskName, "-Confirm:$false")
+		if err != nil {
+			logging.Debugf("unable to unregister the %s task: %v : %s", constants.DaemonTaskName, err, stderr)
+			return err
+		}
+	}
+	return nil
+}
+
+func checkIfDaemonTaskRunning() error {
+	if isDaemonRunningWithReleasedVersion() {
+		return nil
+	}
+	stdout, stderr, err := powershell.Execute(fmt.Sprintf(`(Get-ScheduledTask -TaskName "%s").State`, constants.DaemonTaskName))
+	if err != nil {
+		logging.Debugf("%s task is not running: %v : %s", constants.DaemonTaskName, err, stderr)
+		return err
+	}
+	if strings.TrimSpace(stdout) != "Running" {
+		return fmt.Errorf("expected %s task to be in 'Running' but got '%s'", constants.DaemonTaskName, stdout)
+	}
+	return nil
+}
+
+func fixDaemonTaskRunning() error {
+	_, stderr, err := powershell.Execute("Start-ScheduledTask", "-TaskName", constants.DaemonTaskName)
+	if err != nil {
+		logging.Debugf("unable to run the %s task: %v : %s", constants.DaemonTaskName, err, stderr)
+		return err
+	}
+	return nil
+}
+
+func checkIfOlderTask() error {
+	stdout, stderr, err := powershell.Execute(fmt.Sprintf(`(Get-ScheduledTask -TaskName "%s").Version`, constants.DaemonTaskName))
+	if err != nil {
+		return fmt.Errorf("%s task is not running: %v : %s", constants.DaemonTaskName, err, stderr)
+	}
+	if strings.TrimSpace(stdout) != version.GetCRCVersion() {
+		return fmt.Errorf("expected %s task to be on version '%s' but got '%s'", constants.DaemonTaskName, version.GetCRCVersion(), stdout)
+	}
+	return nil
+}
+
+func isDaemonRunningWithReleasedVersion() bool {
+	ver, err := daemonclient.New().APIClient.Version()
+	if err != nil {
+		return false
+	}
+	if ver.CrcVersion != version.GetCRCVersion() {
+		logging.Debugf("Daemon is running with %s version but binary version is %s", ver.CrcVersion, version.GetCRCVersion())
+		return false
+	}
+	return true
+}
diff --git a/pkg/crc/preflight/preflight_darwin.go b/pkg/crc/preflight/preflight_darwin.go
index 3a60eed97..d94b7e1c0 100644
--- a/pkg/crc/preflight/preflight_darwin.go
+++ b/pkg/crc/preflight/preflight_darwin.go
@@ -99,6 +99,18 @@ var resolverPreflightChecks = []Check{
 	},
 }
 
+var daemonLaunchdChecks = []Check{
+	{
+		configKeySuffix:    "check-daemon-launchd-plist",
+		checkDescription:   "Checking if crc daemon plist file is present and loaded",
+		check:              checkIfDaemonPlistFileExists,
+		fixDescription:     "Adding crc daemon plist file and loading it",
+		fix:                fixDaemonPlistFileExists,
+		cleanupDescription: "Unloading and removing the daemon plist file",
+		cleanup:            removeDaemonPlistFile,
+	},
+}
+
 // We want all preflight checks including
 // - experimental checks
 // - tray checks when using an installer, regardless of tray enabled or not
@@ -120,6 +132,7 @@ func getChecks(mode network.Mode, bundlePath string, preset crcpreset.Preset) []
 	checks = append(checks, resolverPreflightChecks...)
 	checks = append(checks, bundleCheck(bundlePath, preset))
 	checks = append(checks, trayLaunchdCleanupChecks...)
+	checks = append(checks, daemonLaunchdChecks...)
 
 	return checks
 }
diff --git a/pkg/crc/preflight/preflight_linux.go b/pkg/crc/preflight/preflight_linux.go
index e238e65d7..80d8f40f3 100644
--- a/pkg/crc/preflight/preflight_linux.go
+++ b/pkg/crc/preflight/preflight_linux.go
@@ -20,17 +20,6 @@ import (
 
 func libvirtPreflightChecks(distro *linux.OsRelease) []Check {
 	checks := []Check{
-		{
-			configKeySuffix:    "check-crc-symlink",
-			checkDescription:   "Checking if crc executable symlink exists",
-			check:              checkCrcSymlink,
-			fixDescription:     "Creating symlink for crc executable",
-			fix:                fixCrcSymlink,
-			cleanupDescription: "Removing crc executable symlink",
-			cleanup:            removeCrcSymlink,
-
-			labels: labels{Os: Linux},
-		},
 		{
 			configKeySuffix:  "check-virt-enabled",
 			checkDescription: "Checking if Virtualization is enabled",
diff --git a/pkg/crc/preflight/preflight_windows.go b/pkg/crc/preflight/preflight_windows.go
index f3a01a121..2bf47b4d7 100644
--- a/pkg/crc/preflight/preflight_windows.go
+++ b/pkg/crc/preflight/preflight_windows.go
@@ -83,14 +83,14 @@ var hypervPreflightChecks = []Check{
 		check:            checkIfHyperVVirtualSwitchExists,
 		flags:            StartUpOnly,
 
-		labels: labels{Os: Windows},
+		labels: labels{Os: Windows, NetworkMode: System},
 	},
 	{
 		cleanupDescription: "Removing dns server from interface",
 		cleanup:            removeDNSServerAddress,
 		flags:              CleanUpOnly,
 
-		labels: labels{Os: Windows},
+		labels: labels{Os: Windows, NetworkMode: System},
 	},
 	{
 		cleanupDescription: "Removing crc's virtual machine",
@@ -112,6 +112,41 @@ var vsockChecks = []Check{
 	},
 }
 
+var daemonTaskChecks = []Check{
+	{
+		configKeySuffix:    "check-daemon-task-install",
+		checkDescription:   "Checking if daemon task is installed",
+		check:              checkIfDaemonTaskInstalled,
+		fixDescription:     "Installing the daemon task",
+		fix:                fixDaemonTaskInstalled,
+		cleanupDescription: "Removing daemon task",
+		cleanup:            removeDaemonTask,
+
+		labels: labels{Os: Windows},
+	},
+	{
+		configKeySuffix:  "check-daemon-task-running",
+		checkDescription: "Checking if daemon task is running",
+		check:            checkIfDaemonTaskRunning,
+		fixDescription:   "Running the daemon task",
+		fix:              fixDaemonTaskRunning,
+
+		labels: labels{Os: Windows},
+	},
+}
+
+var adminHelperServiceCheks = []Check{
+	{
+		configKeySuffix:  "check-admin-helper-service-running",
+		checkDescription: "Checking admin helper service is running",
+		check:            checkIfAdminHelperServiceRunning,
+		fixDescription:   "Make sure you installed the crc using installer",
+		flags:            NoFix,
+
+		labels: labels{Os: Windows},
+	},
+}
+
 var errReboot = errors.New("Please reboot your system and run 'crc setup' to complete the setup process")
 
 func username() string {
@@ -157,6 +192,8 @@ func getChecks(bundlePath string, preset crcpreset.Preset) []Check {
 	checks = append(checks, vsockChecks...)
 	checks = append(checks, bundleCheck(bundlePath, preset))
 	checks = append(checks, genericCleanupChecks...)
+	checks = append(checks, daemonTaskChecks...)
+	checks = append(checks, adminHelperServiceCheks...)
 	return checks
 }
 
diff --git a/pkg/os/launchd/launchd_darwin.go b/pkg/os/launchd/launchd_darwin.go
index 96370fc70..3d12fca79 100644
--- a/pkg/os/launchd/launchd_darwin.go
+++ b/pkg/os/launchd/launchd_darwin.go
@@ -18,28 +18,32 @@ import (
 
 const (
 	plistTemplate = `<?xml version='1.0' encoding='UTF-8'?>
-	<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-	<plist version='1.0'>
-		<dict>
-			<key>Label</key>
-			<string>{{ .Label }}</string>
-			<key>ProgramArguments</key>
-			<array>
-				<string>{{ .ExecutablePath }}</string>
-			{{ range .Args }}
-				<string>{{ . }}</string>
-			{{ end }}
-			</array>
-			<key>StandardOutPath</key>
-			<string>{{ .StdOutFilePath }}</string>
-			<key>Disabled</key>
-			<false/>
-			<key>RunAtLoad</key>
-			<true/>
-			<key>ProcessType</key>
-			<string>Interactive</string>
-		</dict>
-	</plist>`
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version='1.0'>
+<dict>
+	<key>Label</key>
+	<string>{{ .Label }}</string>
+	<key>ProgramArguments</key>
+	<array>
+		<string>{{ .ExecutablePath }}</string>
+	{{ range .Args }}
+		<string>{{ . }}</string>
+	{{ end }}
+	</array>
+	<key>StandardOutPath</key>
+	<string>{{ .StdOutFilePath }}</string>
+	<key>StandardErrorPath</key>
+	<string>{{ .StdErrFilePath }}</string>
+	<key>EnvironmentVariables</key>
+	<dict>
+		{{ range $key, $value := .Env }}
+		<key>{{ $key }}</key>
+		<string>{{ $value }}</string>
+		{{ end }}
+	</dict>
+</dict>
+</plist>
+`
 )
 
 // AgentConfig is struct to contain configuration for agent plist file
@@ -47,7 +51,9 @@ type AgentConfig struct {
 	Label          string
 	ExecutablePath string
 	StdOutFilePath string
+	StdErrFilePath string
 	Args           []string
+	Env            map[string]string
 }
 
 var (


Test patch:
diff --git a/pkg/crc/preflight/preflight_darwin_test.go b/pkg/crc/preflight/preflight_darwin_test.go
index 9563fc66e..e53920684 100644
--- a/pkg/crc/preflight/preflight_darwin_test.go
+++ b/pkg/crc/preflight/preflight_darwin_test.go
@@ -13,13 +13,13 @@ import (
 func TestCountConfigurationOptions(t *testing.T) {
 	cfg := config.New(config.NewEmptyInMemoryStorage())
 	RegisterSettings(cfg)
-	assert.Len(t, cfg.AllConfigs(), 12)
+	assert.Len(t, cfg.AllConfigs(), 14)
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
-	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
+	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 19)
+	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 19)
 
-	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
-	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
+	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18)
+	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18)
 }
diff --git a/pkg/crc/preflight/preflight_linux_test.go b/pkg/crc/preflight/preflight_linux_test.go
index f2e95011e..7c312a910 100644
--- a/pkg/crc/preflight/preflight_linux_test.go
+++ b/pkg/crc/preflight/preflight_linux_test.go
@@ -70,11 +70,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -109,11 +109,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -147,11 +147,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -179,11 +179,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -218,11 +218,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -256,11 +256,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -288,11 +288,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -327,11 +327,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -365,11 +365,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -397,11 +397,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -437,11 +437,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
@@ -476,11 +476,11 @@ var checkListForDistros = []checkListForDistro{
 			{check: checkOldAdminHelperExecutableCached},
 			{check: checkSupportedCPUArch},
 			{configKeySuffix: "check-ram"},
+			{check: checkCrcSymlink},
 			{cleanup: removeCRCMachinesDir},
 			{cleanup: removeOldLogs},
 			{cleanup: cluster.ForgetPullSecret},
 			{cleanup: removeHostsFileEntry},
-			{check: checkCrcSymlink},
 			{check: checkVirtualizationEnabled},
 			{check: checkKvmEnabled},
 			{check: checkLibvirtInstalled},
diff --git a/pkg/crc/preflight/preflight_windows_test.go b/pkg/crc/preflight/preflight_windows_test.go
index b4c745aae..34413b368 100644
--- a/pkg/crc/preflight/preflight_windows_test.go
+++ b/pkg/crc/preflight/preflight_windows_test.go
@@ -13,13 +13,13 @@ import (
 func TestCountConfigurationOptions(t *testing.T) {
 	cfg := config.New(config.NewEmptyInMemoryStorage())
 	RegisterSettings(cfg)
-	assert.Len(t, cfg.AllConfigs(), 10)
+	assert.Len(t, cfg.AllConfigs(), 12)
 }
 
 func TestCountPreflights(t *testing.T) {
-	assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 15)
-	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 15)
+	assert.Len(t, getPreflightChecks(false, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18)
+	assert.Len(t, getPreflightChecks(true, network.SystemNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 18)
 
-	assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
-	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 16)
+	assert.Len(t, getPreflightChecks(false, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
+	assert.Len(t, getPreflightChecks(true, network.UserNetworkingMode, constants.GetDefaultBundlePath(preset.OpenShift), preset.OpenShift), 17)
 }