diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ba0e78d63332b0ff078bb83a3222c79b25fee13..948430d6bfebd61a3b6dd756e95f567d8c312b5f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,6 +32,7 @@ set(ARB_VALIDATION_DATA_DIR "${PROJECT_SOURCE_DIR}/validation/data" CACHE PATH
 #----------------------------------------------------------
 # Configure-time features for Arbor:
 #----------------------------------------------------------
+
 option(ARB_WITH_GPU "build with GPU support" OFF)
 
 option(ARB_WITH_MPI "build with MPI support" OFF)
@@ -44,9 +45,6 @@ option(ARB_WITH_ASSERTIONS "enable arb_assert() assertions in code" OFF)
 # Global CMake configuration
 #----------------------------------------------------------
 
-# Use GNU standard installation path conventions.
-include(GNUInstallDirs)
-
 # Include own CMake modules in search path, load common modules.
 
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
@@ -98,16 +96,22 @@ set(CMAKE_CXX_STANDARD 14)
 # Set up flags and dependencies:
 #----------------------------------------------------------
 
+# Note: any target dependency of arbor needs to be explicitly added
+# to the 'export set', even the private ones, and this must be done
+# in the same CMakeLists.txt in which the target is defined.
+
 # Interface library `arbor-private-deps` collects dependencies, options etc.
 # for the arbor library.
 
 add_library(arbor-private-deps INTERFACE)
+install(TARGETS arbor-private-deps EXPORT arbor-targets)
 
 # Interface library `arbor-public-deps` collects requirements for the
 # users of the arbor library (e.g. mpi) that will become part
 # of arbor's PUBLIC interface.
 
 add_library(arbor-public-deps INTERFACE)
+install(TARGETS arbor-public-deps EXPORT arbor-targets)
 
 # External libraries in `ext` sub-directory: json and tclap.
 # Creates interface libraries `ext-json` and `ext-tclap`.
@@ -119,6 +123,11 @@ add_subdirectory(ext)
 
 add_subdirectory(aux)
 
+# Keep track of packages we need to add to the generated CMake config
+# file for arbor.
+
+set(arbor_export_dependencies)
+
 # Target microarchitecture for building arbor libraries, tests and examples
 #---------------------------------------------------------------------------
 if(ARB_ARCH)
@@ -143,6 +152,8 @@ find_package(Threads REQUIRED)
 find_threads_cuda_fix()
 target_link_libraries(arbor-private-deps INTERFACE Threads::Threads)
 
+list(APPEND arbor_export_dependencies "Threads\;REQUIRED")
+
 # MPI support
 #-------------------
 
@@ -160,6 +171,9 @@ if(ARB_WITH_MPI)
     target_link_libraries(mpi-wrap INTERFACE MPI::MPI_CXX)
     target_compile_definitions(mpi-wrap INTERFACE MPICH_SKIP_MPICXX=1 OMPI_SKIP_MPICXX=1)
     target_link_libraries(arbor-public-deps INTERFACE mpi-wrap)
+    install(TARGETS mpi-wrap EXPORT arbor-targets)
+
+    list(APPEND arbor_export_dependencies "MPI\;REQUIRED\;CXX")
 endif()
 
 # CUDA support
@@ -187,6 +201,8 @@ find_package(Unwind)
 if(Unwind_FOUND)
     target_link_libraries(arbor-private-deps INTERFACE Unwind::unwind)
     target_compile_definitions(arbor-private-deps ARB_WITH_UNWIND)
+
+    list(APPEND arbor_export_dependencies "Unwind\;REQUIRED")
 endif()
 
 # Build and use modcc unless explicit path given
@@ -212,9 +228,47 @@ if(ARB_WITH_PROFILING)
 endif()
 
 #----------------------------------------------------------
-# Configure targets in sub-directories.
+# Set up install paths, permissions.
 #----------------------------------------------------------
 
+# Set up install paths according to GNU conventions.
+#
+# GNUInstallDirs picks (e.g.) `lib64` for the library install path on some
+# systems where this is definitely not correct (e.g. Arch Linux). If there
+# are cases where `lib` is inappropriate, we will have to incorporate special
+# case behaviour here.
+
+if(NOT CMAKE_INSTALL_LIBDIR)
+    set(CMAKE_INSTALL_LIBDIR lib)
+endif()
+include(GNUInstallDirs)
+
+# Implicitly created directories require permissions to be set explicitly
+# via this CMake variable.
+#
+# Note that this has no effect until CMake version 3.11.
+
+set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS
+    OWNER_READ
+    OWNER_WRITE
+    OWNER_EXECUTE
+    GROUP_READ
+    GROUP_EXECUTE
+    WORLD_READ
+    WORLD_EXECUTE)
+
+# CMake versions 3.11 and 3.12 ignore this variable for directories
+# implicitly created by install(DIRECTORY ...), which for us corresponds
+# to our doc and include directories. Work-around by trying to install
+# a non-existant file to these locations.
+
+foreach(directory "${CMAKE_INSTALL_DOCDIR}" "${CMAKE_INSTALL_INCLUDEDIR}")
+    install(FILES _no_such_file_ OPTIONAL DESTINATION "${directory}")
+endforeach()
+
+#----------------------------------------------------------
+# Configure targets in sub-directories.
+#----------------------------------------------------------
 
 # arbor-public-headers:
 add_subdirectory(include)
@@ -242,3 +296,33 @@ if(ARB_BUILD_VALIDATION_DATA)
     add_subdirectory(validation) # validation-data
 endif()
 
+#----------------------------------------------------------
+# Generate CMake config/version files for install.
+#----------------------------------------------------------
+
+# Note: each dependency for the arbor library target, private or otherwise,
+# needs to add itself to the arbor-exports EXPORT target in the subdirectory
+# in which they are defined, or none of this will work.
+
+set(cmake_config_dir "${CMAKE_INSTALL_LIBDIR}/cmake/arbor")
+install(EXPORT arbor-targets NAMESPACE arbor:: DESTINATION "${cmake_config_dir}")
+
+include(CMakePackageConfigHelpers)
+write_basic_package_version_file(
+    "${CMAKE_CURRENT_BINARY_DIR}/arbor-config-version.cmake"
+    COMPATIBILITY SameMajorVersion)
+
+# Template file will use contents of arbor_export_dependencies to include the
+# required `find_dependency` statements. 
+
+configure_file(
+    cmake/arbor-config.cmake.in
+    "${CMAKE_CURRENT_BINARY_DIR}/arbor-config.cmake"
+    @ONLY)
+
+install(
+    FILES
+        "${CMAKE_CURRENT_BINARY_DIR}/arbor-config.cmake"
+        "${CMAKE_CURRENT_BINARY_DIR}/arbor-config-version.cmake"
+    DESTINATION "${cmake_config_dir}")
+
diff --git a/arbor/CMakeLists.txt b/arbor/CMakeLists.txt
index 74d6a3cdbeba06eb2e1bb3f6f7f00a027b5eee40..dd885eac382d8533679b9a7ec3914607a4fa1292 100644
--- a/arbor/CMakeLists.txt
+++ b/arbor/CMakeLists.txt
@@ -80,8 +80,15 @@ endif()
 # and arbor unit tests. Private headers are also used for the other binaries
 # until the process of splitting our private and public headers is complete.
 
+# Because we need to add this target to the EXPORT set, and it needs to be
+# installed (despite being private to arbor), we have to qualify the include
+# directory with a build-time only generator expression.
+
 add_library(arbor-private-headers INTERFACE)
-target_include_directories(arbor-private-headers INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(arbor-private-headers INTERFACE
+    "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>")
+
+install(TARGETS arbor-private-headers EXPORT arbor-targets)
 
 # Mechanisms, generated from .mod files; sets arbor_mechanism_sources
 # variable, build_all_mods target. Note: CMake source file properties are
@@ -97,5 +104,5 @@ add_dependencies(arbor build_all_mods)
 target_link_libraries(arbor PRIVATE arbor-private-deps arbor-private-headers)
 target_link_libraries(arbor PUBLIC arbor-public-deps arbor-public-headers)
 
-install(TARGETS arbor ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(TARGETS arbor EXPORT arbor-targets ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
 
diff --git a/cmake/arbor-config.cmake.in b/cmake/arbor-config.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..703e70058269727803a00d75ffa474da08799dcd
--- /dev/null
+++ b/cmake/arbor-config.cmake.in
@@ -0,0 +1,9 @@
+include(CMakeFindDependencyMacro)
+
+foreach(dep @arbor_export_dependencies@)
+    find_dependency(${dep})
+endforeach()
+
+include("${CMAKE_CURRENT_LIST_DIR}/arbor-targets.cmake")
+
+
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
index 4006ec2adc485538d8667a1b5c9e24e69386ee1c..815a3b6f412a06ae8ab9583805cf5124a4aae718 100644
--- a/include/CMakeLists.txt
+++ b/include/CMakeLists.txt
@@ -62,3 +62,5 @@ add_dependencies(arbor-public-headers generate_version_hpp)
 install(DIRECTORY arbor ${CMAKE_CURRENT_BINARY_DIR}/arbor
     DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
     FILES_MATCHING PATTERN "*.hpp")
+
+install(TARGETS arbor-public-headers EXPORT arbor-targets)