前段时间想自己写合约铸造nft,了解到nft图片通常存储在ipfs,去看了下ipfs网关价格,卧槽,比存储桶都贵啊,因此决定自建。
自建ipfs网关基本0额外成本,吃灰小鸡多干点活就行了,还能规避网关认为内容不合规导致的风险。进行一些设置后,可以只在网关存储并pin自己上传的白名单文件,并提供给其它网关访问,且不会被滥用。
docker-compose.yml
services:
kubo:
image: ipfs/kubo:v0.36.0
container_name: kubo
restart: unless-stopped
networks:
- dockernetwork
ports:
- "24001:4001/tcp"
- "24001:4001/udp"
volumes:
- ./ipfs_data:/data/ipfs
- ./scripts:/scripts:ro
environment:
- IPFS_PATH=/data/ipfs
entrypoint: ["/bin/sh","-c","/scripts/boot.sh"] # 启动脚本
healthcheck:
test: ["CMD-SHELL", "ipfs id >/dev/null 2>&1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
ipfs-webui:
image: l1angth6/ipfs-webui:latest
container_name: ipfs-webui
restart: unless-stopped
expose:
- "8080"
networks:
- dockernetwork
environment:
- IPFS_API_URL=https://ipfs-api.example.com
networks:
dockernetwork:
external: true
解释一下为什么又单独运行了一个ipfs-webui,因为设置了关闭反序列化渲染,因此无法加载自带的webui。我发现没有官方的webui容器,就用官方仓库编译了一个,不放心或嫌麻烦请开启反序列化或使用自己放心的webui(比如官方webui也不是不能用)。
config/pins.txt 按照本文思路可以不使用
# IPFS Pin列表
# 每行一个CID,以#开头的为注释
#
# 示例:
# QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn
scripts/boot.sh 需要给可执行权限
#!/bin/sh
set -e
# 若未初始化,则初始化
ipfs config show >/dev/null 2>&1 || ipfs init --profile=server
# 暴露面:监听容器网卡,如果不需要使用api、webui 可以将api监听改为127.0.0.1
ipfs config Addresses.API /ip4/0.0.0.0/tcp/5001
ipfs config Addresses.Gateway /ip4/0.0.0.0/tcp/8080
# 防滥用:不从网络抓取新块;关闭反序列化渲染(trustless-only)
ipfs config --json Gateway.NoFetch true
ipfs config --json Gateway.DeserializedResponses false
# 轻量 DHT 客户端
ipfs config Routing.Type dhtclient
# CORS:允许 WebUI 容器和管理域名访问 API
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["https://ipfs-webui.example.com", "https://ipfs-api.example.com", "http://localhost:3000", "http://127.0.0.1:5001"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["GET","POST","PUT","OPTIONS"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Headers '["Authorization","Content-Type","X-Requested-With","Range","User-Agent","Origin","Accept","Referer"]'
ipfs config --json API.HTTPHeaders.Access-Control-Expose-Headers '["Location","Ipfs-Hash"]'
ipfs config --json API.HTTPHeaders.Access-Control-Allow-Credentials '["true"]'
# 垃圾回收周期(注意这里是字符串,不能用 --json 直接写裸字面量)
ipfs config Datastore.GCPeriod 12h
# 以守护进程运行(并启用 GC)
exec ipfs daemon --migrate=true --enable-gc
scripts/pin-manager.sh 需要手动执行,如果通过webui操作则不需要使用此脚本
#!/bin/bash
PIN_LIST="./config/pins.txt"
CONTAINER_NAME="kubo"
case "$1" in
"add")
echo "$2" >> "$PIN_LIST"
docker exec "$CONTAINER_NAME" ipfs pin add "$2"
echo "已Pin并添加到列表: $2"
;;
"sync")
echo "同步Pin列表中的所有文件..."
while read -r cid; do
[[ "$cid" =~ ^#.*$ ]] && continue
[[ -z "$cid" ]] && continue
echo "正在Pin: $cid"
docker exec "$CONTAINER_NAME" ipfs pin add "$cid"
done < "$PIN_LIST"
;;
"list")
docker exec "$CONTAINER_NAME" ipfs pin ls --type recursive
;;
"gc")
echo "执行垃圾收集..."
docker exec "$CONTAINER_NAME" ipfs repo gc
;;
*)
echo "用法: $0 {add <CID>|sync|list|gc}"
;;
esac
此用例需要解析网关(到kubo:8080)、api(到kubo:5001)、webui域名(到ipfs-webui:8080)。并且必须对api域名做强鉴权,比如限制白名单ip。如果不想这样,可以只解析网关域名并修改boot.sh中api为外部不可访问,操作麻烦些:
ipfs config Addresses.API /ip4/127.0.0.1/tcp/5001
默认软存储上限为10GB,通过以下命令查看
# 查看当前上限与占用:
docker exec kubo ipfs config Datastore.StorageMax
docker exec kubo ipfs config Datastore.StorageGCWatermark
docker exec kubo ipfs repo stat --human
# 调整软上限(示例:改为 200GB)与水位线:
docker exec kubo ipfs config Datastore.StorageMax 200GB
docker exec kubo ipfs config Datastore.StorageGCWatermark 90
# 重启容器以确保生效
docker compose restart kubo
说明:StorageMax
是字符串(如 200GB
);水位线 %
一般保持 90。GC 的默认周期是 1 小时,你已在脚本里改成了 12h
。
p2p端口用于向其它节点提供文件,默认是4001,本文中设置为24001。如果修改了p2p端口,可能需要添加公告。在boot脚本中添加公告,使其他网关能发现本地文件,(放在已有的 Routing.Type dhtclient
之后、exec ipfs daemon …
之前):
# ---- 公告外网可达地址(两种写法,二选一) ----
# 重置(避免残留坏条目)
ipfs config --json Addresses.Announce '[]'
ipfs config --json Addresses.AppendAnnounce '[]'
# 公告 ipv4
ipfs config --json Addresses.Announce '[
"/ip4/152.53.38.001/tcp/14001",
"/ip4/152.53.38.001/udp/14001/quic-v1"
]'
# 避免 UPnP/NAT 自动探测干扰(你已手动公告)
ipfs config --json Swarm.DisableNatPortMap true
ipfs config --json Addresses.AppendAnnounce '[]'
# (可选)只再公布已 pin 的内容,便于别的节点/DHT 更快发现你
ipfs config Reprovider.Strategy pinned
# ================
# 方式B:用域名(IP 变化少、或喜欢用域名时)
# 确保 A/AAAA 已指向本机
ipfs config --json Addresses.Announce '[
"/dns4/ipfs-test.example.com/tcp/24001",
"/dns4/ipfs-test.example.com/udp/24001/quic-v1"
]'
ipfs config --json Addresses.AppendAnnounce '[]'
# (可选)只再公布已 pin 的内容,便于别的节点/DHT 更快发现你
ipfs config Reprovider.Strategy pinned
随后运行 docker compose up -d ,如果没有报错,则继续进行
访问ipfs-webui.example.com ,api如果没有自动设置,则手动设置为
https://ipfs-api.example.com/api/v0
随后可以在左侧的 文件
标签页上传和pin文件
![图片[1]-记录一次docker自建ipfs网关-THsInk](https://www.thsink.com/wp-content/uploads/2025/08/截屏2025-08-17-16.59.52-1024x533.png)
这里尝试上传了一个静态网站和一些nft需要的文件。
如果先pin文件夹,之后对文件夹内容进行了修改,会导致此文件夹cid改变,pin的文件夹实际是此文件夹的历史版本。pin文件夹会递归pin此文件夹内当前包含的文件和文件夹。
至此,实现了:
- 存储自己需要存储在ipfs中的文件,并保证了其可访问性
- 节省了大量网关服务产生的费用
- ipfs节点被设置为无法获取不在本地的文件,意味着如果其他人获取了网关地址,无法用来访问不在此网关本地的文件。类似于设置了文件白名单。也可以直接不解析域名到此网关。
- 使用此网关上传的文件可以通过其它网关访问。比如nft图片和网站,通常都应该能显示。
此网关无法查看网页,因为设置了Gateway.DeserializedResponses false 以提高安全性。即使开启,也无法正确查看有资源路径的网页,如果要支持这种类型网站,需要设置将cid解析到单独的子域名,由于我暂时用不到此功能,cloudflare对子域名证书也有限制,因此暂未尝试。
上传的网页在其它支持上述功能的网关测试可以正常访问。
然后又要说一下折腾网站的事。。最开始在youtube看到有Ethereum Name Service,注册的.eth域名可以代替标准0x eth地址用来转账交易,还可以搭配ipfs创建一个web3网站,可以通过一些现成的服务访问此.eth网站,挺有意思的。类似的名称服务还有.sol .pol等,和eth按年付费不同,这些通常是一次性付费的。本来想买个sol域名,结果sol域名的1刀活动结束了,索性不做了,以后再说吧。
v2ex最近也有一股sol风潮,我对sol链倒是用得少,pol反而多点。突然发现一个月前买的v2ex币涨了挺多,卖出大部分后爽吃了一顿😂
暂无评论内容