r

relay

v75f8de4library

No description provided.

0 stars0 forksUnknownupdated 2 months ago
Open repo

relay

Layer-efficient Docker image transfer to an embedded Linux device (Raspberry Pi, Jetson, etc.) over a direct cable connection.

Instead of docker save | ssh | docker load — which always transfers the full image — relay push starts a Registry v2 server on the laptop, opens a reverse SSH tunnel so the device's localhost reaches it, then triggers docker pull on the device. Docker only fetches layers it doesn't already have.

How it works

Mac                          Device (Pi / Jetson)
────────────────────         ──────────────────────
relay serve          SSH reverse tunnel
  (port 5050)         ←──────  localhost:5050
                              docker pull localhost:5050/<image>

The reverse tunnel means all traffic flows through the existing SSH connection — no firewall rules or insecure-registry config needed on the Mac side. The device needs localhost:5050 in its insecure-registries (see Setup).

Layer cache

ContainerdStore (used when available): reads original compressed blobs directly from Docker's containerd content store at /var/lib/docker/containerd/daemon/io.containerd.content.v1.content/blobs/sha256/. Because these are the original bytes from the registry, digests match Docker Hub exactly — layers the device already has from any previous pull are cache-hits.

DockerSaveStore (fallback): streams the image tar via the Docker Engine API, extracts and caches layer blobs on disk. Requires the containerd image store to be enabled in Docker so that docker save outputs OCI-format blobs (see "Enabling the containerd image store" below). This is the path used on macOS where the containerd store directory is inside Docker's VM.

Requirements

  • Mac: Odin compiler, Docker, SSH key access to the device
  • Device: Docker, Odin compiler (for make deploy)

Setup

1. Build and deploy

# Build relay for macOS
make build

# Sync source to the device and build there
# Edit DEVICE_USER / DEVICE_IP in Makefile first
make deploy

To install Odin on the device (one-time):

curl -L "https://github.com/odin-lang/Odin/releases/download/dev-2026-05/odin-linux-arm64-dev-2026-05.tar.gz" \
  | tar -xz && sudo mv odin-linux-arm64-*/odin /usr/local/bin/

2. Configure the device's Docker daemon

Docker requires an explicit host:port entry in insecure-registries to use HTTP for that registry. Run this once on the device:

echo '{"insecure-registries":["localhost:5050","127.0.0.1:5050"]}' \
  | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker

3. Push an image

# On the Mac — transfers only missing layers
./relay push dustynv/ros:humble-ros-base-l4t-r35.4.1 --to pi@192.168.1.25

The command prepares the image cache, starts a local registry, opens the SSH tunnel, then triggers docker pull on the device. Docker's own layer deduplication skips anything the device already has.

CLI reference

relay push <image> --to <user@host> [--port 5050]

Push an image to a device. Handles everything: cache prep, server, SSH tunnel, remote pull.

relay serve [--port 5050] [--cache-dir ~/.cache/relay]

Start the Registry v2 server in standalone mode (for use with pull or manual docker pull).

relay pull <image> --from <host:port>

Run on the device. Prints insecure-registry hints and pulls from the given registry.

relay list

List images available in the local Docker daemon.

Enabling the containerd image store

ContainerdStore gives better cache hit rates because layer digests match Docker Hub exactly. On Docker Desktop (macOS): Settings → Features in development → Use containerd for pulling and storing images. On Docker Engine (Linux):

{ "features": { "containerd-snapshotter": true } }

Project structure

odin-docker-local-registry/
├── registry/                # package registry — core library
│   ├── server.odin          # Registry v2 HTTP handlers (odin-http)
│   ├── store.odin           # Store abstraction + helpers
│   ├── containerd_store.odin
│   ├── dockersave_store.odin
│   ├── manifest.odin        # OCI manifest types + builder
│   ├── tar.odin             # Streaming tar reader
│   ├── docker_cli.odin      # Docker Engine API wrappers
│   └── docker_socket.odin   # Minimal HTTP client over Unix socket
├── tests/
├── main.odin                # CLI entry point
├── vendor/odin-http/
└── Makefile
Package Info
Version
v75f8de4
License
Unknown
Author
@hodgka
Type
library
Forks
0
Created
2 months ago
Updated
2 months ago
Health
maintained
has releases
has readme
has license
Activity
last 56 weeks