diff --git a/docs/advanced/worker-configuration-reference.md b/docs/advanced/worker-configuration-reference.md
index 5df339e67..e06c6109a 100644
--- a/docs/advanced/worker-configuration-reference.md
+++ b/docs/advanced/worker-configuration-reference.md
@@ -342,7 +342,7 @@ sources:
 #### soures.usb.deviceLabelFields
 
 The set of USB ID fields from which to compose the name of the feature label.
-Valid fields are `class`, `vendor` and `device`.
+Valid fields are `class`, `vendor`, `device` and `serial`.
 
 Default: `[class, vendor, device]`
 
diff --git a/docs/get-started/features.md b/docs/get-started/features.md
index 038e9e2aa..360e4c8bc 100644
--- a/docs/get-started/features.md
+++ b/docs/get-started/features.md
@@ -229,8 +229,8 @@ Element     :An identifier of the USB attribute.
 ```
 
 The UsbId Rule allows matching the USB devices in the system on the following
-Attributes: `class`,`vendor` and `device`. A list of Elements is provided for
-each Attribute.
+Attributes: `class`,`vendor`, `device` and `serial`. A list of Elements is
+provided for each Attribute.
 
 ###### Format
 
@@ -239,6 +239,7 @@ usbId :
   class: [<class id>, ...]
   vendor: [<vendor id>,  ...]
   device: [<device id>, ...]
+  serial: [<serial>, ...]
 ```
 
 Matching is done by performing a logical _OR_ between Elements of an Attribute
@@ -346,6 +347,7 @@ custom:
       - usbId:
           vendor: ["1d6b"]
           device: ["0003"]
+          serial: ["090129a"]
   - name: "my.combined.feature"
     matchOn:
       - loadedKMod : ["vendor_kmod1", "vendor_kmod2"]
@@ -494,8 +496,8 @@ The **usb** feature source supports the following labels:
 
 `<device label>` is composed of raw USB IDs, separated by underscores.  The set
 of fields used in `<device label>` is configurable, valid fields being `class`,
-`vendor`, and `device`.  Defaults are `class`, `vendor` and `device`. An
-example label using the default label fields:
+`vendor`, `device` and `serial`.  Defaults are `class`, `vendor` and `device`.
+An example label using the default label fields:
 
 ```plaintext
 feature.node.kubernetes.io/usb-fe_1a6e_089a.present=true
diff --git a/source/custom/rules/usb_id_rule.go b/source/custom/rules/usb_id_rule.go
index bd592c025..104ec594e 100644
--- a/source/custom/rules/usb_id_rule.go
+++ b/source/custom/rules/usb_id_rule.go
@@ -29,6 +29,7 @@ type UsbIDRuleInput struct {
 	Class  []string `json:"class,omitempty"`
 	Vendor []string `json:"vendor,omitempty"`
 	Device []string `json:"device,omitempty"`
+	Serial []string `json:"serial,omitempty"`
 }
 
 type UsbIDRule struct {
@@ -38,7 +39,7 @@ type UsbIDRule struct {
 // Match USB devices on provided USB device attributes
 func (r *UsbIDRule) Match() (bool, error) {
 	devAttr := map[string]bool{}
-	for _, attr := range []string{"class", "vendor", "device"} {
+	for _, attr := range []string{"class", "vendor", "device", "serial"} {
 		devAttr[attr] = true
 	}
 	allDevs, err := usbutils.DetectUsb(devAttr)
@@ -74,5 +75,9 @@ func (r *UsbIDRule) matchDevOnRule(dev usbutils.UsbDeviceInfo) bool {
 		return false
 	}
 
+	if len(r.Serial) > 0 && !in(dev["serial"], r.Serial) {
+		return false
+	}
+
 	return true
 }
diff --git a/source/internal/usb_utils.go b/source/internal/usb_utils.go
index e2b4e7971..2b80178fb 100644
--- a/source/internal/usb_utils.go
+++ b/source/internal/usb_utils.go
@@ -29,7 +29,7 @@ import (
 type UsbDeviceInfo map[string]string
 type UsbClassMap map[string]UsbDeviceInfo
 
-var DefaultUsbDevAttrs = []string{"class", "vendor", "device"}
+var DefaultUsbDevAttrs = []string{"class", "vendor", "device", "serial"}
 
 // The USB device sysfs files do not have terribly user friendly names, map
 // these for consistency with the PCI matcher.
@@ -37,6 +37,7 @@ var devAttrFileMap = map[string]string{
 	"class":  "bDeviceClass",
 	"device": "idProduct",
 	"vendor": "idVendor",
+	"serial": "serial",
 }
 
 func readSingleUsbSysfsAttribute(path string) (string, error) {