diff --git a/cmd/flux/install_test.go b/cmd/flux/install_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..a02512064ceb2b8c24a130376aa5aa920ef6bde3
--- /dev/null
+++ b/cmd/flux/install_test.go
@@ -0,0 +1,53 @@
+/*
+Copyright 2022 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 main
+
+import "testing"
+
+func TestInstall(t *testing.T) {
+	// The pointer to kubeconfigArgs.Namespace is shared across
+	// the tests. When a new value is set, it will linger and
+	// impact subsequent tests.
+	// Given that this test uses an invalid namespace, it ensures
+	// to restore whatever value it had previously.
+	currentNamespace := *kubeconfigArgs.Namespace
+	defer func() {
+		*kubeconfigArgs.Namespace = currentNamespace
+	}()
+
+	tests := []struct {
+		name   string
+		args   string
+		assert assertFunc
+	}{
+		{
+			name:   "invalid namespace",
+			args:   "install --namespace='@#[]'",
+			assert: assertError("namespace must be a valid DNS label: \"@#[]\""),
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			cmd := cmdTestCase{
+				args:   tt.args,
+				assert: tt.assert,
+			}
+			cmd.runTestCmd(t)
+		})
+	}
+}
diff --git a/cmd/flux/main.go b/cmd/flux/main.go
index 010ca7adac8c85be0872fd59e8ccd13f1910c590..cc3d95535997a689f87cc65943b23be7e4403928 100644
--- a/cmd/flux/main.go
+++ b/cmd/flux/main.go
@@ -27,6 +27,7 @@ import (
 	"github.com/spf13/cobra"
 	"golang.org/x/term"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/util/validation"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
 	_ "k8s.io/client-go/plugin/pkg/client/auth"
 
@@ -96,6 +97,18 @@ Command line utility for assembling Kubernetes CD pipelines the GitOps way.`,
 
   # Uninstall Flux and delete CRDs
   flux uninstall`,
+	PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
+		ns, err := cmd.Flags().GetString("namespace")
+		if err != nil {
+			return fmt.Errorf("error getting namespace: %w", err)
+		}
+
+		if e := validation.IsDNS1123Label(ns); len(e) > 0 {
+			return fmt.Errorf("namespace must be a valid DNS label: %q", ns)
+		}
+
+		return nil
+	},
 }
 
 var logger = stderrLogger{stderr: os.Stderr}
@@ -178,6 +191,14 @@ func configureDefaultNamespace() {
 	*kubeconfigArgs.Namespace = rootArgs.defaults.Namespace
 	fromEnv := os.Getenv("FLUX_SYSTEM_NAMESPACE")
 	if fromEnv != "" {
+		// namespace must be a valid DNS label. Assess against validation
+		// used upstream, and ignore invalid values as environment vars
+		// may not be actively provided by end-user.
+		if e := validation.IsDNS1123Label(fromEnv); len(e) > 0 {
+			logger.Warningf(" ignoring invalid FLUX_SYSTEM_NAMESPACE: %q", fromEnv)
+			return
+		}
+
 		kubeconfigArgs.Namespace = &fromEnv
 	}
 }