diff --git a/cmd/flux/create_helmrelease.go b/cmd/flux/create_helmrelease.go
index a62c0027ad141288867a6151cfa5c8de1bb0f957..5f78386d4ce4ec91fb636bd486e4c12b7de6b53d 100644
--- a/cmd/flux/create_helmrelease.go
+++ b/cmd/flux/create_helmrelease.go
@@ -116,6 +116,7 @@ type helmReleaseFlags struct {
 	valuesFiles     []string
 	valuesFrom      flags.HelmReleaseValuesFrom
 	saName          string
+	crds            flags.CRDsPolicy
 }
 
 var helmReleaseArgs helmReleaseFlags
@@ -130,6 +131,7 @@ func init() {
 	createHelmReleaseCmd.Flags().StringVar(&helmReleaseArgs.saName, "service-account", "", "the name of the service account to impersonate when reconciling this HelmRelease")
 	createHelmReleaseCmd.Flags().StringArrayVar(&helmReleaseArgs.valuesFiles, "values", nil, "local path to values.yaml files")
 	createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.valuesFrom, "values-from", helmReleaseArgs.valuesFrom.Description())
+	createHelmReleaseCmd.Flags().Var(&helmReleaseArgs.crds, "crds", helmReleaseArgs.crds.Description())
 	createCmd.AddCommand(createHelmReleaseCmd)
 }
 
@@ -184,6 +186,11 @@ func createHelmReleaseCmdRun(cmd *cobra.Command, args []string) error {
 		helmRelease.Spec.ServiceAccountName = helmReleaseArgs.saName
 	}
 
+	if helmReleaseArgs.crds != "" {
+		helmRelease.Spec.Install = &helmv2.Install{CRDs: helmv2.Create}
+		helmRelease.Spec.Upgrade = &helmv2.Upgrade{CRDs: helmv2.CRDsPolicy(helmReleaseArgs.crds.String())}
+	}
+
 	if len(helmReleaseArgs.valuesFiles) > 0 {
 		valuesMap := make(map[string]interface{})
 		for _, v := range helmReleaseArgs.valuesFiles {
diff --git a/docs/cmd/flux_create_helmrelease.md b/docs/cmd/flux_create_helmrelease.md
index 38792c5c4fc4594dd6a3ce7138d298f74673adfe..15f7b1f34fddc344032fd8655af2d9ac3d8c440f 100644
--- a/docs/cmd/flux_create_helmrelease.md
+++ b/docs/cmd/flux_create_helmrelease.md
@@ -81,6 +81,7 @@ flux create helmrelease [name] [flags]
 ```
       --chart string                        Helm chart name or path
       --chart-version string                Helm chart version, accepts a semver range (ignored for charts from GitRepository sources)
+      --crds crds                           upgrade CRDs policy, available options are: (Skip, Create, CreateReplace)
       --depends-on stringArray              HelmReleases that must be ready before this release can be installed, supported formats '<name>' and '<namespace>/<name>'
   -h, --help                                help for helmrelease
       --release-name string                 name used for the Helm release, defaults to a composition of '[<target-namespace>-]<HelmRelease-name>'
diff --git a/docs/guides/sealed-secrets.md b/docs/guides/sealed-secrets.md
index 81dd69b88bd28cbb4be00af4839928d67ed6af8c..56469067e922c155aabcfa1b75628b03971fe1b2 100644
--- a/docs/guides/sealed-secrets.md
+++ b/docs/guides/sealed-secrets.md
@@ -52,11 +52,12 @@ flux create helmrelease sealed-secrets \
 --target-namespace=flux-system \
 --source=HelmRepository/sealed-secrets \
 --chart=sealed-secrets \
---chart-version="1.13.x"
+--chart-version=">=1.15.0-0" \
+--crds=CreateReplace
 ```
 
-With chart version `1.13.x` we configure helm-controller to automatically upgrade the release
-when a new chart patch version is fetched by source-controller.
+With chart version `>=1.15.0-0` we configure helm-controller to automatically upgrade the release
+when a new chart version is fetched by source-controller.
 
 At startup, the sealed-secrets controller generates a 4096-bit RSA key pair and 
 persists the private and public keys as Kubernetes secrets in the `flux-system` namespace.
@@ -119,11 +120,11 @@ Helm repository manifest:
 apiVersion: source.toolkit.fluxcd.io/v1beta1
 kind: HelmRepository
 metadata:
-  name: stable
+  name: sealed-secrets
   namespace: flux-system
 spec:
   interval: 1h0m0s
-  url: https://charts.helm.sh/stable
+  url: https://bitnami-labs.github.io/sealed-secrets
 ```
 
 Helm release manifest:
@@ -140,11 +141,15 @@ spec:
       chart: sealed-secrets
       sourceRef:
         kind: HelmRepository
-        name: stable
-      version: "1.13.x"
+        name: sealed-secrets
+      version: ">=1.15.0-0"
   interval: 1h0m0s
   releaseName: sealed-secrets
   targetNamespace: flux-system
+  install:
+    crds: Create
+  upgrade:
+    crds: CreateReplace
 ```
 
 !!! hint
diff --git a/internal/flags/crds.go b/internal/flags/crds.go
new file mode 100644
index 0000000000000000000000000000000000000000..47e05e4f94e5d2cc92c4b891cb586f5f52e7e67e
--- /dev/null
+++ b/internal/flags/crds.go
@@ -0,0 +1,60 @@
+/*
+Copyright 2021 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"fmt"
+	"strings"
+
+	helmv2 "github.com/fluxcd/helm-controller/api/v2beta1"
+
+	"github.com/fluxcd/flux2/internal/utils"
+)
+
+var supportedCRDsPolicies = []string{
+	string(helmv2.Skip),
+	string(helmv2.Create),
+	string(helmv2.CreateReplace),
+}
+
+type CRDsPolicy string
+
+func (a *CRDsPolicy) String() string {
+	return string(*a)
+}
+
+func (a *CRDsPolicy) Set(str string) error {
+	if strings.TrimSpace(str) == "" {
+		return fmt.Errorf("no upgrade CRDs policy given, must be one of: %s",
+			strings.Join(supportedCRDsPolicies, ", "))
+	}
+	if !utils.ContainsItemString(supportedCRDsPolicies, str) {
+		return fmt.Errorf("unsupported upgrade CRDs policy '%s', must be one of: %s",
+			str, strings.Join(supportedCRDsPolicies, ", "))
+
+	}
+	*a = CRDsPolicy(str)
+	return nil
+}
+
+func (a *CRDsPolicy) Type() string {
+	return "crds"
+}
+
+func (a *CRDsPolicy) Description() string {
+	return fmt.Sprintf("upgrade CRDs policy, available options are: (%s)", strings.Join(supportedCRDsPolicies, ", "))
+}
diff --git a/internal/flags/crds_test.go b/internal/flags/crds_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..be53a4615e7ffaed288ce0821a36836ae6439a58
--- /dev/null
+++ b/internal/flags/crds_test.go
@@ -0,0 +1,45 @@
+/*
+Copyright 2021 The Flux authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package flags
+
+import (
+	"testing"
+)
+
+func TestCRDsPolicy_Set(t *testing.T) {
+	tests := []struct {
+		name      string
+		str       string
+		expect    string
+		expectErr bool
+	}{
+		{"supported", "CreateReplace", "CreateReplace", false},
+		{"unsupported", "createreplace", "", true},
+		{"empty", "", "", true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var a CRDsPolicy
+			if err := a.Set(tt.str); (err != nil) != tt.expectErr {
+				t.Errorf("Set() error = %v, expectErr %v", err, tt.expectErr)
+			}
+			if str := a.String(); str != tt.expect {
+				t.Errorf("Set() = %v, expect %v", str, tt.expect)
+			}
+		})
+	}
+}