IPCR Forge
From git push to docker pull on any server. Work in Gitea as
usual: its Actions build your images, IPCR serves them from IPFS, and every public repository is
mirrored to the Radicle peer-to-peer network.
gitea_admin.
Published imagesWhat this forge serves from IPFS, with the line to put in a compose file.
On RadicleThe public repositories mirrored to the Radicle network. Public.
How it works
git push a v* tag to Gitea, or create it in Gitea's web UI.gitea/workflows/ and pushes the image to localhost:5000ipcr.localhost:4767/ipns/…Steps 2 and 3 are automatic and usually take a few minutes. The very first build on this server takes longer (about 5 to 10 minutes) while it downloads its build images.
Alongside, every public repository is copied to Radicle
within seconds of each push: branches and tags, signed by this server's Radicle node. Gitea stays
the one place to work. The Radicle copy is for distribution: anyone can rad clone it,
and it stays available even when this server is not. Private repositories are never copied.
Try it with the example
The install created gitea_admin/ipcr-hello, a tiny web
page with a Dockerfile and the workflow that publishes it.
- Open it in Gitea, then Tags → New tag (or Releases → New release): name
v1.0.0, targetmain. - Follow the build under the repository's Actions tab.
- About a minute after it finishes, the image appears below under Published images, and the tag on Radicle.
Published images
Loading…
Publish your own project
- Create a repository in Gitea (+ → New repository)
and push your code to it:
Git asks for your Gitea user name and password (or an access token, from Settings → Applications).git remote add origin https://gitea-<domain>/<owner>/<repo>.git git push -u origin main - Add a workflow at
.gitea/workflows/build.yml. This one builds theDockerfileat the root for everyv*tag. It is a GitHub-style workflow with three differences: it logs in tolocalhost:5000, this forge's staging registry, as the repository itself (the forge sets theIPCR_PUSH_TOKENsecret on every public repository allowed to publish), the image goes there under the repository's own name, and buildx's builder runs withnetwork=hostso it can reach that registry.name: image on: push: tags: ['v*'] jobs: image: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: registry: localhost:5000 username: ${{ github.repository }} password: ${{ secrets.IPCR_PUSH_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: localhost:5000/${{ github.repository }} tags: | type=semver,pattern={{version}} type=raw,value=latest - uses: docker/setup-buildx-action@v3 with: driver-opts: network=host - uses: docker/build-push-action@v6 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - Release:
The image is published asgit add .gitea && git commit -m "Build the image" git tag v1.0.0 git push origin main v1.0.0<owner>/<repo>:1.0.0under this forge's name. Repositories of the forge's own organisation are also published at the root:<repo>:1.0.0.
Who can publish: each public repository publishes under its own name only, with its own push credential; the short root names belong to the forge's organisation, so to its members. Workflows from forks get no secrets, so they cannot publish. Private repositories never publish.
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/k51…/<owner>/<repo>:1.0.0 # a tag: follows new builds of 1.0.0
# image: ipcr.localhost:4767/ipfs/bafkrei… # a CID: this exact image, forever
Or pull it by hand: docker pull ipcr.localhost:4767/ipns/k51…/<owner>/<repo>:1.0.0.
/ipns/… is a name this forge can move to a new build; /ipfs/<cid> is the
image's content address and can never change.
For a readable name, point a name you own at this forge's publisher name
(k51…), which holds every image it builds:
- ENS: set the content hash of
example.ethtoipns://k51…(one transaction, once). Images are thenipcr.localhost:4767/ipns/example.eth/<owner>/<repo>:1.0.0. - DNS: add a TXT record
_dnslink.example.com=dnslink=/ipns/k51…. Images are thenipcr.localhost:4767/ipns/example.com/<owner>/<repo>:1.0.0.
New releases need no change to either record: they update the publisher name.
On Radicle
Public repositories of this forge, as mirrored to the Radicle network by this server's node
(…). Clone one with rad clone <id> from any Radicle node.
Loading…
A repository that turns private or is deleted in Gitea is no longer synced (frozen). What was already published stays on the Radicle network: it cannot be taken back.
Pull without IPCR
To try an image on a machine without IPCR, pull it through this forge's public front door, with plain Docker and nothing to install:
docker pull ipcr-<domain>/ipns/<name>/<repo>:1.0.0
It serves this forge's images only, read-only, and depends on this server being online. For anything that should keep working without it, use IPCR's local address (below): any IPCR node can serve the image.
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.