IPFS Node Deployment
IPFS (InterPlanetary File System) — content-addressed decentralized file system. Addressing by content hash (CID — Content Identifier) means file with identical content always has same CID, regardless of who and where stores it. This is key property for Web3: NFT metadata addressed by CID cannot be silently substituted — content change changes CID.
Own node needed when public gateways (ipfs.io, Cloudflare) don't provide needed reliability or speed, when you need guarantee data availability (pinning), or when traffic volumes make using Pinata/NFT.Storage unjustifiably expensive.
Installation and Configuration of Kubo
Kubo — reference IPFS implementation on Go:
# Installation
wget https://dist.ipfs.tech/kubo/v0.28.0/kubo_v0.28.0_linux-amd64.tar.gz
tar -xvzf kubo_v0.28.0_linux-amd64.tar.gz
cd kubo && sudo bash install.sh
# Initialization
ipfs init --profile server # server profile disables local mdns announcements
# Run daemon
ipfs daemon &
--profile server important for cloud deploy: without it node wastes resources on mDNS discovery useless in datacenter.
Production Configuration
# Memory usage limit (critical, IPFS can consume lots)
ipfs config Datastore.StorageMax "100GB"
# Connection limit
ipfs config --json Swarm.ConnMgr.LowWater 200
ipfs config --json Swarm.ConnMgr.HighWater 400
ipfs config --json Swarm.ConnMgr.GracePeriod '"1m"'
# Bandwidth limit
ipfs config --json Swarm.Transports.Network.Relay false
# Gateway: serve only pinned CID (don't work as public gateway)
ipfs config --json Gateway.NoFetch true
ipfs config --json Gateway.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
Gateway.NoFetch true — node won't download CID not stored locally. Without this setting node becomes public gateway and can accumulate others' data.
Systemd Service
# /etc/systemd/system/ipfs.service
[Unit]
Description=IPFS Daemon
After=network.target
[Service]
Type=notify
User=ipfs
Environment=IPFS_PATH=/data/ipfs
ExecStart=/usr/local/bin/ipfs daemon --migrate=true
Restart=on-failure
RestartSec=10s
LimitNOFILE=65536
[Install]
WantedBy=multi-user.target
sudo systemctl enable ipfs && sudo systemctl start ipfs
Pinning: Guarantee Availability
Add file to IPFS without pinning — it will be deleted on next garbage collection. Pinning fixes CID locally:
# Pin specific CID
ipfs pin add QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
# Check pinned list
ipfs pin ls --type recursive
# Garbage collection (deletes unpinned)
ipfs repo gc
For programmatic pinning via API:
import { create } from "ipfs-http-client";
const client = create({ url: "http://localhost:5001/api/v0" });
// Upload and pin file
async function uploadAndPin(content: Buffer, filename: string): Promise<string> {
const result = await client.add(
{ path: filename, content },
{ pin: true, wrapWithDirectory: true }
);
const cid = result.cid.toString();
console.log(`Uploaded: ipfs://${cid}/${filename}`);
return cid;
}
Nginx Reverse Proxy for Gateway
API (5001) shouldn't be open outside — only gateway (8080):
server {
listen 443 ssl;
server_name ipfs.yourservice.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_cache_valid 200 1d; # cache CID responses (they immutable)
proxy_cache_bypass $http_cache_control;
# Important for large files
proxy_read_timeout 300s;
proxy_buffering off;
}
}
Monitoring
# Storage size
ipfs repo stat
# Peers count
ipfs swarm peers | wc -l
# Bandwidth statistics
ipfs stats bw
In Prometheus via ipfs-prometheus-exporter or periodic script writing metrics. Alerts: ipfs_repo_size_bytes > threshold, ipfs_peers_count < 5 (network isolation).
Pinata as Hybrid Approach
Own node + Pinata (or web3.storage) as backup — good practice for NFT projects. Pinata pins your CID on their infrastructure, your node provides fast access for your users. Pinata API:
const pinata = new PinataSDK({ pinataJwt: process.env.PINATA_JWT });
await pinata.upload.cid("QmYour...CID");







