Skip to content

Docker and Compose Deployment

Codaris is distributed as one container image on the GitHub Container Registry. The image serves the web studio and the API and carries the toolchain, so a deployment is one container, one state directory, and the folders you want it to see.


The image

ghcr.io/primeomicx/codaris:latest      # the newest release
ghcr.io/primeomicx/codaris:v1.0.0      # a pinned release
Inside the container Version
Nextflow 25.10.4 (NXF_VER is pinned)
Java OpenJDK 17 (default-jre-headless)
nf-core tools 4.1.0
nf-test 0.9.5
Python 3.11, with a CPU build of PyTorch
Decision model Primeomicx/nf-pilot, cached; HF_HUB_OFFLINE=1
Docker CLI present; used only when the host socket is mounted
Versioned stores 1.1.1 (ruleset 1.1.0), fetched and sha256-verified at build time, so doctor inside the container finds every store and nothing is downloaded at start; the versioned stores describes what they hold and how a refresh reaches an image

Both linux/amd64 and linux/arm64 are published under every tag. docker buildx imagetools inspect ghcr.io/primeomicx/codaris:latest lists the platforms.

The startup banner prints the version and the resolved state directory; GET /health returns "version" as well.


1. Compose

The repository's docker/docker-compose.unified.yml is the reference deployment. It binds both ports to the loopback interface, passes HOME through, mounts the state directory and a projects folder at the same paths inside and outside the container, mounts the Docker socket, and loads backend/.env when it exists.

git clone https://github.com/PrimeOmicX/codaris.git
cd codaris
cp backend/.env.example backend/.env    # optional: keys as environment variables
docker compose -f docker/docker-compose.unified.yml up -d

What the file sets, and the variables that adjust it:

Setting Value Adjust with
Image ghcr.io/primeomicx/codaris:latest CODARIS_IMAGE_TAG=v1.0.0
Ports 127.0.0.1:3000 (studio), 127.0.0.1:8000 (API) edit the file to publish on another interface
Projects mount ${HOME} at the same path CODARIS_PROJECTS_DIR=/path/to/projects
State mount ${HOME}/.codaris at the same path keep; the backend resolves ~/.codaris from HOME
Docker socket /var/run/docker.sock CODARIS_HOST_DOCKER_SOCKET=${HOME}/.docker/run/docker.sock on macOS, or remove the line
Allowed roots ${HOME}:/media:/mnt:/Volumes:/tmp CODARIS_ALLOWED_DIRS=/path/a:/path/b
Local model URL http://host.docker.internal:11434/v1 CODARIS_LOCAL_LLM_BASE_URL_DOCKER=…

docker compose -f docker/docker-compose.unified.yml up -d --build builds the image from the checkout instead of pulling it (CODARIS_VERSION=dev is stamped unless you set it).

Check the deployment:

curl -s http://127.0.0.1:8000/health | jq '{status, version, model: .system1_nfpilot.status}'
docker logs codaris-runner | head -20

2. Plain docker run

docker run -d --name codaris \
  -p 127.0.0.1:3000:3000 -p 127.0.0.1:8000:8000 \
  -e HOME="${HOME}" \
  -v "${HOME}/.codaris:${HOME}/.codaris" \
  -v "${HOME}/projects:${HOME}/projects" \
  -v /var/run/docker.sock:/var/run/docker.sock \
  ghcr.io/primeomicx/codaris:latest

-e HOME="${HOME}" is what keeps the state in the mounted directory; without it the backend writes to /root/.codaris inside the container and loses it on docker rm. CODARIS_HOME=/some/mounted/path is the explicit alternative. The docker.sock mount is what the build → run → fix loop runs its -profile docker test runs on; without it every test run stops at the readiness check, and the startup log says so.

The state mount is also what carries your registered MCP servers: they live in mcp_servers.json in that directory, and the image has no such file, so a run without the mount starts with the built-in server only. Their commands run inside the container; see MCP servers.


3. Reaching the studio from another machine

The studio calls the API on its own origin (/api/... and /health are proxied by the Next.js server to the backend inside the container), so publishing port 3000 alone is enough for the browser, and the API port can stay on the loopback interface for local tools. Put a TLS-terminating reverse proxy in front of port 3000 when the studio is reachable beyond your own machine; the container has no authentication of its own.

For a deployment that serves the API on a separate public origin, build the image with --build-arg NEXT_PUBLIC_API_URL=https://api.example.org and name the studio's origin in CODARIS_CORS_ORIGINS (comma-separated) on the backend.


4. The Docker socket on macOS

Docker Desktop keeps the host socket in your user directory:

CODARIS_HOST_DOCKER_SOCKET="${HOME}/.docker/run/docker.sock" \
  docker compose -f docker/docker-compose.unified.yml up -d

With this mount, Nextflow inside the container launches biocontainers on the host engine for -profile docker runs, and the generated pipeline's work directory stays under the symmetric mount so both sides see the same paths.


5. The CLI in CI

The image's entrypoint starts the services, so a CI step runs the CLI through python -m app.cli with the entrypoint overridden. The example below scores a pipeline in a GitHub Actions job with the deterministic checkers and nf-core lint only (--quick), which needs no model key:

# .github/workflows/pipeline_check.yml
jobs:
  compliance:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Score the pipeline against the nf-core ruleset
        run: |
          docker run --rm \
            --entrypoint python \
            -v "${{ github.workspace }}:/workspace" \
            -w /app \
            ghcr.io/primeomicx/codaris:latest \
            -m app.cli review /workspace --quick --json > review.json
          jq '{overall_score, grade, failed}' review.json
      - uses: actions/upload-artifact@v4
        with:
          name: compliance-report
          path: |
            review.json
            docs/compliance-report.md
            docs/compliance-report.json

review exits 0 on success, 1 on failure, and 2 when a required capability is unavailable; gate the job on the score with jq -e '.overall_score >= 90' review.json when you want a threshold.


6. Repository CI and Actions minutes

This section is for maintainers of the Codaris repository. Actions bills each job by the minute, rounded up. Over 110 successful runs of the verify workflow the jobs billed about 14 minutes for backend, 2 for frontend, and 1 each for secrets and docs; structure bills 1 and the image check build about 12. A full push therefore costs about 19 minutes before the image build.

Change What it saves
concurrency group ci-<workflow>-<ref> with cancel-in-progress on verify, structure and image check A new push to a ref cancels the run still going on it. Pull-request runs already cancelled this way; what is new is a push to main cancelling an unfinished run for the commit before it. A cancelled verify run stops billing at once, so up to about 19 minutes is saved each time two pushes overlap.
A changes job in verify that diffs the pull request against its base (a push against the previous commit) backend and frontend install and test only when something they read changed, and otherwise print no backend changes or no frontend changes and succeed, so the required check names stay green. A skipped backend bills about 1 minute instead of 14 and a skipped frontend 1 instead of 2, against 1 minute for changes. A manual run, a new branch, or a base that cannot be fetched runs everything.
image check runs by hand and on a push to main that touches docker/, backend/requirements.txt, frontend/package.json, frontend/pnpm-lock.yaml, the stores manifest or pyproject.toml The 12-minute build no longer runs on pull-request pushes; it runs once per merge that changes the Dockerfiles or the dependency files. Check a branch before merging with gh workflow run image_check.yml --ref <branch>.
Dependabot: weekly, at most two open pull requests per ecosystem, minor and patch updates in one, and security updates for pip and the root uv.lock grouped Each Dependabot pull request runs the full verify workflow, so fewer and larger pull requests mean fewer runs.

The backend filter is wider than backend/ and src/. The backend suite also reads docker/, scripts/, docs/reference/env-vars.md, the MCP and container quickstart pages, two workflow files, and a few frontend sources and fixtures that its cross-checks open. A change to any of them runs backend, so none can merge with that suite skipped. When a backend test starts reading a new file outside backend/, add the file to the changes job in .github/workflows/ci_verify.yml.