Skip to content

Feature/docker compose - #349

Open
opticon454 wants to merge 2 commits into
Ark0N:masterfrom
opticon454:feature/docker-compose
Open

Feature/docker compose#349
opticon454 wants to merge 2 commits into
Ark0N:masterfrom
opticon454:feature/docker-compose

Conversation

@opticon454

Copy link
Copy Markdown

Summary

This PR adds first-party Docker Compose support for Codeman while preserving its ability to create and manage isolated agent containers.

With this PR Codeman itself runs inside a container & it can still spawn isolated Claude, Codex, Gemini, OpenCode, and shell case containers through the host Docker daemon. These are sibling containers, not nested Docker-in-Docker containers.

The Compose deployment:

  • Runs Codeman and local agent sessions as an unprivileged user.
  • Mounts the host Docker socket for isolated container management.
  • Translates container-side credential and seed paths into paths visible to the host Docker daemon.
  • Uses a shared host-absolute workspace path for isolated cases.
  • Retains the existing isolated-container security and resource settings.

Motivation

Codeman was originally designed to run directly on a host where Codeman and the Docker daemon shared the same filesystem namespace.

Not everyone want's to run applications directly on their desktop and docker is a perfect way to isolate systems. It also allows end-users to run linux applications on Windows using WSL2.

Docker containers also run as a system level so you don't even have to log into your desktop in order to use CM because docker is running as a service.

Future development of CM can also be adapted to spin up named containers based on the branch name to test multiple feature and bugfix additons with AI. Simply ask your AI to spin up a new instance on a new branch with a prefix.

Docker build vs published Image

This setup is building the docker image manually, there's no requirement for actions/pipelines to create an image and publish it to dockerhub (although this can be added at any point in time).

Creating the CM container this way at this point of CM's development allows for a simple git pull and then re-rerun the Start-Container.sh script to rebuild the image with the latest version or use CM's built-ni self update tool.

CodeMan can also self-update within the container and just requires a restart to use the latest version. A future idea would be to add in a self-terminate and restart capability to the container doesn't even need to be restarted.

Application Data

Codemans' user folder is passed mapped through to a docker host volume or folder, preserving all history during rebuilds or updates.

User running context

Running Codeman as root was not a suitable workaround because some agent CLIs, particularly Claude Code, refuse or fail to operate correctly as root. A dedicated user is created during the container build, username is set as per the .env file.

Isolated container support

The Compose deployment uses Docker-outside-of-Docker:

  1. Codeman connects to the host Docker daemon through /var/run/docker.sock.
  2. Codeman creates each isolated case as a sibling container on that daemon.
  3. CODEMAN_CASES_PATH provides a workspace path that is identical inside Codeman and on the Docker host.
  4. CODEMAN_DOCKER_HOST_HOME translates generated credential, seed, transcript, and hook-secret paths from Codeman's container filesystem into host-visible paths.
  5. The isolated container is then started and accessed using the existing Docker session implementation.

Docker itself is not run as a daemon inside the Codeman container. Only the Docker CLI is installed in the image.

Direct host installations continue using the existing behaviour because path translation is enabled only when CODEMAN_DOCKER_HOST_HOME is configured.

Changes

  • docker/server.Dockerfile
  • docker/.env.example
  • docker/Start-Codeman.sh
  • Run Codeman and local agent sessions as an unprivileged account, defaulting to opencode.
  • Made the runtime username configurable through CODEMAN_RUNTIME_USER.
  • Detect the application-data directory's PUID and PGID in the Linux start script.
  • Create a matching image account and group when the requested numeric IDs do not exist.
  • Detect the Docker socket group ID so the unprivileged account can access the host daemon.
  • Added CODEMAN_CASES_PATH for host-visible isolated workspaces.
  • Added CODEMAN_DOCKER_HOST_HOME translation for:
    • Agent credentials
    • Docker seed files
    • Claude transcripts
    • Hook configuration and secrets
  • Added optional CODEMAN_DOCKER_DISABLE_SWAP_LIMIT compatibility for hosts without swap accounting:
    • Retains the configured memory limit.
    • Omits the unsupported --memory-swap option.
    • Filters only Docker's exact swap-capability warning.
    • Preserves all other Docker errors and exit statuses.
  • Handled concurrent attempts to create the same isolated case container without hiding genuine creation failures.
  • Prevented saved Claude conversation IDs from being passed to Codex, Gemini, or other agent CLIs.
  • Added deployment, storage, networking, and migration documentation.
  • Added regression tests for the Compose-specific Docker behaviour.

Security

  • The Codeman application container does not run as root.
  • Linux capabilities are dropped.
  • no-new-privileges is enabled.
  • Agent credentials remain in the configured host bind mount and are not baked into the image.
  • Isolated case containers retain the existing security, process, CPU, and memory restrictions.
  • Docker socket access is highly privileged by nature. Anyone with administrative access to Codeman or its runtime account should be considered capable of controlling the connected Docker daemon.
  • The password in .env.example must be replaced before exposing Codeman to a network.

Backwards compatibility

Direct host installations retain their existing behaviour when the new Compose-specific environment variables are absent.

Existing isolated Docker cases continue using the normal host-native path handling outside Compose deployments.

Swap limiting retains its current behaviour unless CODEMAN_DOCKER_DISABLE_SWAP_LIMIT=1 is explicitly configured.

Validation

The complete validation suite passed in a clean Linux container with Node.js 22 and tmux installed:

Test Files  316 passed | 1 skipped (317)
Tests       6192 passed | 12 skipped (6204)

The following checks passed:

npm run typecheck
npm run lint
npm run format:check
npm run check:frontend-syntax
npm test
bash -n docker/Start-Codeman.sh
docker compose --env-file docker/.env.example \
  -f docker/docker-compose.yaml config --quiet

The server image was built and validated with multiple host identity combinations:

PUID=1000 PGID=12345
PUID=99 PGID=100

Manual end-to-end validation was performed on an Unraid Docker host, including:

  • Starting Codeman as the unprivileged opencode account.
  • Creating new isolated case containers through the host Docker socket.
  • Mounting case workspaces, credentials, and seed files successfully.
  • Starting and reconnecting agent sessions inside existing isolated containers.
  • Running Claude, Codex, and Gemini without incorrectly sharing Claude resume identifiers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant