Help pin the site's IPFS mirror¶
Alongside the main site, the anoni.net docs are also published as an IPFS mirror (the design is explained in decentralized website publishing), so the content stays readable when the main site is blocked or taken down. Content on IPFS only survives while some node pins it, and only a handful of nodes currently serve this mirror, with the community's own node carrying most of it. Every extra node that helps pin is one more complete copy on the network, and more resistance to takedown.
This page walks you through running one always-on IPFS node plus a small scheduled script that keeps up with the latest version automatically. It works the same on Windows, Linux, and macOS, with or without Docker.
What you need to help
- A computer that stays on most of the day and can reach the network (a desktop, a home server, or a small always-on box).
- IPFS installed (this page shows you how).
- A scheduled script that pins the latest version every few hours.
Can't run a node? The last section covers pinning through a service instead.
Why the CID changes every time (30-second IPFS primer)¶
If IPFS is new to you, one idea makes the rest of this page make sense. For the deeper design, see the IPFS content addressing docs; here is just enough to work with.
- IPFS hashes a file's content into a CID (Content Identifier), and that CID is the content's address. Identical content always has the same CID.
- Change the content and the CID changes with it. Every time the docs site updates and republishes, a brand-new CID is produced.
- IPNS (InterPlanetary Name System) is a fixed name that always points to the current CID. The site's IPNS name is the
k51…string below. - Pinning means "guarantee this specific CID stays available." A pin is bound to a CID and does not follow IPNS on its own. So the moment the site publishes a new version, what you pinned last time is still the old CID.
That is exactly what the script does: resolve IPNS to the current CID, pin that new CID, then drop the old one. Because the CID changes, this has to run periodically, which is why it's scheduled.
Three lookalike identifiers¶
IPFS has three identifiers that look alike and do different jobs. Paste one into the wrong field and the tool rejects it outright.
| Identifier | Starts with | Changes? | What it's for |
|---|---|---|---|
| CID | bafybei… |
New one every publish | Pointing at one specific version |
| IPNS name | k51qzi… |
Fixed | Getting the latest version, which is what pinning uses |
| Peer ID | 12D3KooW… |
Fixed | Pointing at one specific node |
Pinning the docs site only needs the IPNS name. The Peer ID comes up when you want your own node to hold a steady connection to the community node, covered under keeping a steady connection below.
How it works (why a schedule is enough, no notification needed)¶
Nobody has to tell you "a new version is out." IPNS is the shared sync point, and your script resolves it to get the latest CID by itself. When the site publishes, it just updates IPNS as usual; your side resolves every few hours, notices the change, and pins the new version. No manual coordination anywhere.
The site's IPFS coordinates (public values, use them directly):
- IPNS name:
k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw - Node Peer ID:
12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9(only needed if you set up peering) - DNSLink:
_dnslink.anoni.netpoints at the same IPNS name, so any DNSLink-capable gateway serves the same content - Open in a browser: https://ipfs.anoni.net/, the gateway the community runs itself. The old address
anoni-net.ipns.dweb.linkshuts down on 30 September 2026, see what changes for IPFS after September 2026
Each run does this: resolve IPNS to the current CID, pin the new CID, unpin the previous one, reclaim space. The script confirms the new version pinned successfully before dropping the old one. If resolving or fetching fails, it keeps the copy you already have and never empties your node.
Step 1: Run an always-on IPFS node¶
For pinning to fetch the content, your machine needs a continuously running IPFS daemon. Pick one setup below.
Install kubo, IPFS's official command-line implementation. On macOS you can use Homebrew:
brew install kubo
The Homebrew formula was renamed from ipfs to kubo. The old name still installs it, but the service commands below need the kubo name. Both Intel and Apple Silicon are supported.
On Linux, download the kubo build for your architecture from the official guide. Then initialize and start the daemon:
ipfs init # first time only
ipfs daemon
To keep the daemon running long-term, use a systemd user service on Linux. On macOS the Homebrew formula ships a service definition, so a single command keeps it resident and starts it at login:
brew services start kubo
For a quick test, running ipfs daemon in the background is fine.
The easiest option is IPFS Desktop. It bundles kubo and, once you log in, stays running in the system tray with the daemon always on. IPFS Desktop has had no dedicated maintainer since 30 September 2026, see what changes for IPFS after September 2026; a standalone kubo install or Docker removes one layer of uncertainty on Windows.
After installing, make sure ipfs is callable from the command line. A standalone kubo install needs ipfs.exe added to your PATH; with IPFS Desktop, if PATH can't find ipfs, use the full path in the script instead.
Run kubo with a docker-compose.yml, naming the container ipfs_host:
services:
ipfs_host:
image: ipfs/kubo:latest
restart: always
volumes:
- ./ipfs-data:/data/ipfs
ports:
- "4001:4001" # swarm; exposing it helps reach other nodes
Start it:
docker compose up -d
From here, run IPFS commands through the container by setting an environment variable the script picks up automatically (used in the next step):
export IPFS_CMD="docker exec ipfs_host ipfs"
Step 2: Get the pin script¶
The script resolves IPNS, pins the new version, and unpins the old one on its own. The IPNS name is hard-coded in it, so there's nothing to edit. Docker users: set IPFS_CMD from the previous step first.
Linux, macOS, Docker: anoni-pin.sh¶
#!/usr/bin/env bash
#
# anoni-pin.sh — pin the latest IPFS mirror of the anoni.net docs site.
#
# Usage:
# ./anoni-pin.sh # uses the local `ipfs`
# Run it on a schedule (cron) every 6 hours or so. See the docs for setup.
#
# Docker users: route ipfs commands through the container
# export IPFS_CMD="docker exec ipfs_host ipfs"
#
# Design: pin the new version first, only then unpin the old one. If resolving
# or fetching fails, keep the copy you already have — never leave the node empty.
#
set -eu
# The docs site's IPNS name (public value, same as DNSLink _dnslink.anoni.net)
IPNS="k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw"
# Override the ipfs command via IPFS_CMD (see the Docker note above)
IPFS="${IPFS_CMD:-ipfs}"
# Remembers the last CID we pinned, so we can unpin it next time
STATE="${ANONI_PIN_STATE:-${HOME}/.anoni-pin/last_cid}"
mkdir -p "$(dirname "$STATE")"
# 1. Resolve the IPNS name to the current CID (--nocache forces a fresh lookup)
NEW="$($IPFS name resolve --nocache "/ipns/$IPNS" 2>/dev/null || true)"
if [ -z "$NEW" ]; then
echo "[anoni-pin] resolve failed (network or node issue), keeping current state"
exit 0
fi
OLD="$(cat "$STATE" 2>/dev/null || true)"
if [ "$NEW" = "$OLD" ]; then
echo "[anoni-pin] already on the latest version $NEW, nothing to do"
exit 0
fi
# 2. Pin the new version first (fetches the whole thing). With set -e, a failure
# here aborts the script before we unpin anything.
echo "[anoni-pin] pinning new version: $NEW"
$IPFS pin add "$NEW"
# 3. Record state, unpin the old version, reclaim space
echo "$NEW" > "$STATE"
if [ -n "$OLD" ] && [ "$OLD" != "$NEW" ]; then
echo "[anoni-pin] unpinning old version: $OLD"
$IPFS pin rm "$OLD" || true
fi
$IPFS repo gc >/dev/null 2>&1 || true
echo "[anoni-pin] done, now pinning $NEW"
Save it and make it executable:
chmod +x anoni-pin.sh
./anoni-pin.sh # run once by hand to check it works
Windows: anoni-pin.ps1¶
#
# anoni-pin.ps1 — pin the latest IPFS mirror of the anoni.net docs site (Windows / PowerShell).
#
# Usage: make sure an IPFS daemon (IPFS Desktop or kubo) is running, then
# powershell -NoProfile -ExecutionPolicy Bypass -File .\anoni-pin.ps1
# Run it on a schedule with Task Scheduler every 6 hours or so. See the docs.
#
# Docker users:
# $env:IPFS_CMD = "docker exec ipfs_host ipfs"
#
# Design: pin the new version first, only then unpin the old one. If resolving
# or fetching fails, keep the copy you already have.
#
# The docs site's IPNS name (public value, same as DNSLink _dnslink.anoni.net)
$IPNS = 'k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw'
# Remembers the last CID we pinned, so we can unpin it next time
$State = if ($env:ANONI_PIN_STATE) { $env:ANONI_PIN_STATE } else { Join-Path $env:LOCALAPPDATA 'anoni-pin\last_cid.txt' }
New-Item -ItemType Directory -Force -Path (Split-Path $State) | Out-Null
# Override the ipfs command via IPFS_CMD (see the Docker note). Split into exe + prefix args.
$cmd = if ($env:IPFS_CMD) { $env:IPFS_CMD } else { 'ipfs' }
$parts = $cmd -split '\s+'
$exe = $parts[0]
$pre = @()
if ($parts.Length -gt 1) { $pre = $parts[1..($parts.Length - 1)] }
function Invoke-Ipfs { & $exe @pre @args }
# 1. Resolve the IPNS name to the current CID (--nocache forces a fresh lookup)
$New = (Invoke-Ipfs name resolve --nocache "/ipns/$IPNS" 2>$null | Out-String).Trim()
if ([string]::IsNullOrWhiteSpace($New)) {
Write-Host '[anoni-pin] resolve failed (network or node issue), keeping current state'
exit 0
}
$Old = if (Test-Path $State) { (Get-Content $State -Raw).Trim() } else { '' }
if ($New -eq $Old) {
Write-Host "[anoni-pin] already on the latest version $New, nothing to do"
exit 0
}
# 2. Pin the new version first. Check LASTEXITCODE (not exceptions); on failure keep the old version.
Write-Host "[anoni-pin] pinning new version: $New"
Invoke-Ipfs pin add $New
if ($LASTEXITCODE -ne 0) {
Write-Host '[anoni-pin] pin add failed, keeping the old version'
exit 1
}
# 3. Record state, unpin the old version, reclaim space
Set-Content -Path $State -Value $New -NoNewline
if ($Old -and $Old -ne $New) {
Write-Host "[anoni-pin] unpinning old version: $Old"
Invoke-Ipfs pin rm $Old 2>$null
}
Invoke-Ipfs repo gc 2>$null | Out-Null
Write-Host "[anoni-pin] done, now pinning $New"
Run it once by hand to check it works (PowerShell blocks scripts by default, so -ExecutionPolicy Bypass allows this one run):
powershell -NoProfile -ExecutionPolicy Bypass -File .\anoni-pin.ps1
Step 3: Schedule it¶
Have the script run every few hours. The site doesn't update often, so every 6 hours is plenty; shorten it if you want to keep up faster.
Use cron. Edit your crontab:
crontab -e
Add a line that runs every 6 hours and writes output to a log:
0 */6 * * * /path/to/anoni-pin.sh >> $HOME/.anoni-pin/log 2>&1
To also catch up on missed runs after a reboot, use a systemd timer with Persistent=true instead, which is more reliable.
Use Task Scheduler. The quickest way is to create it from the command line, running every 6 hours:
schtasks /Create /TN "anoni-ipfs-pin" `
/TR "powershell -NoProfile -ExecutionPolicy Bypass -File C:\Tools\anoni-pin.ps1" `
/SC HOURLY /MO 6 /RL LIMITED
Replace C:\Tools\anoni-pin.ps1 with wherever you saved it. You can also use the Task Scheduler GUI: set the program to powershell and the arguments to -NoProfile -ExecutionPolicy Bypass -File your-path.
Schedule according to your host OS (cron on Linux/macOS, Task Scheduler on Windows; see the two tabs above). The only difference is setting IPFS_CMD before the script runs, for example via a small wrapper:
#!/usr/bin/env bash
export IPFS_CMD="docker exec ipfs_host ipfs"
exec /path/to/anoni-pin.sh
Point cron at this wrapper.
Step 4: Verify¶
Confirm the content is actually pinned. Resolve the current CID, then check it's in the pin list:
CID=$(ipfs name resolve --nocache /ipns/k51qzi5uqu5dlfm2jj0f70ex3r3babmwy8qh071inwknttr7wqa3uhdwvlmrmw)
ipfs pin ls --type=recursive | grep "${CID#/ipfs/}"
You can also open it on your local gateway and check it renders: http://127.0.0.1:8080${CID}/. Docker users: replace ipfs above with docker exec ipfs_host ipfs.
Keeping a steady connection to the community node¶
Pinning works off the IPNS name alone, and the DHT takes care of finding the content. Fetching a full mirror for the first time can drag on when DHT lookups are slow or come back empty. To hold a fixed connection between your node and the source node, configure kubo's peering.
Edit ~/.ipfs/config and add a Peering block at the top level:
"Peering": {
"Peers": [
{ "ID": "12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9" }
]
}
Addrs can be omitted, and kubo will look the addresses up in the DHT. Restart the daemon to apply. Docker users edit ./ipfs-data/config instead, then run docker compose restart.
Check that it connected:
ipfs swarm peers | grep 12D3KooWEzvBhnLa6NZnjnw22Yoqs56xq4pNCZdkkxw5yxvi1eV9
Peering here is one-way: the community node has no matching entry, so keeping the connection alive is your side's job. The kubo config docs warn that one-way peering consumes connection resources on the other node, and that load concentrates on a single machine as the number of mirrors grows. Turn it on if you actually hit slow fetches, and skip this section if things already work.
What changes for IPFS after September 2026¶
Interplanetary Shipyard maintained most of the major IPFS software for the past few years. After Protocol Labs stopped funding it, the team ended its IPFS work on 30 September 2026. Everything on this page still works, and three things change.
- No dedicated maintainer for the software: kubo, Helia, Boxo, IPFS Desktop and IPFS Companion are all in that group, and the IPFS Foundation has moved to grants for individual maintainers. The code keeps working, security updates land more slowly, so check the release date before you install.
- Public gateways and bootstrap nodes stop: ipfs.io, dweb.link and the IPFS bootstrap nodes shut down on the same day. Since kubo 0.38 the default for
Bootstrapisauto, and the expanded list contains exactly those nodes. - The browsing URL on this page has changed: the old
anoni-net.ipns.dweb.linkbelonged to dweb.link, and the new ipfs.anoni.net is run by the community. Both serve the same IPNS name through the DNSLink record. The pin script works from the IPNS name and is unaffected by the gateway.
First check which bootstrap nodes your node currently gets, and how many peers it has:
ipfs config Bootstrap --expand-auto
ipfs swarm peers | wc -l
Once the nodes behind auto shut down, a fresh node may fail to join the DHT. Pinning the community node as a steady peer is the most direct answer, described under keeping a steady connection above; Peering takes the Peer ID alone and kubo looks the addresses up in the DHT.
Maintenance and notes¶
- It keeps up automatically: When the site changes its CID, the next scheduled run pins the new version and drops the old one. You do nothing.
- Disk use stays flat: After unpinning the old version the script runs a garbage collection, so only the latest version takes space. A full mirror is about 220 MB (measured August 2026).
- It won't lose the copy you have: The script pins the new version successfully before dropping the old one, and keeps your existing copy if resolving or downloading fails.
- To stop helping: unpin the current version and remove the schedule. It doesn't affect any other node.
- Privacy and risk: what you pin is public documentation, so there's no privacy concern. Offering IPFS pinning carries different legal risk across jurisdictions, so weigh that for where you operate.
Pinning through a service instead of running a node¶
If you'd rather not maintain a daemon and a schedule, use a pinning service like Pinata or Storacha. Paste the current CID into their interface to pin it manually, or script their API to feed it new CIDs, with the same logic as this page (resolve IPNS to a CID, hand the CID to the service).
The trade-off is that survival now depends on a third-party provider rather than your own node. Fine as a backup; for real decentralization, running your own node is the most direct.