Розгортування IPFS-ноди
IPFS (InterPlanetary File System) — content-addressed децентралізована файлова система. Адресація по content hash (CID — Content Identifier) означає, що файл з одинаковим змістом завжди має один і той же CID, незалежно від того, хто і де його зберігає. Це ключова властивість для Web3: NFT метаданні, адресовані по CID, не можуть бути тихо підміняні — зміна змісту змінює CID.
Власна нода потрібна, коли публічні шлюзи (ipfs.io, Cloudflare) не забезпечують потрібну надійність або швидкість, коли потрібна гарантія доступності ваших даних (pinning), або коли обсяги трафіку роблять використання Pinata/NFT.Storage неоправданно дорогим.
Встановлення та конфігурація Kubo
Kubo — reference реалізація IPFS на Go:
# Встановлення
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
# Ініціалізація
ipfs init --profile server # server profile відключає локальні mdns-объявлення
# Запуск daemon
ipfs daemon &
--profile server важен для облачного деплоя: без нього нода тратить ресурси на mDNS discovery, який бесполезен в датацентре.
Конфігурація для production
# Ограничение использования памяти (критично, IPFS может потреблять много)
ipfs config Datastore.StorageMax "100GB"
# Ограничение соединений
ipfs config --json Swarm.ConnMgr.LowWater 200
ipfs config --json Swarm.ConnMgr.HighWater 400
ipfs config --json Swarm.ConnMgr.GracePeriod '"1m"'
# Ограничение bandwidth
ipfs config --json Swarm.Transports.Network.Relay false
# Gateway: отдавать только запиненные CID (не работать публичным шлюзом)
ipfs config --json Gateway.NoFetch true
ipfs config --json Gateway.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
Gateway.NoFetch true — нода не буде завантажувати CID, які не зберігаються локально. Без цієї настройки нода стає публічним шлюзом і може накопичувати чужі дані.
Systemd сервіс
# /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: гарантія доступності
Додати файл в IPFS без pinning — він буде видалений при наступній garbage collection. Pinning фіксує CID локально:
# Pin конкретного CID
ipfs pin add QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG
# Перевірити список запиненних
ipfs pin ls --type recursive
# Garbage collection (видаляє незапиненне)
ipfs repo gc
Для програмного pinning через API:
import { create } from "ipfs-http-client";
const client = create({ url: "http://localhost:5001/api/v0" });
// Загрузка і pinning файлу
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 для gateway
API (5001) не повинен бути відкритий ззовні — тільки 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; # кешувати CID ответи (вони immutable)
proxy_cache_bypass $http_cache_control;
# Важливо для великих файлів
proxy_read_timeout 300s;
proxy_buffering off;
}
}
Моніторинг
# Розмір хранилища
ipfs repo stat
# Кількість peers
ipfs swarm peers | wc -l
# Bandwidth статистика
ipfs stats bw
В Prometheus через ipfs-prometheus-exporter або періодичний скрипт з записом метрик. Алерти: ipfs_repo_size_bytes > threshold, ipfs_peers_count < 5 (ізоляція від мережи).
Pinata як гібридний підхід
Власна нода + Pinata (або web3.storage) як backup — хороша практика для NFT-проектів. Pinata піннит ваші CID на своїй інфраструктурі, ваша нода забезпечує швидкий доступ для ваших користувачів. Pinata API:
const pinata = new PinataSDK({ pinataJwt: process.env.PINATA_JWT });
await pinata.upload.cid("QmYour...CID");







