diff --git a/.sops.yaml b/.sops.yaml
index 24628eb..06999de 100644
--- a/.sops.yaml
+++ b/.sops.yaml
@@ -1,56 +1,36 @@
-keys:
-  - &build01 age17jtyn2y4fpey6q7ers9gtnh4580xj89zdjuew9nqhxywmsaw94fs5udupc
-  - &build02 age1kh6yvgxz9ys74as7aufdy8je7gmqjtguhnjuxvj79qdjswk2r3xqxf2n6d
-  - &build03 age1qg7tfjwzp6dxwkw9vej6knkhdvqre3fu7ryzsdk5ggvtdx854ycqevlwnq
-  - &build04 age1r464z5e2shvnh9ekzapgghevr9wy7spd4d7pt5a89ucdk6kr6yhqzv5gkj
-  - &web02 age158v8dpppnw3yt2kqgqekwamaxpst5alfrnvvt7z36wfdk4veydrsqxc2tl
-  - &mic92 age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
-  - &ryantm age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
-  - &zimbatm age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
-  - &zowoq age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
-  - &adisbladis age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
-# scan new hosts with `scan-age-keys` task
+# AUTOMATICALLY GENERATED WITH: $ inv update-sops-files
 creation_rules:
-  - path_regex: ^secrets.yaml$
-    key_groups:
+  - key_groups:
       - age:
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
-  - path_regex: terraform/secrets.yaml$
-    key_groups:
+          - age1kh6yvgxz9ys74as7aufdy8je7gmqjtguhnjuxvj79qdjswk2r3xqxf2n6d
+          - age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
+          - age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
+          - age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
+          - age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
+          - age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
+    path_regex: ^hosts/build02/secrets.yaml$
+  - key_groups:
       - age:
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
-  - path_regex: hosts/build02/[^/]+\.yaml$
-    key_groups:
+          - age1qg7tfjwzp6dxwkw9vej6knkhdvqre3fu7ryzsdk5ggvtdx854ycqevlwnq
+          - age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
+          - age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
+          - age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
+          - age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
+          - age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
+    path_regex: ^hosts/build03/secrets.yaml$
+  - key_groups:
       - age:
-          - *build02
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
-  - path_regex: hosts/build03/[^/]+\.yaml$
-    key_groups:
+          - age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
+          - age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
+          - age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
+          - age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
+          - age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
+    path_regex: ^secrets.yaml$
+  - key_groups:
       - age:
-          - *build03
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
-  - path_regex: hosts/web02/[^/]+\.yaml$
-    key_groups:
-      - age:
-          - *web02
-          - *mic92
-          - *ryantm
-          - *zimbatm
-          - *zowoq
-          - *adisbladis
+          - age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy
+          - age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz
+          - age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay
+          - age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h
+          - age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n
+    path_regex: ^terraform/secrets.yaml$
diff --git a/dev/shell.nix b/dev/shell.nix
index 1d058e5..b649aa3 100644
--- a/dev/shell.nix
+++ b/dev/shell.nix
@@ -11,6 +11,7 @@
           python3.pkgs.invoke
           sops
           ssh-to-age
+          yq-go
         ];
       };
     sotp =
diff --git a/devdoc/onboarding.md b/devdoc/onboarding.md
index 365b0e6..52cad21 100644
--- a/devdoc/onboarding.md
+++ b/devdoc/onboarding.md
@@ -6,7 +6,7 @@
 
 - Add their user to [secrets/secrets.nix](../secrets/secrets.nix) and run `inv update-agenix-files`.
 
-- Add their age key to [.sops.yaml](../.sops.yaml), update the `creation_rules` and run `inv update-sops-files`.
+- Add their age key to [sops.json](../sops.json) and run `inv update-sops-files`.
 
 - Add their email in [terraform/locals.tf](../terraform/locals.tf), this will give them access to:
 
diff --git a/sops.json b/sops.json
new file mode 100644
index 0000000..133cf3c
--- /dev/null
+++ b/sops.json
@@ -0,0 +1,13 @@
+{
+  "admins": {
+    "adisbladis": "age1dzvjjum2p240qtdt2qcxpm7pl2s5w36mh4fs3q9dhhq0uezvdqaq9vrgfy",
+    "mic92": "age17n64ahe3wesh8l8lj0zylf4nljdmqn28hvqns2g7hgm9mdkhlsvsjuvkxz",
+    "ryantm": "age1d87z3zqlv6ullnzyng8l722xzxwqr677csacf3zf3l28dau7avfs6pc7ay",
+    "zimbatm": "age1jrh8yyq3swjru09s75s4mspu0mphh7h6z54z946raa9wx3pcdegq0x8t4h",
+    "zowoq": "age1m7xhem3qll35d539f364pm6txexvnp6k0tk34d8jxu4ry3pptv7smm0k5n"
+  },
+  "hosts": {
+    "build02": "age1kh6yvgxz9ys74as7aufdy8je7gmqjtguhnjuxvj79qdjswk2r3xqxf2n6d",
+    "build03": "age1qg7tfjwzp6dxwkw9vej6knkhdvqre3fu7ryzsdk5ggvtdx854ycqevlwnq"
+  }
+}
diff --git a/sops.nix b/sops.nix
new file mode 100644
index 0000000..27487ea
--- /dev/null
+++ b/sops.nix
@@ -0,0 +1,35 @@
+# https://github.com/TUM-DSE/doctor-cluster-config/blob/8c11c117e66af1cc205eb2094ab94e8a3317ff2e/sops.yaml.nix
+let
+  keys = builtins.fromJSON (builtins.readFile ./sops.json);
+  admins = builtins.attrValues keys.admins;
+
+  mapAttrsToList = f: attrs: map (name: f name attrs.${name}) (builtins.attrNames attrs);
+
+  renderPermissions =
+    attrs:
+    mapAttrsToList (path: keys: {
+      path_regex = "^${path}$";
+      key_groups = [
+        {
+          age = keys ++ admins;
+        }
+      ];
+    }) attrs;
+
+  # This is the list of permissions per file. The admins have permissions for all files.
+  sopsPermissions =
+    {
+      "secrets.yaml" = [ ];
+      "terraform/secrets.yaml" = [ ];
+    }
+    // builtins.mapAttrs (_: value: (map (x: keys.hosts.${x}) value)) { }
+    // builtins.listToAttrs (
+      mapAttrsToList (hostname: key: {
+        name = "hosts/${hostname}/secrets.yaml";
+        value = [ key ];
+      }) keys.hosts
+    );
+in
+{
+  creation_rules = renderPermissions sopsPermissions;
+}
diff --git a/tasks.py b/tasks.py
index 83f74bc..f9cd766 100644
--- a/tasks.py
+++ b/tasks.py
@@ -65,8 +65,12 @@ def update_agenix_files(c: Any) -> None:
 @task
 def update_sops_files(c: Any) -> None:
     """
-    Update all sops yaml files according to .sops.yaml rules
+    Update all sops yaml files according to sops.nix rules
     """
+    with open(f"{ROOT}/.sops.yaml", "w") as f:
+        print("# AUTOMATICALLY GENERATED WITH: $ inv update-sops-files", file=f)
+
+    c.run(f"nix eval --json -f {ROOT}/sops.nix | yq e -P - >> {ROOT}/.sops.yaml")
     c.run("shopt -s globstar && sops updatekeys --yes **/secrets.yaml")