Building a Cardano Pool using an Air-Gapped Raspberry Pi

When building blockchain servers, it is essential to build an air-gapped server (or “cold environment”). It is easy to be lazy in this area and not bother (especially if you are in a hurry), but this is a requirement rather than a nice to have. Here’s the why/what:

  • Protects against key-logging attacks, malware/virus based attacks and other firewall or security exploits.
  • Physically isolated from the rest of your network.
  • Must not have a network connection, wired or wireless.
  • Is not a VM on a machine with a network connection.

I found it challenging to do the above 100% of the time as updates, toolchain builds, looking up how-to’s and just general playing was tough. But it’s pretty close and hopefully this tutorial will reduce any online time.

So what to use? Probably an old Intel-based laptop running Ubuntu would do as you just copy your binaries across. I didn’t have one lying around and how boring. But what caught my eye was this:

Raspberry Pi 400 packs a full computer into a keyboard for ...

It’s a Raspberry Pi 400 – a Raspberry 4 inside a keyboard! Hook up a mouse, mini-HDMI cable and USB-C for power and you’re off to the races. Much more exciting.

It comes with a 16GB microSD, however it is almost full on arrival. To do much with it you’ll delete half the apps. I had a GoPro lying around with a 128GB card in it (it hasn’t been used much in the last year for obvious reasons). I’ll use Ubuntu as it supports 64bit and I’m using it for all of my servers. This Pi has 4GB of RAM, so a 32-bit version of any OS could be used, but 64-bit is everything and all the produced binaries in the tutorial will be for that. The instructions are here – just pick the top Ubuntu option:

To digress, microSD is damn slow. Some of these toolchains took many hours to build (just to find some dependency error 2 hours in). 4GB of RAM doesn’t help, as the swapping kills it. You can use an external SSD and the Pi will boot off that nicely and with large speed improvements according to here (especially if you are inclined to run a Cardano relay). So that is on the wish list.

Firstly, increase your swap to 4GB of higher (8GB will do nicely): Building the chains used at least 3GB of swap and should be there anyway (the default 100MB is useless).

To build the relay on a regular amd64 server there are some great tools here: How to setup an external passive relay node and running that for each relay/block producer. To be honest, these scripts make the pool setup almost essential (especially after wasting hours following other tutorials).

There are no arm64 binaries for cardano-cli that I could easily find, so it needs to be built (we also build cardano-node so your Pi can be a relay!). To begin, ghc (the Haskell compiler) and cabal (the npm/maven equivalent for Haskell) have to be built. I’d never used these, so challenge accepted.

Note: be careful following the Raspberry Pi forums – most compiler issues are for the 32-bit OS. Use the aarch64 distributions instead. GHC source can downloaded from and pick the aarch64 version (i.e. ghc-8.10.3-aarch64-deb10-linux.tar.xz) . Cabal source can be downloaded via:, GMP (GNU Multiple Precision Arithmetic Library):, bech32: libsodium/cardano-node/cardano-addresses are downloaded from their respective git repositories.

Since none of us want to waste hours of time, the aarch64 binaries of cardano-cli and friends that were built for this project can be downloaded from:

cadano-tools.tar.gz (v1.25.1)

Unfortunately ghc/cabal aren’t included as the zipped tar was > 500MB! (available on request).

When building from source, just follow the usual instructions in I used GHC 8.10.3 and while these instructions are for v1.24.2, the instructions are basically the same. There were a couple of missing dependencies which I can’t remember, but were easy to install/build. Put the GHC/Cabal binaries in /usr/local/bin (I didn’t bother with $HOME/.local/bin or the extra environment variables).

While cardano-cli/cardano-node were quite easy to build (although time consuming!), cardano-address wasn’t trivial at the time and took some trial and error to complete the build (note: bech32 is also a requirement).

The Scripts

The scripts to set up the stake pool are here: and can be used for any cold environment – not just for the Raspberry Pi. The required environment variables are:

$ADA_USB_MNT – the location of the mounted USB stick for both cold/hot environments e.g. /media/usb/cardano. This can reference an encrypted USB and is the only essential cold environment variable.

$NODE_HOME – the hot location of the pool (e.g. /opt/cardano/cnode)

$NODE_GENESIS – the hot relative location of the genesis file (e.g. files/mainnet-shelley-genesis.json)

$CARDANO_NODE_SOCKET_PATH – the hot location of the cardano node socket (normally $NODE_HOME/sockets/node0.socket)

It assumes you have a Cardano Relay set up which will eventually be a Block Producer when all steps are completed. You should also have the Coin Cashew how-to open as a reference: The scripts are based on these very closely.

The numbering of the scripts matches the chapter numbers of the guide, so you can see where you are in the guide. Follow the ordering in sequence. “BP” stands for “Block Producer” (i.e. the hot environment) and “AG” stands for “Air Gap” (i.e. the cold environment).

Run each script with sudo -E <script name> to make sure the USB receives the files (as the user id’s will be different). – Key Evolving Signature (KES) keys that are regularly updated. kes.vkey shouldn’t be on the BP. Take note of the “start KES period” as this will be used in the next step <start KES period> – the start KES period is from the previous step. Note: node.skey/node.vkey really needs to be put somewhere safe as it is used to sign/verify all node transactions. It is backed up onto the USB by default but shouldn’t ever be mounted onto the BP. – Verifiable Random Function (VRF) keys of which both are stored on the BP (vrf.skey should be set to read-only).

At this point, copy the files in pool_dir across to your pool directory (i.e. $NODE_HOME/priv/pool/ The node can be restarted and the certs/keys will be automatically used. should detect a block producer.

The next step is to create the payment and stake keys. <mnemonics> – an offline wallet has been previously created and used as an argument in this step. Note: stake.skey/stake.vkey are backed up onto the USB by default and shouldn’t ever be mounted onto the BP.

Deposit it least ₳500 + ₳2 + <pledged staking amount> + <fees> to your wallet. Any deposits/pledged amounts will eventually be shown in payment.addr. – confirms that funds have been transferred via the address in payment.addr (or you can check in

The following steps register the stake address to the network for a fee of ₳2 (“key deposit”). Fees were ~₳0.18 at the time.

The next series of steps registers the pool information. A JSON file needs to be created and publicly uploaded (i.e. poolMetaData.json on github) with a short URL generated to reference this file:

"name": "",
"description": " Cardano pool",
"ticker": "HWC",
"homepage": ""

Next there is a pool deposit of ₳500 and the pledge to set. Fees were ~₳0.2 at the time. <pool metadata URL> – the JSON metadata file is downloaded and converted into a hash. <pledge> <relay host> <relay ip> <short url> – the relay details are set and the pool certificates are created.

At this point the pool should be created. The next steps retrieve the pool id and verify that the stake pool was created.

Other helper scripts: – when the KES keys need to be rotated. Run and then this script. See 18.1 of the Coin Cashew how-to. – if the pledge or details change, then run through the 12.x steps with the 3rd step replaced with this script. See 18.4 of the Coin Cashew how-to.


On the relay’s $NODE_HOME/scripts run when your node has fully synced (it takes forever to sync otherwise). Make sure CUSTOM_PEERS has your block producer set. It should return

{ "resultcode": "204", "datetime":"2021-02-01 14:15:21", "clientIp": "a.b.c.d", "iptype": 4, "msg": "glad you're staying with us" }

Running it again will produce an error, complaining that it needs to be run in 1 hour. The resulting $NODE_HOME/files/topology.json file should have your block producer host/port and then 16 other entries (17 in total).

An example output of $NODE_HOME/scripts/ is below:

Note that all the values are non-zero. There should be at least 1 incoming connection that comes from the block producer. When you set up up your account in (see below) the master topology list will be updated so that other relays can see your relay and this number will be >1.

There should be 17 outgoing connections (1 block producer + 16 relays). The above shows one less as the connection to one relay has temporarily failed.

The processed TX/Mempool numbers are non-zero when the block producer is fully operational. Something to note: the uptime will be less than 1 day as the relay is restarted daily due to cnode-tu-restart.service to force the new topology (make sure the cnode-tu-* services are running for the relay).

The block producer is shown below:

The title at the top needs to say “Core – Mainnet” and the KES timestamps shown. I originally created a node certificate before syncing was complete (due to running too early) and it showed todays date – it has to be in the future. The block/slot numbers need to track the relay’s numbers.

Peers needs to show the number of relays for both incoming/outgoing (topology.json has 1 entry per relay).

Run $NODE_HOME/scripts/ ptsendtip and make sure you see the following:

... cncli::nodeclient::pooltool > Checking cardano-node version: 1.25.1:9a733
... cncli::nodeclient::pooltool > Pooltool (HWC, 9ea2e3dc): (5287140, 6be34b24396c5082), json: {"success":true,"message":null}

This ensures that sites like know your current slot number. Set up the appropriate cnode-cli-*.services for the block producer.

Go to and search for your pool using either the pool id or the ticker (“hwc” in my case).

The slot number should be shown in green. If ptsendtip isn’t working (or your relay/BP is down), then it will be shown in red.

If you have an account with and add your pool to it, you can give your relay a name. You can look at your glorious relay as a red dot in relation to the thousands of other relays on the network diagram. Yes, it will in the Kuiper belt:

Finally, your relay should appear in the master topology list in

Set up Prometheus/Grafana and then look at some nice charts that show both the relay and the block producer. I’ve modified the Coin Cashew version as there were some things I didn’t like and had to be upgraded for the 1.25.1 Cardano release anyway (download here):

Now, just need get at least ₳2 000 000 to give the pool a better chance of getting rewards 🙂

Published by Crypto Cam Tech

Professional developer for 30 years + with the passion for helping others in their journey.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: