Hyperledger Fabric Blockchain Deployment
Hyperledger Fabric — this is not a public blockchain. This is a permissioned distributed ledger framework for corporate consortiums where participants are known and identified. If your task is "run your own blockchain for business" without public cryptocurrency and with control over participants, Fabric — right choice. If you need a token — that's not the technology.
Fabric Architecture: Key Concepts
Before deployment you need to understand specifics that radically differ from Ethereum:
Organizations — network participants. Each org has its own Certificate Authority (CA) and MSP (Membership Service Provider). Transactions are signed with certificates from specific CAs. No anonymity — every participant is identified.
Peers — organization nodes. Store ledger and chaincode. Two types: endorsing peers (execute chaincode and sign result) and committing peers (only validate and write blocks).
Orderer — transaction ordering service. Separated from peers. Consensus happens here. Types: Solo (for development, single node), Raft (production, several nodes with Byzantine fault tolerance).
Channels — isolated ledgers within one network. Organizations A and B can have private channel inaccessible to organization C, even if all three in one Fabric network.
Chaincode — smart contracts in Go, Java or Node.js. Execute in Docker containers on endorsing peers.
Transaction Model (Execute-Order-Validate)
This is fundamental difference from Ethereum. Transaction in Fabric goes through three phases:
- Execute: client sends proposal to endorsing peers. Peers simulate chaincode execution, return read/write sets and their signatures.
- Order: client collects endorsements (must satisfy endorsement policy) and sends to orderer. Orderer forms block.
- Validate: each peer validates transactions in block (endorsement policy, MVCC conflicts) and writes to ledger.
MVCC (Multi-Version Concurrency Control) — important detail. If two clients simultaneously read and write one key, one gets MVCC conflict and transaction invalidated. Need to design chaincode with this in mind.
Infrastructure Preparation
Tools
# Install Fabric binaries and Docker images
curl -sSL https://bit.ly/2ysbOFE | bash -s -- 2.5.6 1.5.9
# Add to PATH
export PATH=$PATH:$(pwd)/fabric-samples/bin
Binaries: cryptogen, configtxgen, peer, orderer, fabric-ca-client.
Configuration Structure
Typical directory structure for two-organization network:
my-fabric-network/
├── config/
│ ├── configtx.yaml # Channel and genesis block config
│ └── crypto-config.yaml # Organizations topology
├── docker/
│ └── docker-compose.yaml
├── chaincode/
│ └── my-contract/
└── scripts/
├── network.sh
└── deploy-chaincode.sh
Generating Cryptographic Materials
# crypto-config.yaml
OrdererOrgs:
- Name: Orderer
Domain: orderer.example.com
Specs:
- Hostname: orderer
PeerOrgs:
- Name: Org1
Domain: org1.example.com
Template:
Count: 2 # two peers
Users:
Count: 1 # one application user
- Name: Org2
Domain: org2.example.com
Template:
Count: 2
Users:
Count: 1
cryptogen generate --config=./config/crypto-config.yaml --output="crypto-material"
Generates complete PKI: root CA, intermediate CA, TLS certificates, keys for peers, orderers, admins.
Channel Configuration and Genesis Block
# configtx.yaml (abbreviated)
Organizations:
- &OrdererOrg
Name: OrdererOrg
ID: OrdererMSP
MSPDir: crypto-material/ordererOrganizations/orderer.example.com/msp
Policies:
Readers: { Type: Signature, Rule: "OR('OrdererMSP.member')" }
Writers: { Type: Signature, Rule: "OR('OrdererMSP.member')" }
Admins: { Type: Signature, Rule: "OR('OrdererMSP.admin')" }
- &Org1
Name: Org1MSP
ID: Org1MSP
MSPDir: crypto-material/peerOrganizations/org1.example.com/msp
AnchorPeers:
- Host: peer0.org1.example.com
Port: 7051
Capabilities:
Channel: &ChannelCapabilities
V2_0: true
Orderer: &OrdererCapabilities
V2_0: true
Application: &ApplicationCapabilities
V2_5: true
Profiles:
TwoOrgsOrdererGenesis:
Orderer:
OrdererType: etcdraft
Addresses: [orderer.example.com:7050]
EtcdRaft:
Consenters:
- Host: orderer.example.com
Port: 7050
ClientTLSCert: crypto-material/ordererOrganizations/.../tls/server.crt
ServerTLSCert: crypto-material/ordererOrganizations/.../tls/server.crt
BatchTimeout: 2s
BatchSize:
MaxMessageCount: 500
AbsoluteMaxBytes: 10 MB
PreferredMaxBytes: 2 MB
# Genesis block for orderer
configtxgen -profile TwoOrgsOrdererGenesis -channelID system-channel \
-outputBlock ./system-genesis-block/genesis.block
# Channel creation transaction
configtxgen -profile TwoOrgsChannel -outputCreateChannelTx \
./channel-artifacts/mychannel.tx -channelID mychannel
Docker Compose for Production-Like Network
# docker-compose.yaml (key services)
services:
orderer.example.com:
image: hyperledger/fabric-orderer:2.5.6
environment:
- ORDERER_GENERAL_LISTENADDRESS=0.0.0.0
- ORDERER_GENERAL_BOOTSTRAPMETHOD=file
- ORDERER_GENERAL_BOOTSTRAPFILE=/var/hyperledger/orderer/genesis.block
- ORDERER_GENERAL_LOCALMSPID=OrdererMSP
- ORDERER_GENERAL_TLS_ENABLED=true
volumes:
- ./system-genesis-block/genesis.block:/var/hyperledger/orderer/genesis.block
- ./crypto-material/ordererOrganizations/orderer.example.com/orderers/orderer.example.com/msp:/var/hyperledger/orderer/msp
- ./crypto-material/ordererOrganizations/orderer.example.com/orderers/orderer.example.com/tls:/var/hyperledger/orderer/tls
ports: ["7050:7050"]
peer0.org1.example.com:
image: hyperledger/fabric-peer:2.5.6
environment:
- CORE_PEER_ID=peer0.org1.example.com
- CORE_PEER_LOCALMSPID=Org1MSP
- CORE_PEER_TLS_ENABLED=true
- CORE_LEDGER_STATE_STATEDATABASE=CouchDB
- CORE_LEDGER_STATE_COUCHDBCONFIG_COUCHDBADDRESS=couchdb0:5984
depends_on: [couchdb0]
ports: ["7051:7051"]
couchdb0:
image: couchdb:3.3.2
environment:
- COUCHDB_USER=admin
- COUCHDB_PASSWORD=adminpw
ports: ["5984:5984"]
CouchDB vs LevelDB: CouchDB recommended for production — supports rich queries (JSON) via Mango Query, indexes, critical for complex chaincode. LevelDB — only key-value lookup.
Chaincode Deployment
Chaincode lifecycle in Fabric 2.x — two-step process with mandatory organization approval:
# 1. Package
peer lifecycle chaincode package my-contract.tar.gz \
--path ./chaincode/my-contract \
--lang golang \
--label my-contract_1.0
# 2. Install on each endorsing peer of each org
peer lifecycle chaincode install my-contract.tar.gz
# 3. Query package ID
peer lifecycle chaincode queryinstalled
# 4. Approve from each organization
peer lifecycle chaincode approveformyorg \
--channelID mychannel \
--name my-contract \
--version 1.0 \
--package-id <PACKAGE_ID> \
--sequence 1 \
--tls --cafile $ORDERER_CA
# 5. Check commit readiness
peer lifecycle chaincode checkcommitreadiness \
--channelID mychannel --name my-contract --version 1.0 --sequence 1
# 6. Commit (when all required org approved)
peer lifecycle chaincode commit \
--channelID mychannel --name my-contract \
--version 1.0 --sequence 1 \
--peerAddresses peer0.org1.example.com:7051 \
--peerAddresses peer0.org2.example.com:9051 \
--tls --cafile $ORDERER_CA
Each chaincode upgrade — increment --sequence. All participating org must approve new version.
Chaincode in Go: Key Patterns
package chaincode
import (
"encoding/json"
"fmt"
"github.com/hyperledger/fabric-contract-api-go/contractapi"
)
type SmartContract struct {
contractapi.Contract
}
type Asset struct {
ID string `json:"id"`
Owner string `json:"owner"`
Value int `json:"value"`
}
func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface,
id string, owner string, value int) error {
exists, err := s.AssetExists(ctx, id)
if err != nil { return err }
if exists { return fmt.Errorf("asset %s already exists", id) }
asset := Asset{ID: id, Owner: owner, Value: value}
assetJSON, err := json.Marshal(asset)
if err != nil { return err }
return ctx.GetStub().PutState(id, assetJSON)
}
// Rich query (only CouchDB)
func (s *SmartContract) GetAssetsByOwner(ctx contractapi.TransactionContextInterface,
owner string) ([]*Asset, error) {
queryString := fmt.Sprintf(`{"selector":{"owner":"%s"}}`, owner)
resultsIterator, err := ctx.GetStub().GetQueryResult(queryString)
// ...
}
Private Data Collections
Fabric supports private data — data visible only to subset of organizations, but whose hash is fixed in public ledger:
# collections_config.json
[{
"name": "assetPrivateDetails",
"policy": "OR('Org1MSP.member', 'Org2MSP.member')",
"requiredPeerCount": 1,
"maxPeerCount": 3,
"blockToLive": 0,
"memberOnlyRead": true,
"memberOnlyWrite": true
}]
Powerful feature for B2B scenarios: two companies see deal details, third — only fact of its existence.
Operational Aspects
Updating channel policies: changing endorsement policy or adding new organization — operation via configtxlator with signatures from current admins. Non-trivial procedure, requires coordination between orgs.
Monitoring: Prometheus exporter available for peer and orderer via --metrics.provider=prometheus. Grafana dashboards from Hyperledger community.
Backup: snapshot state database (CouchDB) + archive of blocks. Fabric supports peer snapshot command for creating snapshots from specific block.
Deployment Timeline
| Phase | Timeline |
|---|---|
| Network design (organizations, channels, policies) | 3–5 days |
| Infrastructure setup and PKI generation | 3–5 days |
| Network deployment and configuration | 3–5 days |
| Chaincode development | 1–4 weeks (depends on complexity) |
| Client SDK integration | 1–2 weeks |
| Testing and hardening | 1–2 weeks |
Realistic timeline from start to production-ready network with simple chaincode: 6–8 weeks. For complex chaincode with rich business logic, several channels and enterprise integrations — 2–3 months.







