diff --git a/flake.nix b/flake.nix
index dae7232..849432a 100644
--- a/flake.nix
+++ b/flake.nix
@@ -46,6 +46,7 @@
         modules/blocks/ssl.nix
         modules/blocks/tinyproxy.nix
         modules/blocks/vpn.nix
+        modules/blocks/zfs.nix
 
         modules/services/arr.nix
         modules/services/audiobookshelf.nix
diff --git a/modules/blocks/zfs.nix b/modules/blocks/zfs.nix
new file mode 100644
index 0000000..f3e76d0
--- /dev/null
+++ b/modules/blocks/zfs.nix
@@ -0,0 +1,73 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.shb.zfs;
+in
+{
+  options.shb.zfs = {
+    defaultPoolName = lib.mkOption {
+      type = lib.types.nullOr lib.types.str;
+      default = null;
+      description = "ZFS pool name datasets should be created on if no pool name is given in the dataset.";
+    };
+
+    datasets = lib.mkOption {
+      description = ''
+        ZFS Datasets.
+
+        Each entry in the attrset will be created and mounted in the given path.
+        The attrset name is the dataset name.
+
+        This block implements the following contracts:
+          - mount
+      '';
+      default = {};
+      example = lib.literalExpression ''
+        shb.zfs."safe/postgresql".path = "/var/lib/postgresql";
+      '';
+      type = lib.types.attrsOf (lib.types.submodule {
+        options = {
+          enable = lib.mkEnableOption "shb.zfs.datasets";
+
+          poolName = lib.mkOption {
+            type = lib.types.nullOr lib.types.str;
+            default = null;
+            description = "ZFS pool name this dataset should be created on. Overrides the defaultPoolName.";
+          };
+
+          path = lib.mkOption {
+            type = lib.types.str;
+            description = "Path this dataset should be mounted on.";
+          };
+        };
+      });
+    };
+  };
+
+  config = {
+    assertions = [
+      {
+        assertion = lib.any (x: x.poolName == null) (lib.mapAttrsToList (n: v: v) cfg.datasets) -> cfg.defaultPoolName != null;
+        message = "Cannot have both datasets.poolName and defaultPoolName set to null";
+      }
+    ];
+
+    system.activationScripts = lib.mapAttrs' (name: cfg':
+      let
+        dataset = (if cfg'.poolName != null then cfg'.poolName else cfg.defaultPoolName) + "/" + name;
+      in
+      lib.attrsets.nameValuePair "zfsCreate-${name}" {
+        text = ''
+          ${pkgs.zfs}/bin/zfs list ${dataset} > /dev/null 2>&1 \
+            || ${pkgs.zfs}/bin/zfs create \
+               -o mountpoint=none \
+               ${dataset} || :
+
+          [ "$(${pkgs.zfs}/bin/zfs get -H mountpoint -o value ${dataset})" = ${cfg'.path} ] \
+            || ${pkgs.zfs}/bin/zfs set \
+               mountpoint=${cfg'.path} \
+               ${dataset}
+        '';
+      }) cfg.datasets;
+  };
+}
diff --git a/modules/contracts/default.nix b/modules/contracts/default.nix
index cb95801..54a06f6 100644
--- a/modules/contracts/default.nix
+++ b/modules/contracts/default.nix
@@ -1,4 +1,5 @@
 { lib }:
 {
+  mount = import ./mount.nix { inherit lib; };
   ssl = import ./ssl.nix { inherit lib; };
 }
diff --git a/modules/contracts/mount.nix b/modules/contracts/mount.nix
new file mode 100644
index 0000000..71083be
--- /dev/null
+++ b/modules/contracts/mount.nix
@@ -0,0 +1,11 @@
+{ lib, ... }:
+lib.types.submodule {
+  freeformType = lib.types.anything;
+
+  options = {
+    path = lib.mkOption {
+      type = lib.types.str;
+      description = "Path to be mounted.";
+    };
+  };
+}