diff --git a/require-ess/tools/driver.makefile b/require-ess/tools/driver.makefile
index b734aaf1c4135597fa5b0ca96ce5a0aa71bd2f7c..b025a71a7059f3b788587e00e198b76516cc07ff 100644
--- a/require-ess/tools/driver.makefile
+++ b/require-ess/tools/driver.makefile
@@ -258,6 +258,12 @@ CFGS = ${CONFIGS}
 CFGS += ${CONFIGS_${EPICSVERSION}}
 export CFGS
 
+INSTALL_LICENSE = ${MODULE_LOCATION}/doc
+# Find all license files to distribute with binaries
+LICENSES = $(shell find -not -path '*/.*' -type f -iname LICENSE)
+LICENSES += $(shell find -not -path '*/.*' -type f -iname Copyright)
+export LICENSES
+
 # Filter architectures to build using EXCLUDE_ARCHS and ARCH_FILTER.
 ALL_ARCHS = ${EPICS_HOST_ARCH} ${CROSS_COMPILER_TARGET_ARCHS}
 BUILD_ARCHS = $(filter-out $(addprefix %,${EXCLUDE_ARCHS}),$(filter-out $(addsuffix %,${EXCLUDE_ARCHS}),\
@@ -309,6 +315,7 @@ debug::
 	@echo "LIBVERSION = ${LIBVERSION}"
 	@echo "E3_SITEMODS_PATH = ${E3_SITEMODS_PATH}"
 	@echo "EPICS_MODULES = ${EPICS_MODULES}"
+	@echo "LICENSES = ${LICENSES}"
 
 # Create e.g. build-$(T_A) rules for each architecture, so that we can just do
 #   build: build-arch1 build-arch2
@@ -349,6 +356,20 @@ build:: ${COMMON_DIR}/${METAFILE}
 
 install:: ${INSTALL_META}
 
+# The licenses should be installed after everything
+define license_install =
+$1: $2
+	@echo "Installing license file $$^"
+	$$(INSTALL) -d -m444 $$^ $$(@D)
+
+install:: $1
+
+endef
+# Creates a target for every license file to be installed.  Some modules have
+# more than one license file that needs distribution.  For them we add the
+# previous directory so we have them separate by projects inside /doc.
+$(foreach d, $(LICENSES), $(eval $(call license_install, $(INSTALL_LICENSE)/$(filter-out ., $(lastword $(subst /, , $(dir $(d))))/$(notdir $(d))), $(d))))
+
 else # T_A
 
 ifeq ($(filter O.%,$(notdir ${CURDIR})),)
diff --git a/tests/test_build.py b/tests/test_build.py
index bb07bf06f343adf3f2ab5d35011d4a760fb02392..595760d698d5a8a33a6b8ac0c1eda96af73b1f0e 100644
--- a/tests/test_build.py
+++ b/tests/test_build.py
@@ -736,3 +736,20 @@ def test_missing_record_dbd_file_causes_build_failure(wrapper: Wrapper):
         ),
         errs,
     )
+
+
+def test_install_license(wrapper: Wrapper):
+    wrapper.add_file("LICENSE")
+    wrapper.add_directory("foo")
+    wrapper.add_file("foo/LICENSE")
+
+    rc, out, _ = wrapper.run_make("install")
+    assert rc == 0
+    assert re.search("Installing license file LICENSE", out)
+    assert re.search("Installing license file foo/LICENSE", out)
+
+    file_path = wrapper.package_dir / "doc/LICENSE"
+    assert file_path.exists()
+
+    file_path = wrapper.package_dir / "doc/foo/LICENSE"
+    assert file_path.exists()
diff --git a/tests/utils.py b/tests/utils.py
index d22e2459b65cc680a794c4168239495abfd54ab9..5e27e754282c3a007a6fb4d6ebd4f3ae0701b7dc 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -98,6 +98,9 @@ include $(E3_REQUIRE_TOOLS)/driver.makefile
     def add_file(self, name):
         (self.module_dir / name).touch()
 
+    def add_directory(self, name):
+        (self.module_dir / name).mkdir()
+
     def write_dot_local_data(self, config_file: str, config_vars: dict):
         """Write config data to the specific .local file."""