Skip to content

Install on separate Docker hosts

For guided installation, use the managed installer.

Put the browser interface on one machine and the server on another. At the end the Console reaches the server over an authenticated private connection.

Skip this page if you run both containers on one machine, described in Install on one Docker host, or on a cluster, described in Install on Kubernetes.

Before you start

  1. Two Linux hosts on amd64 with Docker Engine 23.0 or newer and Docker Compose 2.24.4 or newer, that meet Check the host before you install. Check out the same IRIS release on both.
  2. The image build prerequisites from Build the server and Console images. Build each image after configuring that host's environment file below. The tested aria2c clients are included in the repository, and the build scripts verify them.
  3. The two public signing roots on the server host, from Create the two offline signing keys, and the firewall permits in Open the required ports.

Run the time check on each host:

bash tools/check-host-time.sh

Both hosts must report a healthy clock.

Choose the addresses

Pick all three before you provision either host.

Address Used by
Server device address, IRIS_HOST_IP Device catalog, tracker, artifacts, and origin seeder.
Server private management address The Console's IRIS_MANAGEMENT_API_URL, normally https://iris-mgmt.example.com:9443. Its hostname must resolve from the Console container.
Console browser address Operators, for example https://console.example.com:8080. Set the same full URL as IRIS_CONSOLE_URL on the server.

The management address can share the device-facing interface if that interface is on a trusted management network. Bind port 9443 to the management address and let only the Console host through the firewall. Restrict the Console browser port to trusted operator sources.

What lives on which host

Only the server host holds $IRIS_CONFIG/instr/signing-key.age, the separate age identity, $IRIS_RUN/instr/signing-key runtime plaintext, and durable instruction state under $IRIS_STATE. The Console holds its management token and CA files and its browser identity. The two offline-root public keys are public material; their private keys stay with separate offline key holders.

Warning

Never copy server data, the age identity, or the management private key to the Console host.

Keep the Compose project name, environment files, and server volumes unchanged.

Certificates and trust

Identity Private key location Trusted by
Catalog/device certificate Encrypted server configuration, decrypted into server tmpfs Device agents and installers.
Management certificate Server host's management-tls directory Console's management-ca/ca.pem.
Default browser certificate Console host's console-tls directory Operator browsers.

The management certificate must cover the exact DNS name or IP in IRIS_MANAGEMENT_API_URL. The browser certificate must cover the Console URL.

  1. On a trusted operator machine, create both host bundles:

    umask 077
    mkdir -p ~/.config/iris
    python3 tools/prepare-docker-hosts.py \
      --out "$HOME/.config/iris/docker-hosts" \
      --management-host iris-mgmt.example.com \
      --console-host console.example.com
    

    A server/ and a console/ directory hold self-signed certificates valid for 365 days and one scoped management token. The token has no automatic expiry; rotate it through the administration procedure.

  2. Copy the contents of server/ to /etc/iris/docker-hosts/ on the server host and the contents of console/ to that path on the Console host, over SSH or SCP. The server token path is then /etc/iris/docker-hosts/tier-auth/current.json.

  3. On each host, set ownership and keep the generated permissions:

    sudo chown -R 10001:10001 /etc/iris/docker-hosts
    sudo find /etc/iris/docker-hosts -type d -exec chmod 700 {} +
    sudo find /etc/iris/docker-hosts -type f -exec chmod 600 {} +
    
  4. Compare the management certificate fingerprint with the Console's ca.pem, then install the browser certificate in the operator trust store.

Start the server host

  1. Copy the server environment example from the repository root:

    cp server/server.env.example server/server.env
    
  2. Set every example address and path, at least COMPOSE_PROJECT_NAME, IRIS_HOST_IP, IRIS_MANAGEMENT_BIND_IP, IRIS_CONSOLE_URL, IRIS_AGE_KEY_FILE_HOST, IRIS_AGE_RECIPIENTS, IRIS_MANAGEMENT_TLS_DIR, IRIS_TIER_AUTH_DIR, IRIS_IMAGE_ROOT and IRIS_ARTIFACTS_HOST_DIR. See Server configuration and Install on one Docker host.

  3. Create the server age identity as in Configure the server, recording its path and public recipients in server/server.env, not server/.env. Load this layout's settings before preparing host paths:

    set -a
    . server/server.env
    set +a
    

    Prepare the image and artifact paths for uid 10001, as in Give the runtime user the host paths, using the configured paths. Then build and start a fresh server:

    iris_server() {
      docker compose --env-file server/server.env \
        -f server/docker-compose.server.yml "$@"
    }
    
    iris_server build --pull
    iris_server run --rm iris iris-bootstrap
    iris_server run --rm \
      -v "$HOME/iris-roots:/pub:ro" --entrypoint sh iris -c \
      'install -d -m 0755 "$IRIS_CONFIG/instr" "$IRIS_CONFIG/instr/roots.d" && \
       install -m 0644 /pub/*.pub "$IRIS_CONFIG/instr/roots.d/"'
    iris_server up -d
    iris_server ps
    

    iris_server ps shows the server container running. Put your approved directory of two distinct public roots in place of $HOME/iris-roots.

    Warning

    Do not replace roots on an existing server. Reuse its COMPOSE_PROJECT_NAME, container name, age identity and host paths, and skip bootstrap and the roots command.

  4. Turn on signing, described in Turn on instruction signing.

  5. Set the public roots directory in the same shell so the package helpers match the server:

    export IRIS_INSTRUCTION_ROOTS_DIR="$HOME/iris-roots"
    

    Then follow the commands for separate hosts in Build and publish the device packages.

Start the Console host

  1. Copy the Console environment example:

    cp server/console.env.example server/console.env
    
  2. Set its bind address, browser port, management URL, and directory paths.

  3. Start the Console:

    iris_console() {
      docker compose --env-file server/console.env \
        -f server/docker-compose.console.yml "$@"
    }
    
    iris_console build --pull
    iris_console up -d
    iris_console ps
    

    iris_console ps shows the Console container running. While the server is unreachable, API requests return a redacted 503 with Retry-After.

  4. Open the Console URL and claim the administrator account, described in Sign in for the first time. The account lives on the server, so a second Console shares it.

Verify

Run iris_server ps on the server host and iris_console ps on the Console host. Both report their container as running. If one does not, read its log:

iris_server logs --tail=100 iris
iris_console logs --tail=100 console

Then check the path end to end: sign in, open the device list, read the addresses in Settings, import an image, and stream a job log.

Next steps