Topology-driven iRODS test environment. Define any iRODS architecture as a YAML topology file; seabass generates the Docker Compose project and manages the lifecycle.
- Docker Engine 24+ with Compose v2 plugin
- Python 3.11+ with
pyyaml(pip install pyyaml)
# start the default topology (database + 1 provider + 1 client)
./seabass up
# open an iCommands shell
./seabass shell icom
# tear it down
./seabass downseabass up generates docker-compose.yml and _initdb.sql in the current directory, then runs docker compose up --build -d. Default topology is topologies/default.yaml.
icat/ PostgreSQL init SQL (referenced by generated compose)
irods-server/ Generic iRODS server image (provider or consumer via env var)
├── Dockerfile
├── start.sh Entrypoint — detects first boot vs restart
├── generate_config.py Builds unattended JSON from env vars
└── wait-for-it.sh
icom/ iCommands client image
├── Dockerfile
├── start.sh Reads env vars, runs iinit
└── wait-for-it.sh
topologies/ Pre-built topology files
├── default.yaml
└── multi-resource.yaml
seabass CLI tool (Python)
| Command | Description |
|---|---|
seabass up [topology] |
Build & start the topology (default: topologies/default.yaml) |
seabass down |
docker compose down -v — destroys containers + volumes |
seabass shell [service] |
docker exec -it <service> bash (default: icom) |
seabass test <topology> <script> |
Stand up, run a bash script, tear down |
seabass generate [topology] |
Generate docker-compose.yml without starting |
16 pre-built files in topologies/:
| File | Use case | Demonstrates |
|---|---|---|
default.yaml |
Quick dev session | Minimal: 1 DB + 1 provider + 1 client |
multi-resource.yaml |
Two resource servers | Multiple consumers with distinct resources |
three-consumers.yaml |
Storage tiering | 3 consumers named SSD / HDD / archive |
five-consumers.yaml |
Load-balancing tests | 5 consumers with numbered vaults |
eight-consumers.yaml |
Scale test | 8 consumers for registration + transfer stress |
single-consumer.yaml |
Baseline multi-server | Exactly 1 consumer, 1 client |
two-consumers-two-clients.yaml |
Concurrent ops | 2 consumers + 2 clients (alice, bob) |
two-clients.yaml |
Parallel access | 2 clients, one with host mount, one isolated |
many-clients.yaml |
Connection pool limits | 4 clients (icom + node1-3) |
storage-tiering.yaml |
Tiered storage | 3 resources on the provider (hot/warm/cold) |
data-ingest.yaml |
Ingest pipeline | Staging client (host mount) + archive client |
exposed-ports.yaml |
External tooling | Ports 1247/1248 mapped to host |
renamed-zone.yaml |
Production-like | Custom zone testzone, all keys changed |
no-mount.yaml |
Air-gapped testing | No host filesystem mounts |
custom-keys.yaml |
Full schema reference | Every optional field populated |
federation.yaml |
Future use | Reference stub for multi-compose federation |
zone_name: tempZone
admin_password: password
database:
hostname: icat
provider:
hostname: ies
resources:
- name: demoResc
type: unixfilesystem
path: /var/lib/irods/Vault
consumers: []
clients:
- hostname: icom
mount: "."All fields beyond zone_name, database.hostname, provider.hostname, and at least one provider.resources entry are optional:
zone_name: myZone
admin_password: s3cret
zone_key: MY_ZONE_KEY
negotiation_key: 12345678901234567890123456789012 # exactly 32 bytes
database:
hostname: postgres
image: postgres:17 # any supported version
user: mydbuser
password: mydbpass
db_name: MY_CATALOG
provider:
hostname: provider1
port: 1247
resources:
- name: fast_resc
type: unixfilesystem
path: /vault/fast
consumers:
- hostname: resc01
port: 1247
resources:
- name: resc01_vault
type: unixfilesystem
path: /vault/resc01
clients:
- hostname: admin
mount: /data/set
- hostname: analyst| Image | Role | Starts as |
|---|---|---|
icat (postgres:16) |
Database | Listens on 5432 |
irods-server |
Catalog provider or consumer | Wait for DB/provider, then setup_irods.py or irodsctl start |
icom |
iCommands client | Wait for provider, then iinit |
topology.yaml
│
▼
seabass (generates)
│
├── docker-compose.yml ──► docker compose up --build
├── _initdb.sql ──► postgres init scripts
│
▼
container starts
│
├── start.sh reads env vars
│
├── [/etc/irods/server_config.json exists?]
│ ├── yes → irodsctl start (restart path)
│ └── no → generate_config.py → setup_irods.py (first boot)
│
▼
iRODS ready
| Variable | Default | Description |
|---|---|---|
IRODS_SERVER_ROLE |
provider |
provider or consumer |
IRODS_ZONE_NAME |
tempZone |
iRODS zone name |
IRODS_ADMIN_PASSWORD |
password |
rodsadmin password |
IRODS_HOST |
container hostname | This server's hostname |
IRODS_PORT |
1247 |
iRODS port |
IRODS_RESOURCE_NAME |
demoResc |
Default resource name |
IRODS_VAULT_DIR |
/var/lib/irods/Vault |
Vault directory |
IRODS_DB_HOST |
icat |
Database hostname (provider only) |
IRODS_DB_PORT |
5432 |
Database port |
IRODS_DB_NAME |
ICAT |
Database name |
IRODS_DB_USER |
irods |
Database user |
IRODS_DB_PASSWORD |
password |
Database password |
IRODS_ODBC_DRIVER |
PostgreSQL ANSI |
ODBC driver name |
IRODS_CATALOG_PROVIDER |
— | Provider hostname (required for consumers) |
IRODS_ZONE_KEY |
TEMPORARY_ZONE_KEY |
Zone authentication key |
IRODS_NEGOTIATION_KEY |
32_byte_server_negotiation_key__ |
Must be exactly 32 bytes |
IRODS_CONTROL_PLANE_KEY |
32_byte_server_control_plane_key |
Control plane key |
IRODS_CONTROL_PLANE_PORT |
1248 |
Control plane port |
| Variable | Default | Description |
|---|---|---|
IRODS_PROVIDER_HOST |
ies |
Provider hostname to connect to |
IRODS_PROVIDER_PORT |
1247 |
Provider port |
IRODS_ZONE_NAME |
tempZone |
Zone name |
IRODS_ADMIN_USER |
rods |
rodsadmin username |
IRODS_ADMIN_PASSWORD |
password |
rodsadmin password |
When a server container starts, start.sh checks for /etc/irods/server_config.json:
- Not present (first boot / fresh container): runs
generate_config.pyto build the unattended JSON from environment variables, then runssetup_irods.py. The setup script creates the service account, database tables (if provider), and starts iRODS. - Present (container restart): runs
irodsctl startto restart the iRODS server process.
This means you can docker compose stop / docker compose start without re-running setup.
| Action | Command | iRODS state |
|---|---|---|
| First start | seabass up |
Fresh install |
| Stop | docker compose stop |
Stopped, config preserved |
| Restart | docker compose start |
Resumes via irodsctl start |
| Full reset | seabass down |
Containers + volumes destroyed |
# run a test script against a custom topology
./seabass test topologies/multi-resource.yaml ./my-test.shThe test command stands up the topology, executes the script, captures its exit code, then tears down. The test script runs on the host and can use docker exec to run iCommands inside the icom container.
Example test script (my-test.sh):
#!/bin/bash
set -e
# wait for iRODS to be ready
sleep 5
# run commands via the icom container
docker exec icom iinit <<< "password"
docker exec icom imkdir test-coll
docker exec icom iput -r /some/data /test-coll
docker exec icom ils -r /test-coll
echo "PASS"Edit irods-server/Dockerfile to pin a version:
RUN apt-get install -y irods-server=4.3.5-0~ubuntu24.04 irods-database-plugin-postgresOr replace the apt install with your own package source (local mirror, volume-mounted .deb files).
Federation requires multiple Compose projects. Create one topology per zone and stand them up separately:
seabass generate zone-a.yaml -o /tmp/zone-a
seabass generate zone-b.yaml -o /tmp/zone-b
docker compose -p zone-a -f /tmp/zone-a/docker-compose.yml up -d
docker compose -p zone-b -f /tmp/zone-b/docker-compose.yml up -dThen exchange zone keys manually via iadmin modzone and docker exec.