Dockerform task

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 gradle task generateInitScripts to the dependsOn list of the prepareDockerNodes task, add the dockerConfig element, and initialise it with the postgres block. An example is shown below:
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.

If you configure an external database, a Postgres_Dockerfile file and Postgres_init.sh file are also generated in the build directory. If you make any changes to your CorDapp source or prepareDockerNodes task, you will need to re-run the task to see the changes take effect.

If the external database is not defined and configured properly, as described in specifying an external database, the files Postgres_Dockerfile and Postgres_init.sh will not be generated.

In this case, each Corda node is associated with a Postgres database. Only one Corda node can connect to the same database. While there is no maximum number of nodes you can deploy with Dockerform, you are constrained by the maximum available resources on the machine running this task, as well as the overhead introduced by every Docker container that is started. All the started nodes run in the same Docker overlay network.

The connection settings to the Postgres database are provided to each node through the postgres.gradle file. The Postgres JDBC driver is provided via Maven as part of the cordaDrive gradle configuration, which is also specified in the dependencies block of the postgres.gradle file.

Note that this feature is not designed for users to access the database via elevated or admin rights - you must only use such configuration changes for testing/development purposes.

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.