Cortex

Containers

Cortex is distributed as the metaloom/cortex-server image. It runs as a daemon: it connects to a Loom server, registers, and serves node tasks. See Container Images for the image overview.

Running with Docker

docker run -d \
  --name cortex \
  --network dev \
  -p 8093:8093 \
  -e LOOM_HOST=loom \
  -e LOOM_PORT=8092 \
  -e LOOM_TOKEN=<jwt> \
  -v /media/assets:/data \
  metaloom/cortex-server:latest

The media at /data must be the same paths Loom hands out — Loom sends a reference (a path), never the bytes, so the worker needs to see the shared media mount itself.

Docker Compose with Loom

version: "3.8"
services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_DB: loom
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: secret

  loom:
    image: metaloom/loom-server:latest
    depends_on: [postgres]
    ports:
      - "8092:8092"
    environment:
      LOOM_DB_HOST: postgres
      LOOM_DB_PASSWORD: secret

  cortex:
    image: metaloom/cortex-server:latest
    depends_on: [loom]
    environment:
      LOOM_HOST: loom
      LOOM_PORT: "8092"
    volumes:
      - /media/assets:/data

Kubernetes Deployment

Because Cortex is a long-running worker (not a batch job), deploy it as a Deployment and scale the replica count to add processing capacity. Point each replica at the Loom service and wire the monitoring port to probes:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: cortex
spec:
  replicas: 3
  selector:
    matchLabels: { app: cortex }
  template:
    metadata:
      labels: { app: cortex }
    spec:
      containers:
        - name: cortex
          image: metaloom/cortex-server:latest
          env:
            - name: LOOM_HOST
              value: loom.svc.cluster.local
            - name: LOOM_PORT
              value: "8092"
          ports:
            - containerPort: 8093
          livenessProbe:
            httpGet: { path: /api/health, port: 8093 }
          readinessProbe:
            httpGet: { path: /api/ready, port: 8093 }
          volumeMounts:
            - name: media
              mountPath: /data
      volumes:
        - name: media
          persistentVolumeClaim:
            claimName: media-pvc
Tip
To pin heavy node kinds (e.g. facedetect, whisper) to GPU nodes, run a second Deployment that advertises only those kinds and schedule it onto the GPU node pool. Loom routes each node task to a worker that accepts its kind. See Configuration.

Looking for something else?