diff --git a/cmd/flux/create_tenant.go b/cmd/flux/create_tenant.go
index c3031dd82d82efa031b91163a50b25119581f45d..ee8dde41bc1bc4ed4118bdcba544812c682975ee 100644
--- a/cmd/flux/create_tenant.go
+++ b/cmd/flux/create_tenant.go
@@ -38,7 +38,7 @@ var createTenantCmd = &cobra.Command{
 	Use:   "tenant",
 	Short: "Create or update a tenant",
 	Long: `
-The create tenant command generates namespaces and role bindings to limit the
+The create tenant command generates namespaces, service accounts and role bindings to limit the
 reconcilers scope to the tenant namespaces.`,
 	Example: `  # Create a tenant with access to a namespace 
   flux create tenant dev-team \
@@ -65,7 +65,6 @@ var (
 )
 
 func init() {
-	createTenantCmd.Hidden = true
 	createTenantCmd.Flags().StringSliceVar(&tenantNamespaces, "with-namespace", nil, "namespace belonging to this tenant")
 	createTenantCmd.Flags().StringVar(&tenantClusterRole, "cluster-role", "cluster-admin", "cluster role of the tenant role binding")
 	createCmd.AddCommand(createTenantCmd)
@@ -89,6 +88,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 	}
 
 	var namespaces []corev1.Namespace
+	var accounts []corev1.ServiceAccount
 	var roleBindings []rbacv1.RoleBinding
 
 	for _, ns := range tenantNamespaces {
@@ -111,6 +111,16 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 		}
 		namespaces = append(namespaces, namespace)
 
+		account := corev1.ServiceAccount{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      tenant,
+				Namespace: ns,
+				Labels:    objLabels,
+			},
+		}
+
+		accounts = append(accounts, account)
+
 		roleBinding := rbacv1.RoleBinding{
 			ObjectMeta: metav1.ObjectMeta{
 				Name:      tenantRoleBinding,
@@ -119,9 +129,12 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 			},
 			Subjects: []rbacv1.Subject{
 				{
-					APIGroup: "rbac.authorization.k8s.io",
-					Kind:     "User",
-					Name:     fmt.Sprintf("gotk:%s:reconciler", ns),
+					Kind: "User",
+					Name: fmt.Sprintf("gotk:%s:reconciler", ns),
+				},
+				{
+					Kind: "ServiceAccount",
+					Name: tenant,
 				},
 			},
 			RoleRef: rbacv1.RoleRef{
@@ -135,7 +148,7 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 
 	if export {
 		for i, _ := range tenantNamespaces {
-			if err := exportTenant(namespaces[i], roleBindings[i]); err != nil {
+			if err := exportTenant(namespaces[i], accounts[i], roleBindings[i]); err != nil {
 				return err
 			}
 		}
@@ -156,6 +169,11 @@ func createTenantCmdRun(cmd *cobra.Command, args []string) error {
 			return err
 		}
 
+		logger.Actionf("applying service account %s", accounts[i].Name)
+		if err := upsertServiceAccount(ctx, kubeClient, accounts[i]); err != nil {
+			return err
+		}
+
 		logger.Actionf("applying role binding %s", roleBindings[i].Name)
 		if err := upsertRoleBinding(ctx, kubeClient, roleBindings[i]); err != nil {
 			return err
@@ -195,6 +213,35 @@ func upsertNamespace(ctx context.Context, kubeClient client.Client, namespace co
 	return nil
 }
 
+func upsertServiceAccount(ctx context.Context, kubeClient client.Client, account corev1.ServiceAccount) error {
+	namespacedName := types.NamespacedName{
+		Namespace: account.GetNamespace(),
+		Name:      account.GetName(),
+	}
+
+	var existing corev1.ServiceAccount
+	err := kubeClient.Get(ctx, namespacedName, &existing)
+	if err != nil {
+		if errors.IsNotFound(err) {
+			if err := kubeClient.Create(ctx, &account); err != nil {
+				return err
+			} else {
+				return nil
+			}
+		}
+		return err
+	}
+
+	if !equality.Semantic.DeepDerivative(account.Labels, existing.Labels) {
+		existing.Labels = account.Labels
+		if err := kubeClient.Update(ctx, &existing); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
 func upsertRoleBinding(ctx context.Context, kubeClient client.Client, roleBinding rbacv1.RoleBinding) error {
 	namespacedName := types.NamespacedName{
 		Namespace: roleBinding.GetNamespace(),
@@ -228,7 +275,7 @@ func upsertRoleBinding(ctx context.Context, kubeClient client.Client, roleBindin
 	return nil
 }
 
-func exportTenant(namespace corev1.Namespace, roleBinding rbacv1.RoleBinding) error {
+func exportTenant(namespace corev1.Namespace, account corev1.ServiceAccount, roleBinding rbacv1.RoleBinding) error {
 	namespace.TypeMeta = metav1.TypeMeta{
 		APIVersion: "v1",
 		Kind:       "Namespace",
@@ -242,6 +289,19 @@ func exportTenant(namespace corev1.Namespace, roleBinding rbacv1.RoleBinding) er
 	data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
 	fmt.Println(resourceToString(data))
 
+	account.TypeMeta = metav1.TypeMeta{
+		APIVersion: "",
+		Kind:       "ServiceAccount",
+	}
+	data, err = yaml.Marshal(account)
+	if err != nil {
+		return err
+	}
+
+	fmt.Println("---")
+	data = bytes.Replace(data, []byte("spec: {}\n"), []byte(""), 1)
+	fmt.Println(resourceToString(data))
+
 	roleBinding.TypeMeta = metav1.TypeMeta{
 		APIVersion: "rbac.authorization.k8s.io/v1",
 		Kind:       "RoleBinding",
diff --git a/docs/cmd/flux_create.md b/docs/cmd/flux_create.md
index 48acbae77c7ca94c51f71640e2253d5705d73be2..0652230098bc43eed5c17a0737b006bf56691188 100644
--- a/docs/cmd/flux_create.md
+++ b/docs/cmd/flux_create.md
@@ -34,4 +34,5 @@ The create sub-commands generate sources and resources.
 * [flux create kustomization](flux_create_kustomization.md)	 - Create or update a Kustomization resource
 * [flux create receiver](flux_create_receiver.md)	 - Create or update a Receiver resource
 * [flux create source](flux_create_source.md)	 - Create or update sources
+* [flux create tenant](flux_create_tenant.md)	 - Create or update a tenant
 
diff --git a/docs/cmd/flux_create_tenant.md b/docs/cmd/flux_create_tenant.md
new file mode 100644
index 0000000000000000000000000000000000000000..7e61e9abac7e87c5d0cdda063a852a678968ca82
--- /dev/null
+++ b/docs/cmd/flux_create_tenant.md
@@ -0,0 +1,55 @@
+## flux create tenant
+
+Create or update a tenant
+
+### Synopsis
+
+
+The create tenant command generates namespaces, service accounts and role bindings to limit the
+reconcilers scope to the tenant namespaces.
+
+```
+flux create tenant [flags]
+```
+
+### Examples
+
+```
+  # Create a tenant with access to a namespace 
+  flux create tenant dev-team \
+    --with-namespace=frontend \
+    --label=environment=dev
+
+  # Generate tenant namespaces and role bindings in YAML format
+  flux create tenant dev-team \
+    --with-namespace=frontend \
+    --with-namespace=backend \
+	--export > dev-team.yaml
+
+```
+
+### Options
+
+```
+      --cluster-role string      cluster role of the tenant role binding (default "cluster-admin")
+  -h, --help                     help for tenant
+      --with-namespace strings   namespace belonging to this tenant
+```
+
+### Options inherited from parent commands
+
+```
+      --context string      kubernetes context to use
+      --export              export in YAML format to stdout
+      --interval duration   source sync interval (default 1m0s)
+      --kubeconfig string   path to the kubeconfig file (default "~/.kube/config")
+      --label strings       set labels on the resource (can specify multiple labels with commas: label1=value1,label2=value2)
+  -n, --namespace string    the namespace scope for this operation (default "flux-system")
+      --timeout duration    timeout for this operation (default 5m0s)
+      --verbose             print generated objects
+```
+
+### SEE ALSO
+
+* [flux create](flux_create.md)	 - Create or update sources and resources
+
diff --git a/mkdocs.yml b/mkdocs.yml
index 9c4cafd008f2d5e4196c2c4dc63ff39a4422aa0b..a9771d90eae7c64fa462066c5d3d0b4ddd9509f3 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -95,7 +95,7 @@ nav:
     - Create alert provider: cmd/flux_create_alert-provider.md
     - Create alert: cmd/flux_create_alert.md
     - Create receiver: cmd/flux_create_receiver.md
-    #- Create tenant: cmd/flux_create_tenant.md
+    - Create tenant: cmd/flux_create_tenant.md
     - Delete: cmd/flux_delete.md
     - Delete kustomization: cmd/flux_delete_kustomization.md
     - Delete helmrelease: cmd/flux_delete_helmrelease.md