Getting Truffle and Ganache Working with Docker

Photo by Jessica Loaiza on Unsplash

Getting Truffle and Ganache Working with Docker

March 9, 2022

We have been experimenting on how to introduce web3 at work. After using hardhat for all of the Buildspace classes, I thought exploring Truffle and Ganache would be interesting for comparison. Unfortunately, not everything worked out-of-the-box for me. And it lead me to try to get a working Truffle/Ganache setup using Docker.

Understanding the Technology

That introduction used a lot of terms that you might not be familiar with. You also might not know what in the world web3 is. If so, this section is for you. If the introduction did not sound like Greek to you, feel free to skip this section and go directly to the problem statement.

web3 & Ethereum

Web3 is an iteration of web development based on blockchain technology. The hopes of this iteration is to decentralize data throughout the web and establish a more universal, accessible economy. 1 The two largest blockchain technologies today is Bitcoin and Ethereum. Ethereum is the blockchain which the libraries mentioned above use.

Ethereum is a decentralized, open-source blockchain technology that has built-in smart contract capability. 2 The ability for smart contracts is what makes Ethereum so attractive to developers. This attractiveness is why there is so much tooling around these contracts.

Solidity

Solidity is a programming language that one uses to write smart contracts. This is the language that is used when you initialize a project with either Hardhat or Truffle. Even though Solidity was first developed to work with the Ethereum blockchain, it has since expanded to work with different blockchains. This fact makes it useful to learn if you want to dive into smart contract development.

Hardhat

Hardhat is an NPM package that creates a development environment to compile, deploy, test, and debug your Ethereum software. 3 This is the most popular tool to develop Ethereum smart contracts.

Truffle

Truffle is another NPM package. It is a development environment, testing framework and asset pipeline for blockchains using the Ethereum Virtual Machine. 4 Very much like Hardhat, Truffle makes contract development smoother.

Ganache

Ganache is the third NPM package listed here. It is a personal blockchain for Ethereum distributed application development. 5 This allows you to deploy your smart contracts locally and saves you from testing on the real blockchains. It’s the first time that I have explored running my own blockchain. I usually just use the testnets, but this was a nice alternative. And I can see using this going forward.

The Problem

I had issues installing Truffle globally on my computer. (Which is the preferred method per their documentation.) There seems to be an issue with Truffle and the latest version of NPM; which seems to have been around for a while. I could not get Truffle to install locally without messing with my global Node.js install. Below is the error I am getting when I tried the global installation:

npm ERR! fatal error: too many errors emitted, stopping now [-ferror-limit=]
npm ERR! 20 errors generated.
npm ERR! make: *** [Release/obj.target/leveldb/deps/leveldb/leveldb-1.20/db/builder.o] Error 1
npm ERR! gyp ERR! build error
npm ERR! gyp ERR! stack Error: `make` failed with exit code: 2
npm ERR! gyp ERR! stack     at ChildProcess.onExit (/nodejs/16.3.0/.npm/lib/node_modules/npm/node_modules/node-gyp/lib/build.js:194:23)
npm ERR! gyp ERR! stack     at ChildProcess.emit (node:events:394:28)
npm ERR! gyp ERR! stack     at Process.ChildProcess._handle.onexit (node:internal/child_process:290:12)
npm ERR! gyp ERR! System Darwin 18.7.0
npm ERR! gyp ERR! command "/nodejs/16.3.0/bin/node" "/nodejs/16.3.0/.npm/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js" "rebuild"
npm ERR! gyp ERR! cwd /nodejs/16.3.0/.npm/lib/node_modules/truffle/node_modules/ganache/node_modules/leveldown
npm ERR! gyp ERR! node -v v16.3.0
npm ERR! gyp ERR! node-gyp -v v8.4.1
npm ERR! gyp ERR! not ok

I am not a huge fan of installing things globally, especially when it does not work with the latest version of Node.js or NPM. Luckily, we have tools to help us in these situations.

Docker

Docker has been around for a while. But in case you are unfamiliar, Docker is a set of tools that facilitates OS-level virtualization.6 These tools will allow us to containerize the Truffle and Ganache setup removing the need of global installation. It’s a win-win. The Docker container we build can be used anywhere, and we no longer need to worry about the version of Node.js we have installed on our computer.

If you do not have Docker installed on your machine yet, go here. It’s beyond the scope of this post, but the linked directions should be pretty straight forward. But once installed, let’s clone my init example where we will keep the code we need for Docker. Run the following commands to set up this directory:

$ git clone https://github.com/joshfinnie/truffle-init-example.git <PATH>
$ cd <PATH>
$ touch Dockerfile
$ touch docker-compose.yml

This will give you the default files we will need to get Truffle and Ganache installed in a Docker container. It’s a bit odd, but I had to figure out how to get the default Truffle folder structure without installing it globally. This repo was generated for this post and gives you the default folders and files a truffle init will do. Give it a ⭐️ if you find it helpful!

Dockerfile

Below is the code for the Dockerfile. We’ll go into some depth asto what’s going on here.

FROM node:16-bullseye-slim as base

RUN apt-get update && \
    apt-get install --no-install-recommends -y \
        build-essential \
        python3 && \
    rm -fr /var/lib/apt/lists/* && \
    rm -rf /etc/apt/sources.list.d/*

RUN npm install --global --quiet npm truffle ganache

FROM base as truffle

RUN mkdir -p /home/app
WORKDIR /home/app

COPY package.json /home/app
COPY package-lock.json /home/app

RUN npm install --quiet

COPY truffle-config.js /home/app
COPY contracts /home/app/contracts
COPY migrations /home/app/migrations/
COPY test /home/app/test/

CMD ["truffle", "version"]

FROM base as ganache

RUN mkdir -p /home
WORKDIR /home
EXPOSE 8545

ENTRYPOINT ["ganache", "--host 0.0.0.0"]

The first block of code I will breakdown is the code that builds our “base” container. I have long ago adopted multi-stage builds as a way to optimize my Dockerfiles, and feel like this is the ideal reason why. Having a “base” container allows us to make sure the default node:16-bullseye-slim Docker image is up-to-date and has all our required packages installed.

FROM node:16-bullseye-slim as base

RUN apt-get update && \
    apt-get install --no-install-recommends -y \
        build-essential \
        python3 && \
    rm -fr /var/lib/apt/lists/* && \
    rm -rf /etc/apt/sources.list.d/*

RUN npm install --global --quiet npm truffle ganache

We install build-essenials and python3 from the Debian package manager. These are dependencies for Truffle. And we install truffle and ganache using NPM.

Taking from our created “base” image, we then buildout our truffle container.

FROM base as truffle

RUN mkdir -p /home/app
WORKDIR /home/app

COPY package.json /home/app
COPY package-lock.json /home/app

RUN npm install --quiet

COPY truffle-config.js /home/app
COPY contracts /home/app/contracts
COPY migrations /home/app/migrations/
COPY test /home/app/test/

CMD ["truffle", "version"]

We create a working directory, copy our package.json file, and install the required packages. We then copy over the rest of the files needed to run the Truffle project. Lastly calling truffle version to spit out our version of Truffle installed. Note This command would be doing something more exciting here, but for this blog post, that is all we’re doing.

Lastly, we build out our Ganache container. This one is a tad less complex due to Ganache being a tool that runs in the background.

FROM base as ganache

RUN mkdir -p /home
WORKDIR /home
EXPOSE 8545

ENTRYPOINT ["ganache-cli", "--host 0.0.0.0"]

In this container, we again take from the “base” container, expose the default Ganache port of 8545 and run it. Note we use the --host flag here to change the default host from 127.0.0.1 to 0.0.0.0.

docker-compose.yml

Below is the Docker Compose file. Even though you don’t need a Compose file, I find them easier to deal with and love to include them. There’s something satisfying about running docker compose up and having your entire system running within Docker.

version: "3.4"
services:
  truffle:
    build:
      context: .
      target: truffle
    depends_on:
      - ganache
    networks:
      - backend
    volumes:
      - .:/home/app
      - /home/app/node_modules
  ganache:
    build:
      context: .
      target: ganache
    ports:
      - 8545:8545
    networks:
      - backend
networks:
  backend:
    driver: "bridge"

Conclusion

I hope this post helps anyone struggling to install Truffle locally. I never understood the reasoning NPM libraries needing global installation. Hardhat fixes this by using npx and I would love to see Truffle move to this paradigm. It would make using it much easier in my mind. But it would also require them to fix this issue with the latest version of Node.js. If anyone knows of another way to fix this, feel free to chat with me on Twitter.

Also might enjoy

You might also enjoy?

Set Up Flask and Restx with Docker

June 1, 2022

Using Latex Through Docker

September 17, 2019

My Basic Python Dockerfile

June 6, 2019