About me: My name is Solène Rapenne, pronouns she/her. I like learning and sharing knowledge. Hobbies: '(Qubes OS BSD OpenBSD Lisp cmdline gaming security QubesOS internet-stuff). I love percent and lambda characters. Qubes OS core team member, former OpenBSD developer solene@. No AI is involved in this blog.

Contact me: solene at dataswamp dot org or @solene@bsd.network (mastodon).

I'm a freelance OpenBSD, FreeBSD, Linux and Qubes OS consultant, this includes DevOps, DevSecOps, technical writing or documentation work.

How to setup your own IPFS private network

Written by Solène, on 21 August 2026.
Tags: #linux #network #ipfs

Comments on Fediverse/Mastodon

1. Introduction §

In this blog post, you will learn how to make a private IPFS network that will allow private peers to exchange data between each other. This is in opposition to the default setup in which you use the default configuration connecting to the whole IPFS network. The incentive to have your own network is mostly speed, because this does not have any extra access control, any peer can potentially access to any content you have in your IPFS network.

If you do not know about IPFS, I wrote some blog posts, but otherwise IPFS website may give you some clue. It is not easy to grasp, but you can see it as a network service providing content addressed object storage. The base blocks provide features to manage data in the network but also cache it or provide it through a web gateway.

IPFS project official website

Kubo is the reference implementation of IPFS.

The setup I explain in this article is pretty much useless for most people. Depending on your needs, you will have a better experience with Nextcloud/Seafile for central file sharing, Peergos for encrypted storage, syncthing for directory synchronization between peers, or even magic wormhole for one-shot transfers. I am still not sure which purpose an IPFS private swarm serves correctly, but none of the use cases I just listed, maybe it is useful if you need to keep some dataset synchronized and quickly available in multiple areas, but then an hyperscaler may be more suited.

2. Setup §

In this setup, you will have various nodes that can speak to each other (through a VPN, LAN or directly over the Internet) and potentially but not mandatory, a node that is up 24/7.

They must all share the same swarm.key to create a swarm (a group of nodes, that is private until you get access to the key).

The setup itself will allow nodes to publish data and give it access to other nodes for caching, downloading or relaying, but also give the opportunity to publish on a web gateway.

3. Too Long Didn't Read §

IPFS daemon will hold data until it reaches its maximum allowed size, then will run a garbage collector to reclaim disk space by removing unused data. Data which was pinned on the server will never be garbage collected. A directory or file is content addressed, this mean is has a unique address derived from its content, which mean the URL of a resource depends on its content, if you update a directory, it will get a new URL because its content changed.

IPFS has a mechanism called IPNS which allows to publish a content hash under a fixed hash, which is published to other peers under some conditions, this is the only way to provide an address that never change but in which you can change the content. An IPNS address requires an associated cryptographic key that is managed by kubo, if you want to pin a resource for each people you share data with, you will need a dedicated key for each.

Data do not propagate magically over IPFS, if you add data to your node, it remains local until another node pulls in the content, then it will be able to distribute the data too, but there is a high chance it gets garbage collected one day if the node owner do not pin your data.

IPFS has no at rest encryption or access control available, it is a method to publish data.

4. Configuring a node §

First, you need to install Kubo, make sure to not install kubo-desktop which is unfortunately incompatible with a private swarm.

I personally prefer to use it in a container version for sandboxing reasons.

Generate the swarm.key with this code:

echo -e "/key/swarm/psk/1.0.0/\n/base16/\n$(openssl rand -hex 32)" > swarm.key

Download the ipfs-webui which is normally downloaded by kubo from the IPFS network, but as you are not connecting to it, you need to inject the webui in your kubo. On the following URL, download the "car" file that matches your kubo version.

Now, create and init your IPFS daemon using this script, if you do not use podman and prefer to use ipfs binary, replace the long podman command by just ipfs.

The script takes swarm.key as the first parameter and the web-ui car file as a second parameter:

#!/bin/sh

set -xe

if ! test -f "$1"
then
    echo "You must give the swarm.key file in parameter"
    exit 1
fi

if ! test -f "$2"
then
    echo "You must give the ipfs-webui@v4.13.0.car file in parameter (version should match your kubo version)"
    echo "You can download it from https://github.com/ipfs/ipfs-webui/releases"
    exit 1
fi

# execute commands with ipfs
run() {
 podman run --replace --name kubo \
   -e LIBP2P_FORCE_PNET=1 -e IPFS_PROFILE=lowpower \
   --userns=keep-id \
   -v $HOME/.ipfs/:/data/ipfs:Z \
   docker.io/ipfs/kubo:release $*
}

# import webui, requires stdin
import_webui() {
 podman run --replace -i --name kubo \
   -e LIBP2P_FORCE_PNET=1 -e IPFS_PROFILE=lowpower \
   --userns=keep-id \
   -v $HOME/.ipfs/:/data/ipfs:Z \
   docker.io/ipfs/kubo:release dag import < "$1"
}

# make sure the directory exist
# you can adjust if you want to store it elsewhere
mkdir -p ~/.ipfs

# copy the swarm key
cp "$1" ~/.ipfs/swarm.key

# uncomment this if you do not use the container version which does it automatically
#run init

# allow server to give other peers address
run config Routing.Type dht

# allow other peers to relay if a peer is not directly reachable by us but another peer can
run config --json Swarm.RelayClient.Enabled true

# remove all the default stuff
run bootstrap rm --all
run config --json Routing.DelegatedRouters '[]'
run config --json Bootstrap '[]'

# private swarm key = TLS can not be used but it is still encrypted
run config --json AutoTLS.Enabled false
run config --json AutoConf.Enabled false

# allow to use IPNS through pubsub and advertise often
run config --json Ipns.UsePubsub true
run config Ipns.RecordLifetime 240h
run config Ipns.RepublishPeriod 1m
run config Ipns.MaxCacheTTL 1m

import_webui "$2"

echo "Setup successful"
echo "You can visit http://localhost:5001/webui/ after starting the server"

Now, start the server (use ipfs daemon and the environment variables if not using the container):

podman run --replace --name kubo --restart=always \
  -e LIBP2P_FORCE_PNET=1 \
  -e IPFS_PROFILE=lowpower \
  -p 8080:8080 -p 127.0.0.1:5001:5001 -p 4001:4001 -p 4001:4001/udp \
  --userns=keep-id \
  -v $HOME/.ipfs/:/data/ipfs:z docker.io/ipfs/kubo:release

On the webui, in the Peers menu, add your other peers.

5. 24/7 server specific setup §

The snippet above will work for a server, but you want to change a few things:

If you use a container with restricted network, you need to give a reachable IP address to announce to your other peers:

ipfs config --json Addresses.Announce '["/ip4/192.168.1.166/tcp/4001"]'

You may also want it to be the DHT server to allow peers to discover each others through it, and maybe relay data between peers which could not connect directly:

# allow to give other peers address for mesh networking
ipfs config Routing.Type dhtserver

# allow to relay data between two peers that could not connect to each other
ipfs config --json Swarm.RelayService.Enabled true

You may also want to limit the amount of storage allowed in your Kubo server before the garbage collector free some space:

ipfs config Datastore.StorageMax "50GB"

6. Networking §

There are 4 differents ports in use that you need to be aware of:

  • Port 4001 TCP and port 4001 UDP are used by peers to exchange data between each other
  • Port 5001 TCP is used to reach the admin API and the webui, do not expose it publicly
  • Port 8080 TCP is used for the gateway, it allows to expose your IPFS private network content to people able to reach the gateway, without them installing kubo at all

7. Conclusion §

In my use case, I have a script regularly pinning a list of IPNS addresses on the server running the HTTPS gateway, so it always download the latest version of the IPNS published resources and make them available through the gateway even if the computers owning the file is offline.

This is actually not super useful as I could have done the same thing using Nextcloud, or with a script copying the files to a static HTTPS server.

8. Going further §

This infrastructure got more useful after adding a single ipfs-cluster service near my gateway running 24/7 allowing the nodes to use it as a remote pinning service. Now, a node can pin a CID on the gateway which make sure the data will be available locally there.