Apache Ignite is an open-source, distributed database and computing platform designed for high-performance, real-time data processing. It operates as an in-memory or hybrid storage system, delivering sub-millisecond data access and horizontal scalability across cluster nodes.

While commercial offerings like GridGain add enterprise capabilities on top of Ignite, Apache Ignite gives you full SQL capability, distributed consensus, and fault tolerance — completely free and open-source under the Apache 2.0 license.

In this guide, we will set up a local 3-node Apache Ignite 3 cluster using Docker Compose, walk through cluster initialization, execute SQL statements, and cover a full teardown.

1. Prepare the Cluster Configuration (docker-compose.yml)

Our cluster consists of three independent nodes (node1, node2, and node3) communicating on internal port 3344. Each node exposes two main endpoints to the host:

  • HTTP REST API (10300): Used for cluster administration, topology health, and management tasks.
  • JDBC Thin Client (10800): Used for client SQL connections and transactional queries.

Create a file named docker-compose.yml in your working directory:

name: ignite3

x-ignite-def: &ignite-def
  image: apacheignite/ignite:3.1.0
  environment:
    JVM_MAX_MEM: "4g"
    JVM_MIN_MEM: "4g"
    BOOTSTRAP_NODE_CONFIG: /opt/ignite/etc/ignite-config.conf
  configs:
    - source: node_config
      target: /opt/ignite/etc/ignite-config.conf
      mode: 0644

services:
  node1:
    <<: *ignite-def
    command: --node-name node1
    ports:
      - "10300:10300"
      - "10800:10800"
    volumes:
      - ./data/node1:/opt/ignite/work

  node2:
    <<: *ignite-def
    command: --node-name node2
    ports:
      - "10301:10300"
      - "10801:10800"
    volumes:
      - ./data/node2:/opt/ignite/work

  node3:
    <<: *ignite-def
    command: --node-name node3
    ports:
      - "10302:10300"
      - "10802:10800"
    volumes:
      - ./data/node3:/opt/ignite/work

configs:
  node_config:
    content: |
      ignite {
        network {
          port: 3344
          nodeFinder.netClusterNodes = ["node1:3344", "node2:3344", "node3:3344"]
        }
        "storage": {
          "profiles": [
            {
              name: "rocksDbProfile"
              engine: "rocksdb"
            }
          ]
        }
      }

The node_config configuration in the Docker Compose file:

  • Adds a storage profile named rocksDbProfile that uses the RocksDB engine;
  • Sets the storage size to 64MB (67108864 bytes) by default;
  • Stores persistent data in the data directory where docker was run.

2. Launch the Cluster Containers

Bring up the container stack in detached mode:

docker-compose up -d

Expected Output:

[+] Running 5/5
 ✔ Network ignite3_default   Created                                                                   0.1s
 ✔ Config ignite3_node_config Created                                                                  0.0s
 ✔ Container ignite3-node1-1 Started                                                                   0.4s
 ✔ Container ignite3-node2-1 Started                                                                   0.4s
 ✔ Container ignite3-node3-1 Started                                                                   0.4s

Check the containers:

docker-compose ps

Expected Output:

NAME              IMAGE                       COMMAND                  SERVICE   CREATED         STATUS         PORTS
ignite3-node1-1   apacheignite/ignite:3.1.0   "docker-entrypoint.s…"   node1     6 seconds ago   Up 5 seconds   0.0.0.0:10300->10300/tcp, [::]:10300->10300/tcp, 0.0.0.0:10800->10800/tcp, [::]:10800->10800/tcp
ignite3-node2-1   apacheignite/ignite:3.1.0   "docker-entrypoint.s…"   node2     6 seconds ago   Up 5 seconds   0.0.0.0:10301->10300/tcp, [::]:10301->10300/tcp, 0.0.0.0:10801->10800/tcp, [::]:10801->10800/tcp
ignite3-node3-1   apacheignite/ignite:3.1.0   "docker-entrypoint.s…"   node3     6 seconds ago   Up 5 seconds   0.0.0.0:10302->10300/tcp, [::]:10302->10300/tcp, 0.0.0.0:10802->10800/tcp, [::]:10802->10800/tcp

3. Configure the CLI Helper Function

Apache Ignite 3 uses the main container image to execute CLI commands. Java 11+ runtime reflection warnings can clutter command output. We can pass --add-opens flags via JAVA_TOOL_OPTIONS to keep our console output clean.

Run this shell function in your terminal (or append it to your ~/.bashrc / ~/.zshrc):

ignite-cli() {
  docker run --rm -it --network=ignite3_default \
    -e JAVA_TOOL_OPTIONS="--add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED" \
    apacheignite/ignite:3.1.0 cli "$@"
}

4. Initialize and Verify the Cluster

Initialize the metastorage group across all three nodes using the REST endpoint on node1 (port 10300):

ignite-cli cluster init --url http://node1:10300 --name my-cluster \
    --metastorage-group node1,node2,node3

Expected Output:

Cluster was initialized successfully

Check the health and operational status of your newly formed cluster:

ignite-cli cluster status --url http://node1:10300

Expected Output:

[name: my-cluster, nodes: 3, status: active, cmgNodes: [node1, node2, node3], msNodes: [node1, node2, node3]]

Inspecting Storage Configuration (Optional):

ignite-cli node config show --url http://node1:10300 \
    --format JSON ignite.storage

Expected Output:

{
  "engines" : {
    "aimem" : {
      "pageSizeBytes" : 16384
    },
    "aipersist" : {
      "checkpoint" : {
        "checkpointDelayMillis" : 200,
        "checkpointThreads" : 4,
        "compactionThreads" : 4,
        "intervalDeviationPercent" : 40,
        "intervalMillis" : 180000,
        "logReadLockThresholdTimeoutMillis" : 0,
        "readLockTimeoutMillis" : 10000,
        "useAsyncFileIoFactory" : true
      },
      "pageSizeBytes" : 16384
    },
    "rocksdb" : {
      "flushDelayMillis" : 100
    }
  },
  "profiles" : [ {
    "engine" : "rocksdb",
    "name" : "rocksDbProfile",
    "sizeBytes" : -1,
    "writeBufferSizeBytes" : 67108864
  }, {
    "engine" : "aipersist",
    "name" : "default",
    "replacementMode" : "CLOCK",
    "sizeBytes" : -1
  } ]
}

5. Execute SQL Queries

SQL execution in Apache Ignite 3 runs over the JDBC thin client protocol (port 10800). Connect using --jdbc-url to create tables and manipulate data:

Create a Distributed Table

ignite-cli sql --jdbc-url jdbc:ignite:thin://node1:10800 \
  "CREATE TABLE IF NOT EXISTS my_table (id INT PRIMARY KEY, val VARCHAR)"

Expected Output:

Updated 0 rows.

Insert Data

ignite-cli sql --jdbc-url jdbc:ignite:thin://node1:10800 \
  "INSERT INTO my_table VALUES (1, 'Hello World')"

Expected Output:

Updated 1 rows.

Query Data

for node in node1 node2 node3; do
  echo "=== Querying $node ==="
  ignite-cli sql --jdbc-url "jdbc:ignite:thin://${node}:10800" "SELECT * FROM my_table"
done

Expected Output:

=== Querying node1 ===
╔════╤═════════════╗
║ ID │ VAL         ║
╠════╪═════════════╣
║ 1  │ Hello World ║
╚════╧═════════════╝

=== Querying node2 ===
╔════╤═════════════╗
║ ID │ VAL         ║
╠════╪═════════════╣
║ 1  │ Hello World ║
╚════╧═════════════╝

=== Querying node3 ===
╔════╤═════════════╗
║ ID │ VAL         ║
╠════╪═════════════╣
║ 1  │ Hello World ║
╚════╧═════════════╝

6. Connecting to a Node

Execute:

ignite-cli

Expected Output:

Picked up JAVA_TOOL_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED


           #              ___                         __
         ###             /   |   ____   ____ _ _____ / /_   ___
     #  #####           / /| |  / __ \ / __ `// ___// __ \ / _ \
   ###  ######         / ___ | / /_/ // /_/ // /__ / / / // ___/
  #####  #######      /_/  |_|/ .___/ \__,_/ \___//_/ /_/ \___/
  #######  ######            /_/
    ########  ####        ____               _  __           _____
   #  ########  ##       /  _/____ _ ____   (_)/ /_ ___     |__  /
  ####  #######  #       / / / __ `// __ \ / // __// _ \     /_ <
   #####  #####        _/ / / /_/ // / / // // /_ / ___/   ___/ /
     ####  ##         /___/ \__, //_/ /_//_/ \__/ \___/   /____/
       ##                  /____/

                      Apache Ignite CLI version 3.1.0


You appear to have not connected to any node yet. Do you want to connect to the default node http://localhost:10300? [Y/n]

Type "Y" and ENTER.

Then paste the below and press ENTER:

connect http://node1:10300

Expected Output:

Connected to http://node1:10300
[node1]>

Let's see the nodes/partitions.

For this let's move to sql mode. Type sql and press ENTER.

Finally run:

SELECT * FROM SYSTEM.LOCAL_PARTITION_STATES;

Expected Output:

None

7. Complete Clean Teardown

To stop the cluster, clean up Docker networks, and wipe host-mounted data directories for a fresh start:

# Stop containers and remove internal network/volumes
docker-compose down -v

# Clear host bind-mount data
rm -rf ./data                                                                   0.1s

Summary

In this guide, we covered the full lifecycle of a multi-node local cluster:

  1. Configured & Started: Defined a 3-node topology using docker-compose.yml and launched it in the background.
  2. Configured CLI: Created an ignite-cli alias to silence JVM warnings and streamline command invocation.
  3. Initialized & Verified: Bootstrapped the metastorage group across nodes via HTTP REST (10300) and confirmed cluster health.
  4. Executed SQL: Created tables and queried data over the thin client protocol (10800).
  5. Cleaned Up: Removed container resources and purged local data directories with docker-compose down -v and rm -rf ./data.

By leveraging Apache Ignite 3 and Docker Compose, you can simulate a production-grade multi-node environment on your local machine with full SQL and REST API capabilities in just a few shell commands.

Further Readings

📣 Call to Action

If you are interested in following along with my journey, I invite you to dive into all the details provided below:

Thanks for reading