This tutorial shows the current Antithesis-style integration: run the
published image as a long-lived amaru-relay-N service. The relay
container bootstraps itself from a paired cardano-node, then replaces the
shell wrapper with amaru run.
For the lower-level one-shot producer CLI, see Bootstrap producer.
- A Compose testnet with cardano-node producers already configured.
- One state volume per cardano-node producer.
- One config volume per cardano-node producer.
- One private
/srv/amarustate volume per Amaru relay. - A shared startup-marker volume for the Antithesis sidecar.
- An
amaru-runtime/directory containingera-history.jsonandglobal-parameters.json. - A published image pinned by full commit SHA:
ghcr.io/lambdasistemi/amaru-bootstrap-producer:<full-commit-sha>.
Do not use a moving tag as the integration contract. Pick the commit SHA that passed this repository's CI and pin that exact tag in the downstream stack. Same-repository PRs also publish immutable preview tags of the form:
ghcr.io/lambdasistemi/amaru-bootstrap-producer:pr-<pr-number>-<full-pr-head-sha>
Place the custom testnet runtime files next to the Compose file:
testnets/cardano_amaru_epoch360/
|-- amaru-runtime/
| |-- era-history.json
| `-- global-parameters.json
`-- docker-compose.yaml
The relay entrypoint passes these files to amaru run with:
--era-history-file /amaru-runtime/era-history.json
--global-parameters-file /amaru-runtime/global-parameters.json
Keep them aligned with the genesis/config emitted by the cardano-node configurator. For short-epoch generated networks, stale runtime files can make Amaru compute epoch boundaries or Praos parameters differently from cardano-node.
The image default entrypoint is bootstrap-producer, so relay services
must override it:
x-amaru: &amaru
image: ghcr.io/lambdasistemi/amaru-bootstrap-producer:<full-commit-sha>
entrypoint: amaru-relay-bootstrap
environment:
AMARU_LOG: info
AMARU_COLOR: never
AMARU_NETWORK: testnet_42
AMARU_BOOTSTRAP_RETRY_SECONDS: "5"
restart: always
services:
p1:
image: ghcr.io/intersectmbo/cardano-node@sha256:<digest>
# normal cardano-node service omitted
volumes:
- p1-configs:/configs:ro
- p1-state:/state
amaru-relay-1:
<<: *amaru
container_name: amaru-relay-1
hostname: amaru-relay-1.example
depends_on:
p1:
condition: service_started
environment:
AMARU_LOG: info
AMARU_COLOR: never
AMARU_NETWORK: testnet_42
AMARU_BOOTSTRAP_RETRY_SECONDS: "5"
RELAY_NAME: amaru-relay-1
AMARU_PEER: p1.example:3001
volumes:
- p1-state:/live:ro
- p1-configs:/cardano/config:ro
- ./amaru-runtime:/amaru-runtime:ro
- amaru-startup:/startup
- a1-state:/srv/amaru
volumes:
p1-configs:
p1-state:
amaru-startup:
a1-state:Repeat the relay service for each paired producer, changing
RELAY_NAME, AMARU_PEER, and the mounted state/config volumes.
The relay writes:
/startup/$RELAY_NAME.started
It writes that file immediately, before the bootstrap loop. The sidecar can gate setup-complete on these markers:
sidecar:
image: ghcr.io/cardano-foundation/cardano-node-antithesis/sidecar:<tag>
entrypoint: /bin/bash
command:
- -ec
- |
for relay in amaru-relay-1 amaru-relay-2; do
while [ ! -f "/amaru-startup/$${relay}.started" ]; do
sleep 1
done
done
exec /bin/sidecar
volumes:
- amaru-startup:/amaru-startup:roComposer checks should still assert later Amaru progress. The startup marker only proves that the relay container entered its contract.
INTERNAL_NETWORK=false docker compose -f testnets/cardano_amaru_epoch360/docker-compose.yaml config
docker compose -f testnets/cardano_amaru_epoch360/docker-compose.yaml up -d
docker compose -f testnets/cardano_amaru_epoch360/docker-compose.yaml logs -f amaru-relay-1Expected relay log shape:
[amaru-relay-1] startup marker written: /startup/amaru-relay-1.started
[amaru-relay-1] bootstrap attempt #1: refreshing snapshot from /live
[amaru-relay-1] bootstrap attempt #1: invoking bootstrap-producer
[amaru-relay-1 bootstrap-producer] + era-readiness predicate satisfied - target_slot=...
[amaru-relay-1 bootstrap-producer] + ledger-state-emitter @ ...
[amaru-relay-1 bootstrap-producer] + amaru import-ledger-state
[amaru-relay-1] bootstrap attempt #1: committed bundle to /srv/amaru
[amaru-relay-1] bundle already complete at /srv/amaru, skipping bootstrap loop
[amaru-relay-1] bundle ready at /srv/amaru, exec'ing amaru run
Transient producer exits are normal while the paired cardano-node is
still growing enough immutable history. The relay refreshes /live and
tries again after AMARU_BOOTSTRAP_RETRY_SECONDS.
After promotion, the relay's private /srv/amaru volume contains:
/srv/amaru/
|-- .bootstrap-complete
|-- chain.testnet_42.db/
|-- ledger.testnet_42.db/
|-- snapshots/
|-- nonces.json
`-- headers/
amaru run opens the stores directly from that directory:
--ledger-dir /srv/amaru/ledger.testnet_42.db
--chain-dir /srv/amaru/chain.testnet_42.db
The ledger store must include live/ and at least three numeric
historical snapshots. The chain store must include the exact header for
the latest ledger snapshot.
Relay failures are usually visible in the wrapper prefix:
| Symptom | What to check |
|---|---|
RELAY_NAME is required |
Set RELAY_NAME or pass it as the first positional argument. |
AMARU_PEER is required |
Set AMARU_PEER or pass it as the second positional argument. |
cardano-node /live not yet usable |
Check the paired state volume and whether cardano-node created immutable, ledger, volatile, protocolMagicId, and lock. |
repeated transient rc=1 |
ChainDB is not ready or the snapshot copy is too early. |
repeated transient rc=2 |
The chain has not reached the producer's era-readiness window. |
fatal rc=3 |
Config/genesis files are missing or invalid. |
fatal rc=9 |
One of the Amaru import commands failed. Check the prefixed producer output. |
| Amaru starts then fails VRF/nonce checks | Check amaru-runtime/era-history.json and global-parameters.json against the generated testnet. |
In relay mode, avoid depends_on: service_completed_successfully for
Amaru. The relay container is expected to keep running as amaru run;
waiting for it to complete creates a deadlock.
For development, the lower-level producer is still available:
nix run .#bootstrap-producer -- \
/path/to/cardano-node/db \
/path/to/cardano-node/config \
/tmp/amaru-bundle \
testnet_42That command writes /tmp/amaru-bundle/testnet_42 and exits. It is the
primitive used by the relay wrapper, not the recommended Antithesis
Compose shape.