Install and manage a node#

At a glance

Use Docker Compose to run a mainnet or testnet node on Linux, macOS, or Windows. You need Docker Engine and the Compose plugin. After this procedure, your node runs in a Docker container.

The concordium/node Docker image includes the node, the collector, and both network genesis files. Select the network in the Compose file.

If you are running a node already, avoid catching up from scratch using the matching migration guide:

Prerequisites#

  • Meet the node requirements.

  • Make sure that the selected image is available for your host platform.

Install Docker Engine and the Compose plugin.

Install and start Docker Desktop for Mac. Allocate resources that meet the node requirements. Keep Docker Desktop running while the node runs; container restart policies do not start Docker Desktop itself after a reboot.

Install WSL2 with Ubuntu, then install Docker Engine and the Docker Compose plugin inside Ubuntu. Docker Desktop is not required. Ensure WSL2 has sufficient resources to meet the node requirements.

Enable systemd in WSL2. Inside Ubuntu, verify that PID 1 reports systemd and that Docker and Compose work:

Ubuntu WSL2 (Bash)#
ps -p 1 -o comm=
sudo docker version
sudo docker compose version

Use sudo for Docker commands inside Ubuntu if your user does not have Docker socket access. Systemd does not start WSL2 when Windows boots; see Start automatically at system boot (optional) for optional boot-time startup.

Configure a node#

  1. Create a directory for storing node configuration and data, like mainnet-node or testnet-node.

  2. In the directory, create compose.yaml from one the network-configuration examples below.

  3. Replace the collector name with your desired node’s dashboard name, note this name will be publicly visible.

Mainnet mainnet-node/compose.yaml example
services:
  node:
    image: concordium/node:latest
    container_name: mainnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8888:8888"
      - "127.0.0.1:20000:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/mainnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.mainnet.concordium.software:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.mainnet.concordium.software/blocks.idx
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-mainnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.mainnet.concordium.software/nodes/post
Testnet testnet-node/compose.yaml example
services:
  node:
    image: concordium/node:latest
    container_name: testnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8889:8889"
      - "127.0.0.1:20001:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/testnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.testnet.concordium.com:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.testnet.concordium.com/blocks.idx
      CONCORDIUM_NODE_LISTEN_PORT: "8889"
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-testnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.testnet.concordium.com/nodes/post

The current image uses AMD64. platform selects that architecture; Apple Silicon Macs require emulation. Check the selected image tag’s architecture and verify that your node keeps up with the network.

Mainnet mainnet-node/compose.yaml example
services:
  node:
    image: concordium/node:latest
    platform: linux/amd64
    container_name: mainnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8888:8888"
      - "127.0.0.1:20000:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/mainnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.mainnet.concordium.software:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.mainnet.concordium.software/blocks.idx
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-mainnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.mainnet.concordium.software/nodes/post
Testnet testnet-node/compose.yaml example
services:
  node:
    image: concordium/node:latest
    platform: linux/amd64
    container_name: testnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8889:8889"
      - "127.0.0.1:20001:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/testnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.testnet.concordium.com:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.testnet.concordium.com/blocks.idx
      CONCORDIUM_NODE_LISTEN_PORT: "8889"
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-testnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.testnet.concordium.com/nodes/post

On Windows 10 and 11, run Docker Engine directly inside Ubuntu WSL2, without Docker Desktop. Create a separate directory for each network and save the appropriate configuration as compose.yaml.

Mainnet mainnet-node/compose.yaml example
Docker Compose - compose.yaml (Mainnet)#
services:
  node:
    image: concordium/node:latest
    container_name: mainnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8888:8888"
      - "127.0.0.1:20000:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/mainnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.mainnet.concordium.software:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.mainnet.concordium.software/blocks.idx
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-mainnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.mainnet.concordium.software/nodes/post
Testnet testnet-node/compose.yaml example
Docker Compose - compose.yaml (Testnet)#
services:
  node:
    image: concordium/node:latest
    container_name: testnet-node
    restart: unless-stopped
    stop_grace_period: 5m
    ports:
      - "8889:8889"
      - "127.0.0.1:20001:20000"
    volumes:
      - ./data:/mnt/data
    environment:
      CONCORDIUM_NODE_CONSENSUS_GENESIS_DATA_FILE: /genesis/testnet-genesis.dat
      CONCORDIUM_NODE_CONNECTION_BOOTSTRAP_NODES: bootstrap.testnet.concordium.com:8888
      CONCORDIUM_NODE_CONSENSUS_DOWNLOAD_BLOCKS_FROM: https://catchup.testnet.concordium.com/blocks.idx
      CONCORDIUM_NODE_LISTEN_PORT: "8889"
      CONCORDIUM_NODE_COLLECTOR_ENABLED: "true"
      CONCORDIUM_NODE_COLLECTOR_NODE_NAME: my-testnet-node
      CONCORDIUM_NODE_COLLECTOR_URL: https://dashboard.testnet.concordium.com/nodes/post

The ./data directory stores the blockchain database and node data persistently inside Ubuntu WSL2. Keep Mainnet and Testnet in separate directories, each with its own compose.yaml and data directory.

The examples mount a local ./data directory beside the compose.yaml ensuring the node data and configurations are persisted across restart and updates.

The examples give public access to the P2P port and limit host gRPC access to the loopback interface. Testnet uses host port 20001 and container port 20000.

They setup the network specific genesis data and the concordium-provided bootstrapper nodes, source for out-of-band blocks (improves catchup time) and enables the collector handling telemetrics which allows the node to show up in the various blockchain explorers.

Note

The image runs as a non-root user with user ID (UID) 10001 and group ID (GID) 10001. This intentionally limits the process’s permissions inside the container. The container user must be able to write to the mounted data/ directory, therefore instructions below cover creating this directory and handing ownership to the container user.

Start the node#

  1. Open a terminal in the directory that contains compose.yaml from the previous section.

  2. For a new node, we first have to create the local state directory and give container user ownership:

    $ mkdir data
    $ sudo chown 10001:10001 data
    
  3. Start the node using

    $ docker compose up --detach
    

    For network and TLS settings, see Advanced node configuration. For startup or reporting problems, see Troubleshoot a node.

  4. Verify the node is running by inspecing the logs:

    $ docker compose logs --follow
    

Start automatically at system boot (optional)#

The Compose configuration uses restart: unless-stopped. This policy can restart the container when Docker starts, but it does not start Docker itself or a WSL2 distribution. A container you explicitly stop remains stopped until you start it again.

Configure automatic startup only after verifying that your node runs correctly.

On a systemd-based host, enable Docker Engine at system boot:

$sudo systemctl enable --now docker

After a reboot, confirm that Docker and the node are running. Check the node logs, peer connections, and recent, advancing finalized blocks. If enabled, check collector reporting and validator status as well.

Docker Desktop must be running before the container can restart. The container restart policy does not launch Docker Desktop after a reboot. Starting Docker Desktop at user sign-in is not the same as starting a node at system boot without a user signing in.

Enable Docker Engine inside Ubuntu WSL2:

Ubuntu WSL2 (Bash)#
sudo systemctl enable --now docker

This starts Docker when Ubuntu starts, not when Windows boots. To run the node without requiring a user to sign in, configure a Windows Task Scheduler task that starts Ubuntu WSL2 at system startup and keeps the distribution running.

Important

The scheduled task must run under the Windows account that owns the Ubuntu WSL2 distribution. Use the account password rather than an interactive logon session. Run the following commands in an elevated Windows PowerShell session.

Confirm that your Ubuntu distribution uses WSL version 2. Note its exact name, as shown by wsl --list --verbose. The examples below use Ubuntu-24.04; replace it with your registered distribution name.

Create the startup task:

Windows PowerShell (Administrator)#
$taskName = "Concordium WSL Autostart"
$credential = Get-Credential -Message "Enter your Windows account credentials"

$action = New-ScheduledTaskAction `
    -Execute "$env:WINDIR\System32\wsl.exe" `
    -Argument "-d Ubuntu-24.04 -u root -- /usr/bin/tail -f /dev/null"

$trigger = New-ScheduledTaskTrigger -AtStartup

$settings = New-ScheduledTaskSettingsSet `
    -ExecutionTimeLimit (New-TimeSpan -Seconds 0) `
    -RestartCount 3 `
    -RestartInterval (New-TimeSpan -Minutes 1) `
    -MultipleInstances IgnoreNew

Register-ScheduledTask `
    -TaskName $taskName `
    -Action $action `
    -Trigger $trigger `
    -Settings $settings `
    -User $credential.UserName `
    -Password $credential.GetNetworkCredential().Password `
    -RunLevel Highest

In Ubuntu WSL2, open the directory containing compose.yaml and verify the container restart policy:

Ubuntu WSL2 (Bash)#
sudo docker inspect $(sudo docker compose ps -q node) --format 'Status={{.State.Status}} RestartPolicy={{.HostConfig.RestartPolicy.Name}}'

The expected values are Status=running and RestartPolicy=unless-stopped.

Restart Windows and do not sign in immediately. Check collector reporting from another device, if available. Once you sign in, check the node logs, peer connections, and recent, advancing finalized blocks. For validators, verify the expected identity and status.

Before stopping the node permanently, disable the startup task in an elevated Windows PowerShell session:

Windows PowerShell (Administrator)#
Disable-ScheduledTask -TaskName "Concordium WSL Autostart"

Upgrade the node#

  1. Read the release notes for any additional instructions.

  2. Stop the node by running docker compose stop from the directory that contains compose.yaml.

  3. Check the logs for a clean shutdown docker compose logs.

  4. If the Compose file uses a fixed tag, update the image entry to the new version. Then run docker compose pull to download the selected image. Run this command for both latest and fixed tags.

  5. Start the node with docker compose up --detach.

  6. Verify the node is running by inspecing the logs:

    $ docker compose logs --follow
    

Stop or remove the node#

To stop and take down the node run:

$ docker compose stop
$ docker compose down

The example configurations use 5 minute shutdown grace period for the node to stop. Check the logs for a clean shutdown.

Warning

If you delete the local state directory, you lose its data and configuration. Do not delete this directory during migration or rollback. First, check that the new installation operates correctly. Then, remove old state or backups only as permitted by your retention policy.

Run a validator node#

For a new validator, see Import validator keys. For an existing Debian validator, follow Ubuntu. For an existing macOS validator, follow macOS. Use the existing validator keys. Do not generate new keys or register them again.

Was this article helpful?