diff --git a/lib/default.nix b/lib/default.nix
index 0094741..73c04f4 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -1,7 +1,16 @@
 inputs: final: _prev:
 
 with builtins;
-
+let
+  evalMeta =
+    raw:
+    (_prev.evalModules {
+      modules = [
+        (import ../modules/meta/default.nix)
+        { machine.meta = raw; }
+      ];
+    }).config.machine.meta;
+in
 rec {
   importConfig =
     path:
@@ -37,29 +46,25 @@ rec {
     };
   };
 
-  evalMeta =
-    raw:
-    (_prev.evalModules {
-      modules = [
-        (import ../modules/meta/default.nix)
-        { machine.meta = raw; }
-      ];
-    }).config.machine.meta;
+  listToAttrsWithMerge =
+    l:
+    mapAttrs (_: v: _prev.fold (elem: acc: elem.value // acc) { } v) (builtins.groupBy (e: e.name) l);
+
+  mapAttrsWithMerge = f: set: listToAttrsWithMerge (map (attr: f attr set.${attr}) (attrNames set));
 
   snowfield = mapAttrs (
     name: _value:
+    let
+      machineF = import (../machines + "/${name}/default.nix");
+    in
     evalMeta
-      (import (../machines + "/${name}/default.nix") {
-        inherit inputs;
-        config = null;
-        pkgs = null;
-        lib = null;
-        modulesPath = null;
-      }).machine.meta
+      (machineF ((mapAttrs (_: _: null) (builtins.functionArgs machineF)) // { inherit inputs; }))
+      .machine.meta
   ) (final.filterAttrs (_: v: v == "directory") (readDir ../machines));
 
   dns = import ./dns.nix {
     lib = final;
     dnsLib = (import inputs.dns).lib;
   };
+
 }
diff --git a/machines/gustave/default.nix b/machines/gustave/default.nix
index c436522..d30b3b6 100644
--- a/machines/gustave/default.nix
+++ b/machines/gustave/default.nix
@@ -1,11 +1,11 @@
 { pkgs, inputs, ... }:
-
 {
   imports = [
     ../../users/default.nix
     ../../users/julien.nix
     ./hardware.nix
     ./home-julien.nix
+    ./nsd.nix
   ];
 
   machine.meta = {
diff --git a/machines/gustave/nsd.nix b/machines/gustave/nsd.nix
new file mode 100644
index 0000000..9ba262d
--- /dev/null
+++ b/machines/gustave/nsd.nix
@@ -0,0 +1,48 @@
+{
+  config,
+  lib,
+  inputs,
+  nixosConfigurations,
+  ...
+}:
+let
+  zonesToList = lib.mapAttrsToList (name: value: { ${name} = value; });
+  zonesFromConfig = lib.mkMerge (
+    lib.fold (elem: acc: acc ++ (zonesToList elem.config.machine.meta.zones)) [ ] (
+      lib.attrValues nixosConfigurations
+    )
+  );
+  dnsLib = (import inputs.dns).lib;
+  evalZones =
+    zones:
+    (lib.evalModules {
+      modules = [
+        {
+          options = {
+            zones = lib.mkOption {
+              type = lib.types.attrsOf dnsLib.types.zone;
+              description = "DNS zones";
+            };
+          };
+          config = {
+            inherit zones;
+          };
+        }
+      ];
+    }).config.zones;
+
+in
+
+{
+  services.nsd = {
+    enable = true;
+    interfaces = [
+      config.machine.meta.ips.public.ipv4
+      config.machine.meta.ips.public.ipv6
+    ];
+    zones = lib.mapAttrs (_: value: {
+      data = builtins.toString value;
+      provideXFR = [ "192.168.0.0/21 NOKEY" ];
+    }) (evalZones zonesFromConfig);
+  };
+}
diff --git a/modules/dns/default.nix b/modules/dns/default.nix
new file mode 100644
index 0000000..414d175
--- /dev/null
+++ b/modules/dns/default.nix
@@ -0,0 +1,61 @@
+{
+  lib,
+  config,
+  inputs,
+  ...
+}:
+let
+  cfg = config.machine.meta;
+  allowedDomains = [
+    "luj.fr"
+    "julienmalka.me"
+    "malka.family"
+    "luj"
+    "luj-static.page"
+  ];
+
+  isVPNDomain = domain: lib.dns.domainToZone [ "luj" ] domain != null;
+  dnsLib = (import inputs.dns).lib;
+  SOA = {
+    nameServer = "ns";
+    adminEmail = "dns@malka.sh";
+    serial = 0;
+  };
+  defaults = {
+    inherit SOA;
+  };
+in
+with lib;
+{
+
+  options = {
+    machine.meta.zones = mkOption {
+      type = types.attrsOf dnsLib.types.zone;
+      default = { };
+    };
+  };
+
+  config =
+    let
+      # list of domains that are defined in the current configuration throught virtualHosts
+      domains = lib.dns.domainsFromConfiguration allowedDomains config;
+      # AttrSet domain -> { records }
+      recordsPerDomain = map (
+        domain:
+        mapAttrs' (
+          n: v:
+          nameValuePair (lib.dns.domainToZone allowedDomains n) (
+            let
+              subdomain = lib.dns.getDomainPrefix allowedDomains n;
+            in
+            (if elem subdomain allowedDomains then v else { subdomains."${subdomain}" = v; }) // defaults
+          )
+        ) (lib.dns.domainToRecords domain cfg (isVPNDomain domain))
+      ) domains;
+    in
+
+    {
+      machine.meta.zones = lib.mkMerge recordsPerDomain;
+    };
+
+}