IPCR Forge

From git push to docker pull on any server, without a central service. Code lives on Radicle, Gitea Actions builds it, and IPCR serves the image from IPFS.

RadicleBrowse repositories, issues and patches. Public. GiteaBuilds, logs and the staging registry. Sign in as gitea_admin. Published imagesWhat this server serves from IPFS, with the line to put in a compose file.

How it works

1 · Pushgit push rad a v* tag to the Radicle network
→
2 · Bridgerepos with .gitea/workflows/ are copied into Gitea
→
3 · BuildGitea Actions builds the image into Gitea's registry
→
4 · IPFSIPCR imports each tag and publishes it under an IPNS name
→
5 · Pullany server with IPCR: ipcr.localhost:4767/ipns/…

Steps 2 to 4 are automatic and take two to three minutes. A repository needs no configuration beyond committing a workflow.

Published images

Loading…

Publish a project

  1. Install Radicle on your machine and create an identity: see radicle.xyz, then rad auth.
  2. Connect to this server's node so your pushes reach it:
    rad node connect <node-id>@<this server>:8776
  3. Publish the repository from its folder: rad init. It prints a repository ID (rad:z…).
  4. Let this node seed it. The node only keeps repositories it is told to (seeding policy block). In this server's Terminal app or over SSH:
    docker exec radicle-node rad seed rad:z… --scope all
  5. Add a workflow at .gitea/workflows/build.yml. This one builds the Dockerfile at the root and publishes it for every v* tag. The REGISTRY_TOKEN secret is already set for every repository.
    on:
      push:
        tags: ['v*']
    jobs:
      image:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - name: Registry address
            id: registry
            run: echo "host=${GITHUB_SERVER_URL#https://}" >> "$GITHUB_OUTPUT"
          - uses: docker/metadata-action@v5
            id: meta
            with:
              images: ${{ steps.registry.outputs.host }}/${{ github.repository }}
              tags: |
                type=semver,pattern={{version}}
                type=raw,value=latest
          - uses: docker/setup-buildx-action@v3
          - uses: docker/login-action@v3
            with:
              registry: ${{ steps.registry.outputs.host }}
              username: ${{ github.repository_owner }}
              password: ${{ secrets.REGISTRY_TOKEN }}
          - uses: docker/build-push-action@v6
            with:
              push: true
              tags: ${{ steps.meta.outputs.tags }}
              labels: ${{ steps.meta.outputs.labels }}
  6. Release:
    git tag v1.0.0
    git push rad main v1.0.0
    Follow the build in Gitea under gitea_admin/<repo> → Actions. A minute after it finishes, the tag appears under Published images.

Use an image

Copy the line from Published images into a compose file. It works on this server and on any server running IPCR:

services:
  myapp:
    image: ipcr.localhost:4767/ipns/k51qzi5uqu5d…:1.0.0   # a tag: follows new builds of 1.0.0
    # image: ipcr.localhost:4767/ipfs/bafkrei…           # a CID: this exact image, forever

/ipns/<name>:<tag> is a name the publisher can move to a new build. /ipfs/<cid> is the image's content address and can never change.

For a readable name, add a DNS TXT record _dnslink.example.com = dnslink=/ipns/k51… to a domain you own. The image is then ipcr.localhost:4767/ipns/example.com:1.0.0.

Pull from another server

Any server with IPCR resolves the same lines and fetches the image from the IPFS network, from this server and from every other server that already pulled it.

On a Yundera server

Install IPCR from the App Store. There is nothing to configure.

On any Linux server with Docker 29+

mkdir -p /DATA/AppData/ipcr && cd /DATA/AppData/ipcr
curl -fsSLO https://raw.githubusercontent.com/Yundera/ipcr/main/apps/IPCR/docker-compose.yml
install -d -m 0700 -o 1000 -g 1000 kubo && install -d -o 1000 -g 1000 init.d
curl -fsSL -o init.d/01-ipcr-node.sh https://raw.githubusercontent.com/Yundera/ipcr/main/apps/IPCR/seed/init.d/01-ipcr-node.sh
chmod 0755 init.d/01-ipcr-node.sh
printf 'PUID=1000\nPGID=1000\nTZ=UTC\nAppID=ipcr\n' > .env
docker compose up -d

IPCR listens on 127.0.0.1:4767 only, and trusts itself through /etc/docker/certs.d/ipcr.localhost:4767/ca.crt. It publishes port 4768 (TCP and UDP) for IPFS peers. Details: github.com/Yundera/ipcr.

Addresses on this server

AddressWhat
Radicle explorer (public)
Radicle API and read-only git over HTTPS (public)
Gitea: builds, logs, staging registry
ipcr.localhost:4767IPCR registry, for this server's Docker only
TCP 8776 · TCP/UDP 4768Radicle peers · IPFS peers