diff --git a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt
index f6d75d6f6a..0d2e1081c4 100644
--- a/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/NodeStartupPerformanceTests.kt
@@ -1,6 +1,7 @@
 package net.corda.node
 
 import com.google.common.base.Stopwatch
+import net.corda.node.driver.FalseNetworkMap
 import net.corda.node.driver.driver
 import org.junit.Ignore
 import org.junit.Test
@@ -12,7 +13,7 @@ class NodeStartupPerformanceTests {
     // Measure the startup time of nodes. Note that this includes an RPC roundtrip, which causes e.g. Kryo initialisation.
     @Test
     fun `single node startup time`() {
-        driver(automaticallyStartNetworkMap = false) {
+        driver(networkMapStrategy = FalseNetworkMap) {
             startNetworkMapService().get()
             val times = ArrayList<Long>()
             for (i in 1 .. 10) {
diff --git a/node/src/main/kotlin/net/corda/node/driver/Driver.kt b/node/src/main/kotlin/net/corda/node/driver/Driver.kt
index e5cfc40980..7c411a9325 100644
--- a/node/src/main/kotlin/net/corda/node/driver/Driver.kt
+++ b/node/src/main/kotlin/net/corda/node/driver/Driver.kt
@@ -102,7 +102,7 @@ interface DriverDSLExposedInterface {
 
     /**
      * Starts a network map service node. Note that only a single one should ever be running, so you will probably want
-     * to set automaticallyStartNetworkMap to false in your [driver] call.
+     * to set networkMapStrategy to FalseNetworkMap in your [driver] call.
      */
     fun startNetworkMapService(): ListenableFuture<Unit>
 
@@ -201,7 +201,7 @@ fun <A> driver(
         debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
         systemProperties: Map<String, String> = emptyMap(),
         useTestClock: Boolean = false,
-        automaticallyStartNetworkMap: Boolean = true,
+        networkMapStrategy: NetworkMapStrategy = DedicatedNetworkMap,
         dsl: DriverDSLExposedInterface.() -> A
 ) = genericDriver(
         driverDsl = DriverDSL(
@@ -210,7 +210,7 @@ fun <A> driver(
                 systemProperties = systemProperties,
                 driverDirectory = driverDirectory.toAbsolutePath(),
                 useTestClock = useTestClock,
-                automaticallyStartNetworkMap = automaticallyStartNetworkMap,
+                networkMapStrategy = networkMapStrategy,
                 isDebug = isDebug
         ),
         coerce = { it },
@@ -412,10 +412,9 @@ class DriverDSL(
         val driverDirectory: Path,
         val useTestClock: Boolean,
         val isDebug: Boolean,
-        val automaticallyStartNetworkMap: Boolean
+        val networkMapStrategy: NetworkMapStrategy
 ) : DriverDSLInternalInterface {
-    private val networkMapLegalName = DUMMY_MAP.name
-    private val networkMapAddress = portAllocation.nextHostAndPort()
+    private val dedicatedNetworkMapAddress = portAllocation.nextHostAndPort()
     val executorService: ListeningScheduledExecutorService = MoreExecutors.listeningDecorator(
             Executors.newScheduledThreadPool(2, ThreadFactoryBuilder().setNameFormat("driver-pool-thread-%d").build())
     )
@@ -488,10 +487,7 @@ class DriverDSL(
                 "rpcAddress" to rpcAddress.toString(),
                 "webAddress" to webAddress.toString(),
                 "extraAdvertisedServiceIds" to advertisedServices.map { it.toString() },
-                "networkMapService" to mapOf(
-                        "address" to networkMapAddress.toString(),
-                        "legalName" to networkMapLegalName.toString()
-                ),
+                "networkMapService" to networkMapStrategy.serviceConfig(dedicatedNetworkMapAddress, name, p2pAddress),
                 "useTestClock" to useTestClock,
                 "rpcUsers" to rpcUsers.map {
                     mapOf(
@@ -578,7 +574,7 @@ class DriverDSL(
     }
 
     override fun start() {
-        if (automaticallyStartNetworkMap) {
+        if (networkMapStrategy.startDedicated) {
             startNetworkMapService()
         }
     }
@@ -586,6 +582,7 @@ class DriverDSL(
     override fun startNetworkMapService(): ListenableFuture<Unit> {
         val debugPort = if (isDebug) debugPortAllocation.nextPort() else null
         val apiAddress = portAllocation.nextHostAndPort().toString()
+        val networkMapLegalName = networkMapStrategy.legalName
         val baseDirectory = driverDirectory / networkMapLegalName.commonName
         val config = ConfigHelper.loadConfig(
                 baseDirectory = baseDirectory,
@@ -595,7 +592,7 @@ class DriverDSL(
                         // TODO: remove the webAddress as NMS doesn't need to run a web server. This will cause all
                         //       node port numbers to be shifted, so all demos and docs need to be updated accordingly.
                         "webAddress" to apiAddress,
-                        "p2pAddress" to networkMapAddress.toString(),
+                        "p2pAddress" to dedicatedNetworkMapAddress.toString(),
                         "useTestClock" to useTestClock
                 )
         )
@@ -603,7 +600,7 @@ class DriverDSL(
         log.info("Starting network-map-service")
         val startNode = startNode(executorService, config.parseAs<FullNodeConfiguration>(), config, quasarJarPath, debugPort, systemProperties)
         registerProcess(startNode)
-        return startNode.flatMap { addressMustBeBound(executorService, networkMapAddress, it) }
+        return startNode.flatMap { addressMustBeBound(executorService, dedicatedNetworkMapAddress, it) }
     }
 
     override fun <A> pollUntilNonNull(pollName: String, pollInterval: Duration, warnCount: Int, check: () -> A?): ListenableFuture<A> {
diff --git a/node/src/main/kotlin/net/corda/node/driver/NetworkMapStrategy.kt b/node/src/main/kotlin/net/corda/node/driver/NetworkMapStrategy.kt
new file mode 100644
index 0000000000..4926782785
--- /dev/null
+++ b/node/src/main/kotlin/net/corda/node/driver/NetworkMapStrategy.kt
@@ -0,0 +1,47 @@
+package net.corda.node.driver
+
+import com.google.common.net.HostAndPort
+import net.corda.core.utilities.DUMMY_MAP
+import org.bouncycastle.asn1.x500.X500Name
+
+/**
+ * Instruct the driver how to set up the network map, if at all.
+ * @see FalseNetworkMap
+ * @see DedicatedNetworkMap
+ * @see NominatedNetworkMap
+ */
+abstract class NetworkMapStrategy(internal val startDedicated: Boolean, internal val legalName: X500Name) {
+    internal abstract fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort): Map<String, String>?
+}
+
+private fun toServiceConfig(address: HostAndPort, legalName: X500Name) = mapOf(
+        "address" to address.toString(),
+        "legalName" to legalName.toString()
+)
+
+abstract class AbstractDedicatedNetworkMap(start: Boolean) : NetworkMapStrategy(start, DUMMY_MAP.name) {
+    override fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort) = toServiceConfig(dedicatedAddress, legalName)
+}
+
+/**
+ * Do not start a network map.
+ */
+object FalseNetworkMap : AbstractDedicatedNetworkMap(false)
+
+/**
+ * Start a dedicated node to host the network map.
+ */
+object DedicatedNetworkMap : AbstractDedicatedNetworkMap(true)
+
+/**
+ * As in gradle-based demos, nominate a node to host the network map, so that there is one fewer node in total than in the [DedicatedNetworkMap] case.
+ * Will fail if the port you pass in does not match the P2P port the driver assigns to the named node.
+ */
+class NominatedNetworkMap(legalName: X500Name, private val address: HostAndPort) : NetworkMapStrategy(false, legalName) {
+    override fun serviceConfig(dedicatedAddress: HostAndPort, nodeName: X500Name, p2pAddress: HostAndPort) = if (nodeName != legalName) {
+        toServiceConfig(address, legalName)
+    } else {
+        p2pAddress == address || throw IllegalArgumentException("Passed-in address $address of nominated network map $legalName is wrong, it should be: $p2pAddress")
+        null
+    }
+}
diff --git a/samples/raft-notary-demo/build.gradle b/samples/raft-notary-demo/build.gradle
index 6dd5a66d82..b2348d8940 100644
--- a/samples/raft-notary-demo/build.gradle
+++ b/samples/raft-notary-demo/build.gradle
@@ -7,7 +7,7 @@ apply plugin: 'net.corda.plugins.cordformation'
 apply plugin: 'maven-publish'
 
 ext {
-    deployTo = "./build/nodes"
+    deployTo = "build/nodes"
     notaryType = "corda.notary.validating.raft"
     notaryName = "CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"
     advertisedNotary = "$notaryType|$notaryName"
@@ -53,7 +53,13 @@ publishing {
     }
 }
 
-task generateNotaryIdentity(type: JavaExec) {
+task cleanNodes {
+    doLast {
+        delete deployTo
+    }
+}
+
+task generateNotaryIdentity(type: JavaExec, dependsOn: 'cleanNodes') {
     classpath = sourceSets.main.runtimeClasspath
     main = "net.corda.node.utilities.ServiceIdentityGeneratorKt"
     def nodeDirs = ["$deployTo/Notary1",
@@ -66,7 +72,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
     directory deployTo
     networkMap "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
     node {
-        name "CN=Party,O=R3,OU=corda,L=London,C=UK"
+        name "CN=Alice Corp,O=Alice Corp,L=London,C=UK"
         nearestCity "London"
         advertisedServices = []
         p2pPort 10002
@@ -78,7 +84,7 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
         ]]]
     }
     node {
-        name "CN=Counterparty,O=R3,OU=corda,L=London,C=UK"
+        name "CN=Bob Plc,O=Bob Plc,L=London,C=UK"
         nearestCity "New York"
         advertisedServices = []
         p2pPort 10005
@@ -89,30 +95,30 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', 'generateN
         name "CN=Notary 1,O=R3,OU=corda,L=London,C=UK"
         nearestCity "London"
         advertisedServices = [advertisedNotary]
-        p2pPort 10008
-        rpcPort 10009
+        p2pPort 10009
+        rpcPort 10010
         cordapps = []
-        notaryNodePort 11002
+        notaryNodePort 10008
     }
     node {
         name "CN=Notary 2,O=R3,OU=corda,L=London,C=UK"
         nearestCity "London"
         advertisedServices = [advertisedNotary]
-        p2pPort 10011
-        rpcPort 10012
+        p2pPort 10013
+        rpcPort 10014
         cordapps = []
-        notaryNodePort 11004
-        notaryClusterAddresses = ["localhost:11002"]
+        notaryNodePort 10012
+        notaryClusterAddresses = ["localhost:10008"]
     }
     node {
         name "CN=Notary 3,O=R3,OU=corda,L=London,C=UK"
         nearestCity "London"
         advertisedServices = [advertisedNotary]
-        p2pPort 10014
-        rpcPort 10015
+        p2pPort 10017
+        rpcPort 10018
         cordapps = []
-        notaryNodePort 11006
-        notaryClusterAddresses = ["localhost:11002"]
+        notaryNodePort 10016
+        notaryClusterAddresses = ["localhost:10008"]
     }
 }
 
diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt
index 83329aee23..052c5d065e 100644
--- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt
+++ b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/Main.kt
@@ -1,22 +1,30 @@
 package net.corda.notarydemo
 
+import com.google.common.net.HostAndPort
+import net.corda.core.crypto.appendToCommonName
 import net.corda.core.div
 import net.corda.core.utilities.ALICE
 import net.corda.core.utilities.BOB
 import net.corda.core.utilities.DUMMY_NOTARY
+import net.corda.flows.NotaryFlow
+import net.corda.node.driver.NominatedNetworkMap
+import net.corda.node.driver.PortAllocation
 import net.corda.node.driver.driver
+import net.corda.node.services.startFlowPermission
 import net.corda.node.services.transactions.RaftValidatingNotaryService
 import net.corda.nodeapi.User
+import net.corda.notarydemo.flows.DummyIssueAndMove
 import org.bouncycastle.asn1.x500.X500Name
 import java.nio.file.Paths
 
 /** Creates and starts all nodes required for the demo. */
 fun main(args: Array<String>) {
-    val demoUser = listOf(User("demo", "demo", setOf("StartFlow.net.corda.notarydemo.flows.DummyIssueAndMove", "StartFlow.net.corda.flows.NotaryFlow\$Client")))
-    driver(isDebug = true, driverDirectory = Paths.get("build") / "notary-demo-nodes") {
+    val demoUser = listOf(User("demo", "demo", setOf(startFlowPermission<DummyIssueAndMove>(), startFlowPermission<NotaryFlow.Client>())))
+    val networkMap = NominatedNetworkMap(DUMMY_NOTARY.name.appendToCommonName("1"), HostAndPort.fromParts("localhost", 10009))
+    driver(isDebug = true, driverDirectory = Paths.get("build") / "notary-demo-nodes", networkMapStrategy = networkMap, portAllocation = PortAllocation.Incremental(10001)) {
         startNode(ALICE.name, rpcUsers = demoUser)
         startNode(BOB.name)
-        startNotaryCluster(DUMMY_NOTARY.name, clusterSize = 3, type = RaftValidatingNotaryService.type)
+        startNotaryCluster(X500Name("CN=Raft,O=R3,OU=corda,L=Zurich,C=CH"), clusterSize = 3, type = RaftValidatingNotaryService.type)
         waitForAllNodesToFinish()
     }
 }
diff --git a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt
index 12d84b1d4a..7136c0685a 100644
--- a/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt
+++ b/samples/raft-notary-demo/src/main/kotlin/net/corda/notarydemo/NotaryDemo.kt
@@ -11,6 +11,7 @@ import net.corda.core.getOrThrow
 import net.corda.core.messaging.CordaRPCOps
 import net.corda.core.messaging.startFlow
 import net.corda.core.transactions.SignedTransaction
+import net.corda.core.utilities.BOB
 import net.corda.flows.NotaryFlow
 import net.corda.nodeapi.config.SSLConfiguration
 import net.corda.notarydemo.flows.DummyIssueAndMove
@@ -39,7 +40,7 @@ private class NotaryDemoClientApi(val rpc: CordaRPCOps) {
     private val counterpartyNode by lazy {
         val (parties, partyUpdates) = rpc.networkMapUpdates()
         partyUpdates.notUsed()
-        parties.first { it.legalIdentity.name == X500Name("CN=Counterparty,O=R3,OU=corda,L=London,C=UK") }
+        parties.first { it.legalIdentity.name == BOB.name }
     }
 
     private companion object {
diff --git a/samples/simm-valuation-demo/build.gradle b/samples/simm-valuation-demo/build.gradle
index cd6cd786ff..59bd02d389 100644
--- a/samples/simm-valuation-demo/build.gradle
+++ b/samples/simm-valuation-demo/build.gradle
@@ -108,9 +108,11 @@ task npmInstall(type: Exec) {
     outputs.upToDateWhen { file('src/main/web/node_modules').exists() }
 }
 
-task cleanWeb() << {
-    delete 'src/main/resources/simmvaluationweb'
-    delete 'src/main/web/dist'
+task cleanWeb() {
+    doLast {
+        delete 'src/main/resources/simmvaluationweb'
+        delete 'src/main/web/dist'
+    }
 }
 
 task buildWeb(type: Exec, dependsOn: [cleanWeb, npmInstall]) {
diff --git a/test-utils/src/main/kotlin/net/corda/testing/RPCDriver.kt b/test-utils/src/main/kotlin/net/corda/testing/RPCDriver.kt
index 2b7c4932ab..ff68c13892 100644
--- a/test-utils/src/main/kotlin/net/corda/testing/RPCDriver.kt
+++ b/test-utils/src/main/kotlin/net/corda/testing/RPCDriver.kt
@@ -194,7 +194,7 @@ fun <A> rpcDriver(
         debugPortAllocation: PortAllocation = globalDebugPortAllocation,
         systemProperties: Map<String, String> = emptyMap(),
         useTestClock: Boolean = false,
-        automaticallyStartNetworkMap: Boolean = false,
+        networkMapStrategy: NetworkMapStrategy = FalseNetworkMap,
         dsl: RPCDriverExposedDSLInterface.() -> A
 ) = genericDriver(
         driverDsl = RPCDriverDSL(
@@ -204,7 +204,7 @@ fun <A> rpcDriver(
                         systemProperties = systemProperties,
                         driverDirectory = driverDirectory.toAbsolutePath(),
                         useTestClock = useTestClock,
-                        automaticallyStartNetworkMap = automaticallyStartNetworkMap,
+                        networkMapStrategy = networkMapStrategy,
                         isDebug = isDebug
                 )
         ),
diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt
index d9e5fd3dd2..7d72a466e8 100644
--- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt
+++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierDriver.kt
@@ -78,7 +78,7 @@ fun <A> verifierDriver(
         debugPortAllocation: PortAllocation = PortAllocation.Incremental(5005),
         systemProperties: Map<String, String> = emptyMap(),
         useTestClock: Boolean = false,
-        automaticallyStartNetworkMap: Boolean = true,
+        networkMapStrategy: NetworkMapStrategy = DedicatedNetworkMap,
         dsl: VerifierExposedDSLInterface.() -> A
 ) = genericDriver(
         driverDsl = VerifierDriverDSL(
@@ -88,7 +88,7 @@ fun <A> verifierDriver(
                         systemProperties = systemProperties,
                         driverDirectory = driverDirectory.toAbsolutePath(),
                         useTestClock = useTestClock,
-                        automaticallyStartNetworkMap = automaticallyStartNetworkMap,
+                        networkMapStrategy = networkMapStrategy,
                         isDebug = isDebug
                 )
         ),
diff --git a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt
index 8531e2f53d..098125bb06 100644
--- a/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt
+++ b/verifier/src/integration-test/kotlin/net/corda/verifier/VerifierTests.kt
@@ -13,6 +13,7 @@ import net.corda.core.utilities.ALICE
 import net.corda.core.utilities.DUMMY_NOTARY
 import net.corda.flows.CashIssueFlow
 import net.corda.flows.CashPaymentFlow
+import net.corda.node.driver.FalseNetworkMap
 import net.corda.node.services.config.VerifierType
 import net.corda.node.services.transactions.ValidatingNotaryService
 import org.junit.Test
@@ -34,7 +35,7 @@ class VerifierTests {
 
     @Test
     fun `single verifier works with requestor`() {
-        verifierDriver(automaticallyStartNetworkMap = false) {
+        verifierDriver(networkMapStrategy = FalseNetworkMap) {
             val aliceFuture = startVerificationRequestor(ALICE.name)
             val transactions = generateTransactions(100)
             val alice = aliceFuture.get()
@@ -51,7 +52,7 @@ class VerifierTests {
 
     @Test
     fun `multiple verifiers work with requestor`() {
-        verifierDriver(automaticallyStartNetworkMap = false) {
+        verifierDriver(networkMapStrategy = FalseNetworkMap) {
             val aliceFuture = startVerificationRequestor(ALICE.name)
             val transactions = generateTransactions(100)
             val alice = aliceFuture.get()
@@ -71,7 +72,7 @@ class VerifierTests {
 
     @Test
     fun `verification redistributes on verifier death`() {
-        verifierDriver(automaticallyStartNetworkMap = false) {
+        verifierDriver(networkMapStrategy = FalseNetworkMap) {
             val aliceFuture = startVerificationRequestor(ALICE.name)
             val numberOfTransactions = 100
             val transactions = generateTransactions(numberOfTransactions)
@@ -99,7 +100,7 @@ class VerifierTests {
 
     @Test
     fun `verification request waits until verifier comes online`() {
-        verifierDriver(automaticallyStartNetworkMap = false) {
+        verifierDriver(networkMapStrategy = FalseNetworkMap) {
             val aliceFuture = startVerificationRequestor(ALICE.name)
             val transactions = generateTransactions(100)
             val alice = aliceFuture.get()