Kubo (IPFS node) integration
Blumen could be used together with Kubo, an IPFS node implementation, in a way that would avoid an upload step through Blumen and instead with a simple file transfer.
Uploading a distribution to a self-hosted IPFS node has certain benefits:
- Less reliance on centralized IPFS providers, most of which don't guarantee persistence of data
- Personal data not leaked or exposed
- Free forever besides server costs
- Expanding the network
- Faster retrieval on IPFS gateways
Configuration
It is recommended to enable accelerated DHT client to make the node appear in the DHT faster when locating the website distribution on the network:
ipfs config --json Routing.AcceleratedDHTClient true
Reannouncing pinned content to the network should be enabled as well. It is best to announce all CIDs within the distribution and not only the root one (the one that shows up in blumen pack
output):
ipfs config Reprovider.Interval 1h # Reannounce every hour
ipfs config Reprovider.Strategy pinned # Announce all pinned CIDs, including non-recursive
Enabling Auto TLS will make it possible for IPFS clients and certain IPFS gateways (such as inbrowser.link) to fetch content in a browser directly:
ipfs config --json AutoTLS.Enabled true
Manual
Packing the distribution
Blumen has a command that will only generate a CAR archive without uploading it anywhere called blumen pack
:
blumen pack <dir> --dist .
This will generate a .car distribution archive in a current directory.
Uploading on a remote server
The simplest and fastest way to upload the distribution would be with rsync
. The target server should have SSH enabled.
rsync -avz ./dist.car user@ip:/home/user
Importing to Kubo
Once the file is on the server, the only thing left is importing it to Kubo through DAG import.
ipfs dag import ./dist.car
Now the distribution is both uploaded to Kubo and is recursively pinned.
Confirm via
ipfs pin ls --type=recursive
Automatic
It is not required to do that manually and the process is fairly easy to automate. Below is a sample GitHub Actions workflow for uploading a website distribution generated by Blumen to a remote server with Kubo.
name: Deploy to Kubo
on:
push:
branches: main
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
cid: ${{ steps.pack_blumen.outputs.cid }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
with:
deno-version: v2.2.2
- name: Install dependencies
run: cd www && deno install --allow-scripts
- uses: actions/cache@v4
with:
path: |
~/.deno
~/.cache/deno
key: ${{ runner.os }}-deno-${{ hashFiles('**/deps.ts') }}
- name: Build website
run: |
deno task build
- name: Pack the website with Blumen
id: pack_blumen # <- Required for step outputs
run: |
output=$(deno --allow-read --allow-env --allow-write npm:blumen pack --dist .)
echo "$output"
cid=$(echo "$output" | grep -oP 'Root CID: \K\S+')
echo "CID=$cid" >> $GITHUB_OUTPUT
echo "$output" >> $GITHUB_STEP_SUMMARY
- name: Upload the CAR file via Rsync
uses: burnett01/[email protected]
with:
switches: -avzr --delete
path: dist.car
remote_path: /var/lib/ipfs/staging
remote_host: ${{ secrets.REMOTE_HOST }}
remote_user: ${{ secrets.REMOTE_USER }}
remote_key: ${{ secrets.REMOTE_KEY }}
- name: IPFS DAG import to Kubo
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_KEY }}
script: docker exec ipfs_host ipfs dag import /export/dist.car
name: Deploy to Kubo
on:
push:
branches: main
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
cid: ${{ steps.pack_blumen.outputs.cid }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install
- uses: actions/cache@v4
with:
path: ~/.bun
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
- name: Build website
run: bun run build
- name: Pack the website with Blumen
id: pack_blumen
run: |
output=$(bunx blumen pack --dist .)
echo "$output"
cid=$(echo "$output" | grep -oP 'Root CID: \K\S+')
echo "CID=$cid" >> $GITHUB_OUTPUT
echo "$output" >> $GITHUB_STEP_SUMMARY
- name: Upload the CAR file via Rsync
uses: burnett01/[email protected]
with:
switches: -avzr --delete
path: dist.car
remote_path: /var/lib/ipfs/staging
remote_host: ${{ secrets.REMOTE_HOST }}
remote_user: ${{ secrets.REMOTE_USER }}
remote_key: ${{ secrets.REMOTE_KEY }}
- name: IPFS DAG import to Kubo
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_KEY }}
script: docker exec ipfs_host ipfs dag import /export/dist.car
name: Deploy to Kubo
on:
push:
branches: main
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
cid: ${{ steps.pack_blumen.outputs.cid }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Install dependencies
run: pnpm install
- uses: actions/cache@v4
with:
path: |
~/.pnpm-store
node_modules
key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Build website
run: pnpm run build
- name: Pack the website with Blumen
id: pack_blumen
run: |
output=$(pnpm dlx blumen pack --dist .)
echo "$output"
cid=$(echo "$output" | grep -oP 'Root CID: \K\S+')
echo "CID=$cid" >> $GITHUB_OUTPUT
echo "$output" >> $GITHUB_STEP_SUMMARY
- name: Upload the CAR file via Rsync
uses: burnett01/[email protected]
with:
switches: -avzr --delete
path: dist.car
remote_path: /var/lib/ipfs/staging
remote_host: ${{ secrets.REMOTE_HOST }}
remote_user: ${{ secrets.REMOTE_USER }}
remote_key: ${{ secrets.REMOTE_KEY }}
- name: IPFS DAG import to Kubo
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.REMOTE_KEY }}
script: docker exec ipfs_host ipfs dag import /export/dist.car