Creating nodes locally

Local nodes are used for testing and demo purposes only.

There are two ways you can create a node locally:

  • Manually: create a local directory, add the relevant node and CorDapp files, and configure them.
  • Automatically: use the Cordform or Dockerform gradle plug-ins, which automatically generate and configure a local set of nodes.

To create a local node manually, make a new directory and add the following files and sub-directories:

  • The Corda JAR artifact file, downloaded from Maven.
  • A node configuration file with a name node.conf, configured as described in the Node configuration section.
  • A sub-directory with a name cordapps, containing any CorDapp JAR files you want the node to load.
  • An up-to-date version of the network-parameters file (see The network map), generated by the bootstrapper tool.

The remaining node files and directories will be generated at runtime. These are described in the Node folder structure section.

  1. Remove any transactionIsolationLevel, initialiseSchema, or initialiseAppSchema entries from the database section of your configuration.
  2. Start the node with run-migration-scripts sub-command with --core-schemas and --app-schemas:
java -jar corda.jar run-migration-scripts --core-schemas --app-schemas

The node will perform any automatic data migrations required, which may take some time. If the migration process is interrupted, it can be continued simply by starting the node again, without harm. The node will stop automatically when migration is complete. See Upgrading your node to Corda 4.8 for more information.

Corda provides two gradle plug-ins: Cordform and Dockerform. They both allow you to run tasks that automatically generate and configure a local set of nodes for testing and demonstration purposes.

  • Nodes deployed via Dockerform use Docker containers. A Dockerform task is similar to Cordform but it provides an extra file that enables you to easily spin up nodes using docker-compose. This creates a docker-compose file that enables you to run a single command to control the deployment of Corda nodes and databases (instead of deploying each node/database manually).
  • For more information about the plugins, visit the Dockerform and Cordform pages.

You need both Docker and docker-compose installed and enabled to use this method. Docker CE (Community Edition) is sufficient. Please refer to Docker CE documentation and Docker Compose documentation for installation instructions for all major operating systems.

Dockerform supports the following configuration options for each node:

  • name
  • notary
  • cordapps
  • rpcUsers
  • useTestClock

You do not need to specify the node ports because every node has a separate container so no ports conflicts will occur. Every node will expose port 10003 for RPC connections. Docker will then map these to available ports on your host machine.

You should interact with each node via its shell over SSH - see the node configuration options for more information.

To enable the shell, you need to set the sshdPort number for each node in the gradle task - this is explained in the section run the Dockerform task further below. For example:

node {
    name "O=PartyA,L=London,C=GB"
    p2pPort 10002
    rpcSettings {
        address("localhost:10003")
        adminAddress("localhost:10023")
    }
    rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
    sshdPort 2223
}

The Docker image associated with each node can be configured in the Dockerform task. This will initialise every node in the Dockerform task with the specified Docker image. If you need nodes with different Docker images, you can edit the docker-compose.yml file with your preferred image.

You can configure Dockerform to use a standalone database to test with non-H2 databases. For example, to use PostgresSQL, you need to make the following changes to your CorDapp project:

  1. Create a file called postgres.gradle in your Cordapp directory, and insert the following code block:
ext {
    postgresql_version     = '42.2.12'
    postgres_image_version = '11'
    dbUser                 = 'myuser'
    dbPassword             = 'mypassword'
    dbSchema               = 'myschema'
    dbName                 = 'mydb'
    dbPort                 = 5432
    dbHostName             = 'localhost'
    dbDockerfile           = 'Postgres_Dockerfile'
    dbInit                 = 'Postgres_init.sh'
    dbDataVolume           =  [
            hostPath      : 'data',
            containerPath : '/var/lib/postgresql/data:\${SUFFIX}',
            containerPathArgs   : [
                    SUFFIX : "rw"
            ]
    ]
    postgres = [
            dataSourceProperties: [
                    dataSourceClassName: 'org.postgresql.ds.PGSimpleDataSource',
                    dataSource: [
                            user    : dbUser,
                            password: dbPassword,
                            url     : "jdbc:postgresql://\${DBHOSTNAME}:\${DBPORT}/\${DBNAME}?currentSchema=\${DBSCHEMA}",
                            urlArgs : [
                                    DBHOSTNAME  : dbHostName,
                                    DBPORT      : dbPort,
                                    DBNAME      : dbName,
                                    DBSCHEMA    : dbSchema
                            ]
                    ]
            ],
            database: [
                    schema                   : dbSchema
            ],
            dockerConfig: [
                    dbDockerfile    : dbDockerfile,
                    dbDockerfileArgs: [
                         DBNAME         : dbName,
                         DBSCHEMA       : dbSchema,
                         DBUSER         : dbUser,
                         DBPASSWORD     : dbPassword,
                         DBPORT         : dbPort
                    ],
                    dbUser          : dbUser,
                    dbPassword      : dbPassword,
                    dbSchema        : dbSchema,
                    dbName          : dbName,
                    dbPort          : dbPort,
                    dbHostName      : dbHostName,
                    dbDatabase      : dbName,
                    dbDataVolume    : dbDataVolume
            ]
    ]
}

apply plugin: 'net.corda.plugins.cordformation'

dependencies {
    cordaDriver "org.postgresql:postgresql:$postgresql_version"
}

def generateInitScripts = tasks.register('generateInitScripts') { Task task ->
    def initialDockerfile = file("$buildDir/$dbDockerfile")
    def initialScript = file( "$buildDir/$dbInit")
    task.inputs.properties(project['postgres'])
    task.outputs.files(initialDockerfile, initialScript)
    /*
     * Dockerfile to initialise the PostgreSQL database.
     */
    task.doLast {
        initialDockerfile.withPrintWriter('UTF-8') { writer ->
            writer << """\
# Derive from postgres image
FROM postgres:$postgres_image_version

ARG DBNAME=$dbName
ARG DBSCHEMA=$dbSchema
ARG DBUSER=$dbUser
ARG DBPASSWORD=$dbPassword
ARG DBPORT=$dbPort

ENV POSTGRES_DB=\$DBNAME
ENV POSTGRES_DB_SCHEMA=\$DBSCHEMA
ENV POSTGRES_USER=\$DBUSER
ENV POSTGRES_PASSWORD=\$DBPASSWORD
ENV PGPORT=\$DBPORT

# Copy all postgres init file to the docker entrypoint
COPY ./$dbInit /docker-entrypoint-initdb.d/$dbInit

# Allow postgres user to run init script
RUN chmod 0755 /docker-entrypoint-initdb.d/$dbInit
"""
        }

        /**
         * Append the persistence configuration if persistence is required (i.e., persistence=true)
         */
        if (project.hasProperty("dbDataVolume")) {

            initialDockerfile.withWriterAppend('UTF-8') { writer ->
                writer << """\

# Associate the volume with the host user
USER 1000:1000

# Initialise environment variable with database directory
ENV PGDATA=/var/lib/postgresql/data/pgdata
"""
            }
        }

        /*
         * A UNIX script to generate the init.sql file that
         * PostgreSQL needs. This must use UNIX line endings,
         * even when generated on Windows.
         */
        initialScript.withPrintWriter('UTF-8') { writer ->
            writer << """\
#!/usr/bin/env bash
# Postgres database initialisation script when using Docker images

dbUser=\${POSTGRES_USER:-"$dbUser"}
dbPassword=\${POSTGRES_PASSWORD:-"$dbPassword"}
dbSchema=\${POSTGRES_DB_SCHEMA:-"$dbSchema"}
dbName=\${POSTGRES_DB:-"$dbName"}

psql -v ON_ERROR_STOP=1 --username "\$dbUser"  --dbname "\$dbName" <<-EOSQL
        CREATE SCHEMA \$dbSchema;
        GRANT USAGE, CREATE ON SCHEMA \$dbSchema TO \$dbUser;
        GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES ON ALL tables IN SCHEMA \$dbSchema TO \$dbUser;
        ALTER DEFAULT privileges IN SCHEMA \$dbSchema GRANT SELECT, INSERT, UPDATE, DELETE, REFERENCES ON tables TO \$dbUser;
        GRANT USAGE, SELECT ON ALL sequences IN SCHEMA \$dbSchema TO \$dbUser;
        ALTER DEFAULT privileges IN SCHEMA \$dbSchema GRANT USAGE, SELECT ON sequences TO \$dbUser;
        ALTER ROLE \$dbUser SET search_path = \$dbSchema;
EOSQL
""".replaceAll("\r\n", "\n")
        }
        initialScript.executable = true
    }
}
  1. In the build.gradle file, add the following code:
  • To apply the postgres.gradle script, add apply from: 'postgres.gradle'.
  • Add gradle task generateInitScripts to the dependsOn list of the prepareDockerNodes task.
  • Add the dockerConfig element.
  • Initialise it with the postgres block.

An example is shown below:

apply from: 'postgres.gradle'

task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar',  'generateInitScripts']) {

    [...]

    node {
        [...]
    }

    // The postgres block from the postgres.gradle file
    dockerConfig = postgres
}

The postgres.gradle file includes the following:

  • A gradle task called generateInitScripts used to generate the Postgres Docker image files.
  • A set of variables used to initialise the Postgres Docker image.

To set up the external database, you must place the following two files in the build directory:

  • Postgres_Dockerfile - a wrapper for the base Postgres Docker image.
  • Postgres_init.sh - a shell script to initialise the database.

The Postgres_Dockerfile is referenced in the docker-compose.yml file and allows for a number of arguments for configuring the Docker image.

You can use the following configuration parameters in the postgres.gradle file:

ParameterDescription
postgresql_versionVersion of JDBC driver to connect to the database
postgres_image_versionVersion of Postgres Docker image
dbUserDatabase user
dbPasswordDatabase password
dbSchemaPostgres schema
dbNameDatabase name
dbPortDatabase port (default: 5432)
dbHostNameDatabase host (default: localhost)
dbInitInitialisation script for Postgres Docker image
dbDockerfileWrapper of base Postgres Docker image
dbDataVolumePath to database files for Postgres Docker image

To make the database files persistent across multiple docker-compose runs, you must set the dbDataVolume parameter. If this variable is commented out, the database files will be removed after every docker-compose run.

To run the Dockerform task, follow the steps below.

  1. Open the build.gradle file of your CorDapp project and add a new gradle task, as shown in the example below.
task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar']) {
    // set docker image for each node
    dockerImage = "corda/corda-zulu-java1.8-4.4"

    nodeDefaults {
        cordapp project(":contracts-java")
    }
    node {
        name "O=Notary,L=London,C=GB"
        notary = [validating : false]
        p2pPort 10002
        rpcSettings {
            address("localhost:10003")
            adminAddress("localhost:10023")
        }
        projectCordapp {
            deploy = false
        }
        cordapps.clear()
        sshdPort 2222
    }
    node {
        name "O=PartyA,L=London,C=GB"
        p2pPort 10002
        rpcSettings {
            address("localhost:10003")
            adminAddress("localhost:10023")
        }
        rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
        sshdPort 2223
    }
    node {
        name "O=PartyB,L=New York,C=US"
        p2pPort 10002
        rpcSettings {
            address("localhost:10003")
            adminAddress("localhost:10023")
        }
        rpcUsers = [[user: "user1", "password": "test", "permissions": ["ALL"]]]
        sshdPort 2224
    }

    // This property needs to be outside the node {...} elements
    dockerImage = "corda/corda-zulu-java1.8-4.8"
}
  1. To create the nodes defined in the prepareDockerNodes gradle task added in the first step, run the following command in a command prompt or a terminal window, from the root of the project where the prepareDockerNodes task is defined:
  • Linux/macOS: ./gradlew prepareDockerNodes
  • Windows: gradlew.bat prepareDockerNodes

This command creates the nodes in the build/nodes directory. A node directory is generated for each node defined in the prepareDockerNodes task. The task also creates a docker-compose.yml file in the build/nodes directory.

Was this page helpful?

Thanks for your feedback!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.

We're sorry this page wasn't helpful. Let us know how we can make it better!

Chat with us

Chat with us on our #docs channel on slack. You can also join a lot of other slack channels there and have access to 1-on-1 communication with members of the R3 team and the online community.

Create an issue

Create a new GitHub issue in this repository - submit technical feedback, draw attention to a potential documentation bug, or share ideas for improvement and general feedback.

Propose documentation improvements directly

Help us to improve the docs by contributing directly. It's simple - just fork this repository and raise a PR of your own - R3's Technical Writers will review it and apply the relevant suggestions.