diff --git a/PROJECT b/PROJECT
index 73558cf8033d1c0cc7181c1361e269643f2471a2..c4c87a0bc7ae5dd78feaac2be7433ac6f8a2f574 100644
--- a/PROJECT
+++ b/PROJECT
@@ -26,4 +26,13 @@ resources:
   kind: RocketChat
   path: libre.sh/controller/apis/apps/v1alpha1
   version: v1alpha1
+- api:
+    crdVersion: v1
+    namespaced: true
+  controller: true
+  domain: libre.sh
+  group: apps
+  kind: Realm
+  path: libre.sh/controller/apis/apps/v1alpha1
+  version: v1alpha1
 version: "3"
diff --git a/apis/apps/v1alpha1/realm_types.go b/apis/apps/v1alpha1/realm_types.go
new file mode 100644
index 0000000000000000000000000000000000000000..6f00e206c06dc4e268bd93e5882e6c1cdb6acc76
--- /dev/null
+++ b/apis/apps/v1alpha1/realm_types.go
@@ -0,0 +1,48 @@
+package v1alpha1
+
+import (
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// EDIT THIS FILE!  THIS IS SCAFFOLDING FOR YOU TO OWN!
+// NOTE: json tags are required.  Any new fields you add must have json tags for the fields to be serialized.
+
+// RealmSpec defines the desired state of Realm
+type RealmSpec struct {
+	// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+
+	// Foo is an example field of Realm. Edit realm_types.go to remove/update
+	Foo string `json:"foo,omitempty"`
+}
+
+// RealmStatus defines the observed state of Realm
+type RealmStatus struct {
+	// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
+	// Important: Run "make" to regenerate code after modifying this file
+}
+
+//+kubebuilder:object:root=true
+//+kubebuilder:subresource:status
+
+// Realm is the Schema for the realms API
+type Realm struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   RealmSpec   `json:"spec,omitempty"`
+	Status RealmStatus `json:"status,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+
+// RealmList contains a list of Realm
+type RealmList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []Realm `json:"items"`
+}
+
+func init() {
+	SchemeBuilder.Register(&Realm{}, &RealmList{})
+}
diff --git a/apis/apps/v1alpha1/zz_generated.deepcopy.go b/apis/apps/v1alpha1/zz_generated.deepcopy.go
index 502d306f07cd08fd232c612a9694fb5f3dbdc291..df2acf97b38a85980c06fe453951b23e740f727a 100644
--- a/apis/apps/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/apps/v1alpha1/zz_generated.deepcopy.go
@@ -1,8 +1,6 @@
 //go:build !ignore_autogenerated
 // +build !ignore_autogenerated
 
-
-
 // Code generated by controller-gen. DO NOT EDIT.
 
 package v1alpha1
@@ -100,6 +98,95 @@ func (in *NextcloudStatus) DeepCopy() *NextcloudStatus {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Realm) DeepCopyInto(out *Realm) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	out.Spec = in.Spec
+	out.Status = in.Status
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Realm.
+func (in *Realm) DeepCopy() *Realm {
+	if in == nil {
+		return nil
+	}
+	out := new(Realm)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *Realm) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RealmList) DeepCopyInto(out *RealmList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]Realm, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmList.
+func (in *RealmList) DeepCopy() *RealmList {
+	if in == nil {
+		return nil
+	}
+	out := new(RealmList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *RealmList) DeepCopyObject() runtime.Object {
+	if c := in.DeepCopy(); c != nil {
+		return c
+	}
+	return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RealmSpec) DeepCopyInto(out *RealmSpec) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmSpec.
+func (in *RealmSpec) DeepCopy() *RealmSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(RealmSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RealmStatus) DeepCopyInto(out *RealmStatus) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RealmStatus.
+func (in *RealmStatus) DeepCopy() *RealmStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(RealmStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *RocketChat) DeepCopyInto(out *RocketChat) {
 	*out = *in
diff --git a/config/crd/bases/apps.libre.sh_realms.yaml b/config/crd/bases/apps.libre.sh_realms.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..1c7c3cb76bdccf04526ac9cb432166f5beb4855c
--- /dev/null
+++ b/config/crd/bases/apps.libre.sh_realms.yaml
@@ -0,0 +1,56 @@
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.8.0
+  creationTimestamp: null
+  name: realms.apps.libre.sh
+spec:
+  group: apps.libre.sh
+  names:
+    kind: Realm
+    listKind: RealmList
+    plural: realms
+    singular: realm
+  scope: Namespaced
+  versions:
+  - name: v1alpha1
+    schema:
+      openAPIV3Schema:
+        description: Realm is the Schema for the realms API
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: RealmSpec defines the desired state of Realm
+            properties:
+              foo:
+                description: Foo is an example field of Realm. Edit realm_types.go
+                  to remove/update
+                type: string
+            type: object
+          status:
+            description: RealmStatus defines the observed state of Realm
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
diff --git a/config/crd/bases/apps.libre.sh_rocketchats.yaml b/config/crd/bases/apps.libre.sh_rocketchats.yaml
index 0d396f879d1c9d014a2278d8dbb1e578750c8d5d..852fcbd1de584ffa0c1a09adcbe94585f565e681 100644
--- a/config/crd/bases/apps.libre.sh_rocketchats.yaml
+++ b/config/crd/bases/apps.libre.sh_rocketchats.yaml
@@ -35,10 +35,10 @@ spec:
           spec:
             description: RocketChatSpec defines the desired state of RocketChat
             properties:
-              foo:
-                description: Foo is an example field of RocketChat. Edit rocketchat_types.go
-                  to remove/update
+              domain:
                 type: string
+              enableOAuth:
+                type: boolean
             type: object
           status:
             description: RocketChatStatus defines the observed state of RocketChat
diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml
index 05330a935bb6e847bf85e56bc48810650070547c..45cad9ae278ab19f33fc460f8544904fe2cf0c4f 100644
--- a/config/crd/kustomization.yaml
+++ b/config/crd/kustomization.yaml
@@ -4,6 +4,7 @@
 resources:
 - bases/apps.libre.sh_nextclouds.yaml
 - bases/apps.libre.sh_rocketchats.yaml
+- bases/apps.libre.sh_realms.yaml
 #+kubebuilder:scaffold:crdkustomizeresource
 
 patchesStrategicMerge:
@@ -11,12 +12,14 @@ patchesStrategicMerge:
 # patches here are for enabling the conversion webhook for each CRD
 #- patches/webhook_in_nextclouds.yaml
 #- patches/webhook_in_rocketchats.yaml
+#- patches/webhook_in_realms.yaml
 #+kubebuilder:scaffold:crdkustomizewebhookpatch
 
 # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
 # patches here are for enabling the CA injection for each CRD
 #- patches/cainjection_in_nextclouds.yaml
 #- patches/cainjection_in_rocketchats.yaml
+#- patches/cainjection_in_realms.yaml
 #+kubebuilder:scaffold:crdkustomizecainjectionpatch
 
 # the following config is for teaching kustomize how to do kustomization for CRDs.
diff --git a/config/crd/patches/cainjection_in_apps_realms.yaml b/config/crd/patches/cainjection_in_apps_realms.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..8d1a802067c53bb1de2b2b26c35f5696291dec22
--- /dev/null
+++ b/config/crd/patches/cainjection_in_apps_realms.yaml
@@ -0,0 +1,7 @@
+# The following patch adds a directive for certmanager to inject CA into the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
+  name: realms.apps.libre.sh
diff --git a/config/crd/patches/webhook_in_apps_realms.yaml b/config/crd/patches/webhook_in_apps_realms.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a9173fc1e4aed9eff9402bba4e1768fa17146466
--- /dev/null
+++ b/config/crd/patches/webhook_in_apps_realms.yaml
@@ -0,0 +1,16 @@
+# The following patch enables a conversion webhook for the CRD
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  name: realms.apps.libre.sh
+spec:
+  conversion:
+    strategy: Webhook
+    webhook:
+      clientConfig:
+        service:
+          namespace: system
+          name: webhook-service
+          path: /convert
+      conversionReviewVersions:
+      - v1
diff --git a/config/rbac/apps_realm_editor_role.yaml b/config/rbac/apps_realm_editor_role.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..87594f3d65f25d53796bf277bd23f071933908f6
--- /dev/null
+++ b/config/rbac/apps_realm_editor_role.yaml
@@ -0,0 +1,24 @@
+# permissions for end users to edit realms.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: realm-editor-role
+rules:
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms/status
+  verbs:
+  - get
diff --git a/config/rbac/apps_realm_viewer_role.yaml b/config/rbac/apps_realm_viewer_role.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..d02f44e46730564f802a4e7e0a50480caecc7015
--- /dev/null
+++ b/config/rbac/apps_realm_viewer_role.yaml
@@ -0,0 +1,20 @@
+# permissions for end users to view realms.
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+  name: realm-viewer-role
+rules:
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms
+  verbs:
+  - get
+  - list
+  - watch
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms/status
+  verbs:
+  - get
diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml
index b7da03eb88da6508423520dad5b71541d29d513c..a0a3ef9165ee59128b7103b1c734af2efbde8edb 100644
--- a/config/rbac/role.yaml
+++ b/config/rbac/role.yaml
@@ -31,6 +31,32 @@ rules:
   - get
   - patch
   - update
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms
+  verbs:
+  - create
+  - delete
+  - get
+  - list
+  - patch
+  - update
+  - watch
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms/finalizers
+  verbs:
+  - update
+- apiGroups:
+  - apps.libre.sh
+  resources:
+  - realms/status
+  verbs:
+  - get
+  - patch
+  - update
 - apiGroups:
   - apps.libre.sh
   resources:
diff --git a/config/samples/apps_v1alpha1_realm.yaml b/config/samples/apps_v1alpha1_realm.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..ad296c729c9acdb57357bbe890b06fb5b5129915
--- /dev/null
+++ b/config/samples/apps_v1alpha1_realm.yaml
@@ -0,0 +1,6 @@
+apiVersion: apps.libre.sh/v1alpha1
+kind: Realm
+metadata:
+  name: realm-sample
+spec:
+  # TODO(user): Add fields here
diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml
index 0e1156297fb8fa86421671eab2011c3ebb015f3d..0f1165f50609a2322dc40ea1c8b3c250ddfdbe5e 100644
--- a/config/samples/kustomization.yaml
+++ b/config/samples/kustomization.yaml
@@ -2,4 +2,5 @@
 resources:
 - apps_v1alpha1_nextcloud.yaml
 - apps_v1alpha1_rocketchat.yaml
+- apps_v1alpha1_realm.yaml
 #+kubebuilder:scaffold:manifestskustomizesamples
diff --git a/controllers/apps/realm_controller.go b/controllers/apps/realm_controller.go
new file mode 100644
index 0000000000000000000000000000000000000000..0823837d06a3d55763946b4188547b7f8196a538
--- /dev/null
+++ b/controllers/apps/realm_controller.go
@@ -0,0 +1,46 @@
+package apps
+
+import (
+	"context"
+
+	"k8s.io/apimachinery/pkg/runtime"
+	ctrl "sigs.k8s.io/controller-runtime"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/log"
+
+	appsv1alpha1 "libre.sh/controller/apis/apps/v1alpha1"
+)
+
+// RealmReconciler reconciles a Realm object
+type RealmReconciler struct {
+	client.Client
+	Scheme *runtime.Scheme
+}
+
+//+kubebuilder:rbac:groups=apps.libre.sh,resources=realms,verbs=get;list;watch;create;update;patch;delete
+//+kubebuilder:rbac:groups=apps.libre.sh,resources=realms/status,verbs=get;update;patch
+//+kubebuilder:rbac:groups=apps.libre.sh,resources=realms/finalizers,verbs=update
+
+// Reconcile is part of the main kubernetes reconciliation loop which aims to
+// move the current state of the cluster closer to the desired state.
+// TODO(user): Modify the Reconcile function to compare the state specified by
+// the Realm object against the actual cluster state, and then
+// perform operations to make the cluster state reflect the state specified by
+// the user.
+//
+// For more details, check Reconcile and its Result here:
+// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.12.1/pkg/reconcile
+func (r *RealmReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+	_ = log.FromContext(ctx)
+
+	// TODO(user): your logic here
+
+	return ctrl.Result{}, nil
+}
+
+// SetupWithManager sets up the controller with the Manager.
+func (r *RealmReconciler) SetupWithManager(mgr ctrl.Manager) error {
+	return ctrl.NewControllerManagedBy(mgr).
+		For(&appsv1alpha1.Realm{}).
+		Complete(r)
+}
diff --git a/main.go b/main.go
index 93dbd12a457e8046ee06cfa3cde26c3dc7e8a76d..2342d42ff9bece908bac82589f03996b16bc5a1a 100644
--- a/main.go
+++ b/main.go
@@ -76,6 +76,13 @@ func main() {
 		setupLog.Error(err, "unable to create controller", "controller", "RocketChat")
 		os.Exit(1)
 	}
+	if err = (&appscontrollers.RealmReconciler{
+		Client: mgr.GetClient(),
+		Scheme: mgr.GetScheme(),
+	}).SetupWithManager(mgr); err != nil {
+		setupLog.Error(err, "unable to create controller", "controller", "Realm")
+		os.Exit(1)
+	}
 	//+kubebuilder:scaffold:builder
 
 	if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {