diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 75c0e40b61..274fdad980 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -585,6 +585,10 @@ usage(boolean_t requested)
 		(void) fprintf(fp, "YES       NO   <size> | none\n");
 		(void) fprintf(fp, "\t%-15s ", "projectobjquota@...");
 		(void) fprintf(fp, "YES       NO   <size> | none\n");
+		(void) fprintf(fp, "\t%-15s ", "defaultuserquota");
+		(void) fprintf(fp, "YES       NO   <size> | none\n");
+		(void) fprintf(fp, "\t%-15s ", "defaultgroupquota");
+		(void) fprintf(fp, "YES       NO   <size> | none\n");
 		(void) fprintf(fp, "\t%-15s ", "written@<snap>");
 		(void) fprintf(fp, " NO       NO   <size>\n");
 		(void) fprintf(fp, "\t%-15s ", "written#<bookmark>");
diff --git a/include/os/linux/zfs/sys/zfs_vfsops_os.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h
index b4d5db21f5..6a8e100978 100644
--- a/include/os/linux/zfs/sys/zfs_vfsops_os.h
+++ b/include/os/linux/zfs/sys/zfs_vfsops_os.h
@@ -128,6 +128,8 @@ struct zfsvfs {
 	uint64_t	z_groupobjquota_obj;
 	uint64_t	z_projectquota_obj;
 	uint64_t	z_projectobjquota_obj;
+	uint64_t	z_defaultuserquota_obj;
+	uint64_t	z_defaultgroupquota_obj;
 	uint64_t	z_replay_eof;	/* New end of file - replay only */
 	sa_attr_type_t	*z_attr_table;	/* SA attr mapping->id */
 	uint64_t	z_hold_size;	/* znode hold array size */
diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h
index c7e48d1edc..91323dd5d1 100644
--- a/include/sys/fs/zfs.h
+++ b/include/sys/fs/zfs.h
@@ -193,6 +193,8 @@ typedef enum {
 	ZFS_PROP_SNAPSHOTS_CHANGED,
 	ZFS_PROP_PREFETCH,
 	ZFS_PROP_VOLTHREADING,
+	ZFS_PROP_DEFAULTUSERQUOTA,
+	ZFS_PROP_DEFAULTGROUPQUOTA,
 	ZFS_NUM_PROPS
 } zfs_prop_t;
 
diff --git a/include/sys/zfs_quota.h b/include/sys/zfs_quota.h
index 4567cc651a..e184f89459 100644
--- a/include/sys/zfs_quota.h
+++ b/include/sys/zfs_quota.h
@@ -37,6 +37,7 @@ extern int zfs_userspace_many(struct zfsvfs *, zfs_userquota_prop_t,
     uint64_t *, void *, uint64_t *);
 extern int zfs_set_userquota(struct zfsvfs *, zfs_userquota_prop_t,
     const char *, uint64_t, uint64_t);
+extern int zfs_set_defaultquota(struct zfsvfs *, int, uint64_t);
 
 extern boolean_t zfs_id_overobjquota(struct zfsvfs *, uint64_t, uint64_t);
 extern boolean_t zfs_id_overblockquota(struct zfsvfs *, uint64_t, uint64_t);
diff --git a/lib/libzfs/libzfs_dataset.c b/lib/libzfs/libzfs_dataset.c
index 231bbbd92d..e670379cf4 100644
--- a/lib/libzfs/libzfs_dataset.c
+++ b/lib/libzfs/libzfs_dataset.c
@@ -2811,6 +2811,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
 	case ZFS_PROP_REFQUOTA:
 	case ZFS_PROP_RESERVATION:
 	case ZFS_PROP_REFRESERVATION:
+	case ZFS_PROP_DEFAULTUSERQUOTA:
+	case ZFS_PROP_DEFAULTGROUPQUOTA:
 
 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
 			return (-1);
diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c
index 2015c20d73..e5161a04e3 100644
--- a/module/os/linux/zfs/zfs_vfsops.c
+++ b/module/os/linux/zfs/zfs_vfsops.c
@@ -743,6 +743,22 @@ zfsvfs_init(zfsvfs_t *zfsvfs, objset_t *os)
 	else if (error != 0)
 		return (error);
 
+	error = zap_lookup(os, MASTER_NODE_OBJ,
+	    zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA),
+	    8, 1, &zfsvfs->z_defaultuserquota_obj);
+	if (error == ENOENT)
+		zfsvfs->z_defaultuserquota_obj = 0;
+	else if (error != 0)
+		return (error);
+
+	error = zap_lookup(os, MASTER_NODE_OBJ,
+	    zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA),
+	    8, 1, &zfsvfs->z_defaultgroupquota_obj);
+	if (error == ENOENT)
+		zfsvfs->z_defaultgroupquota_obj = 0;
+	else if (error != 0)
+		return (error);
+
 	error = zap_lookup(os, MASTER_NODE_OBJ, ZFS_FUID_TABLES, 8, 1,
 	    &zfsvfs->z_fuid_obj);
 	if (error == ENOENT)
diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c
index 764993b45e..6a08d0a411 100644
--- a/module/zcommon/zfs_prop.c
+++ b/module/zcommon/zfs_prop.c
@@ -694,6 +694,12 @@ zfs_prop_init(void)
 	zprop_register_number(ZFS_PROP_SNAPSHOT_LIMIT, "snapshot_limit",
 	    UINT64_MAX, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME,
 	    "<count> | none", "SSLIMIT", B_FALSE, sfeatures);
+	zprop_register_number(ZFS_PROP_DEFAULTUSERQUOTA, "defaultuserquota", 0,
+	    PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "<size> | none",
+	    "DEFAULTUSERQUOTA", B_FALSE, sfeatures);
+	zprop_register_number(ZFS_PROP_DEFAULTGROUPQUOTA, "defaultgroupquota",
+	    0, PROP_DEFAULT, ZFS_TYPE_FILESYSTEM, "<size> | none",
+	    "DEFAULTGROUPQUOTA", B_FALSE, sfeatures);
 
 	/* inherit number properties */
 	zprop_register_number(ZFS_PROP_RECORDSIZE, "recordsize",
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index 897335dd4e..63c0f9efe4 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -2567,6 +2567,25 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source,
 		}
 		break;
 	}
+	case ZFS_PROP_DEFAULTUSERQUOTA:
+	case ZFS_PROP_DEFAULTGROUPQUOTA:
+	{
+		zfsvfs_t *zfsvfs;
+
+		if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_TRUE)) != 0)
+			break;
+
+		err = zfs_set_defaultquota(zfsvfs, prop, intval);
+		zfsvfs_rele(zfsvfs, FTAG);
+
+		/*
+		 * Set err to -1 to force the zfs_set_prop_nvlist code down the
+		 * default path to set the value in the nvlist.
+		 */
+		if (err == 0)
+			err = -1;
+		break;
+	}
 	default:
 		err = -1;
 	}
diff --git a/module/zfs/zfs_quota.c b/module/zfs/zfs_quota.c
index 9b351eefc0..e1856c5d63 100644
--- a/module/zfs/zfs_quota.c
+++ b/module/zfs/zfs_quota.c
@@ -366,6 +366,59 @@ zfs_set_userquota(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type,
 	return (err);
 }
 
+int
+zfs_set_defaultquota(zfsvfs_t *zfsvfs, int type, uint64_t quota)
+{
+	int err;
+	dmu_tx_t *tx;
+	uint64_t *objp;
+	const char *name;
+
+	if (type == ZFS_PROP_DEFAULTUSERQUOTA) {
+		objp = &zfsvfs->z_defaultuserquota_obj;
+		name = zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA);
+	} else if (type == ZFS_PROP_DEFAULTGROUPQUOTA) {
+		objp = &zfsvfs->z_defaultgroupquota_obj;
+		name = zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA);
+	} else {
+		/* defaultprojectquota NYI (does it make sense to do so?) */
+		return (SET_ERROR(EINVAL));
+	}
+
+	tx = dmu_tx_create(zfsvfs->z_os);
+	dmu_tx_hold_zap(tx, *objp ? *objp : DMU_NEW_OBJECT, B_TRUE, NULL);
+	if (*objp == 0) {
+		dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, B_TRUE, name);
+	}
+	err = dmu_tx_assign(tx, TXG_WAIT);
+	if (err) {
+		dmu_tx_abort(tx);
+		return (err);
+	}
+
+	mutex_enter(&zfsvfs->z_lock);
+	if (*objp == 0) {
+		*objp = zap_create(zfsvfs->z_os, DMU_OT_USERGROUP_QUOTA,
+		    DMU_OT_NONE, 0, tx);
+		VERIFY(0 == zap_add(zfsvfs->z_os, MASTER_NODE_OBJ, name,
+		    8, 1, objp, tx));
+	}
+	mutex_exit(&zfsvfs->z_lock);
+
+	if (quota == 0) {
+		err = zap_remove(zfsvfs->z_os, *objp, name, tx);
+		if (err == ENOENT)
+			err = 0;
+	} else {
+		err = zap_update(zfsvfs->z_os, *objp, name,
+		    8, 1, &quota, tx);
+	}
+
+	ASSERT(err == 0);
+	dmu_tx_commit(tx);
+	return (err);
+}
+
 boolean_t
 zfs_id_overobjquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 {
@@ -423,8 +476,9 @@ boolean_t
 zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 {
 	char buf[20];
-	uint64_t used, quota, quotaobj;
-	int err;
+	uint64_t used, quota, quotaobj, defquota, defquotaobj;
+	int err, uerr, derr;
+	const char *name;
 
 	if (usedobj == DMU_PROJECTUSED_OBJECT) {
 		if (!dmu_objset_projectquota_present(zfsvfs->z_os)) {
@@ -440,22 +494,46 @@ zfs_id_overblockquota(zfsvfs_t *zfsvfs, uint64_t usedobj, uint64_t id)
 		quotaobj = zfsvfs->z_projectquota_obj;
 	} else if (usedobj == DMU_USERUSED_OBJECT) {
 		quotaobj = zfsvfs->z_userquota_obj;
+		defquotaobj = zfsvfs->z_defaultuserquota_obj;
+		name = zfs_prop_to_name(ZFS_PROP_DEFAULTUSERQUOTA);
 	} else if (usedobj == DMU_GROUPUSED_OBJECT) {
 		quotaobj = zfsvfs->z_groupquota_obj;
+		defquotaobj = zfsvfs->z_defaultgroupquota_obj;
+		name = zfs_prop_to_name(ZFS_PROP_DEFAULTGROUPQUOTA);
 	} else {
 		return (B_FALSE);
 	}
-	if (quotaobj == 0 || zfsvfs->z_replay)
+
+	/* no quota assigned */
+	if (quotaobj == 0 && defquotaobj == 0) {
 		return (B_FALSE);
+	}
+	if (zfsvfs->z_replay) {
+		return (B_FALSE);
+	}
 
 	(void) snprintf(buf, sizeof (buf), "%llx", (longlong_t)id);
-	err = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
-	if (err != 0)
+	derr = zap_lookup(zfsvfs->z_os, defquotaobj, name,
+	    8, 1, &defquota);
+	uerr = zap_lookup(zfsvfs->z_os, quotaobj, buf, 8, 1, &quota);
+
+	/* bail if both id-specific && default lookups failed */
+	if (uerr != 0 && derr != 0)
 		return (B_FALSE);
 
 	err = zap_lookup(zfsvfs->z_os, usedobj, buf, 8, 1, &used);
 	if (err != 0)
 		return (B_FALSE);
+
+	/*
+	 * if a user/group has a specific quota assigned, use that.
+	 * if neither quota...we've already returned false (hopefully).
+	 * if a default quota is set, but no user quota is,
+	 *   use the default.
+	 */
+	if (uerr != 0 && derr == 0 && defquota)
+		quota = defquota;
+
 	return (used >= quota);
 }
 
@@ -470,6 +548,7 @@ EXPORT_SYMBOL(zpl_get_file_info);
 EXPORT_SYMBOL(zfs_userspace_one);
 EXPORT_SYMBOL(zfs_userspace_many);
 EXPORT_SYMBOL(zfs_set_userquota);
+EXPORT_SYMBOL(zfs_set_defaultquota);
 EXPORT_SYMBOL(zfs_id_overblockquota);
 EXPORT_SYMBOL(zfs_id_overobjquota);
 EXPORT_SYMBOL(zfs_id_overquota);