Post

Containerized codex-cli with a local uncensored LLM

Containerized codex-cli with a local uncensored LLM

Another runbook on how to run coding harness with a local uncensored LLM. the previous was about the same local LLM and opencode . this one differs in that opencode used chat completions, and this codex setup uses llama.cpp through API.

my setup

Ubuntu VM for the dev environment, Debian LXC with an NVIDIA P40 and docker for running the inference. Also would need a Python virtual environment (through conda or venv) to download the model using hf cli.

the model

same models as in opencode setup.

the difference is that we also need the Jinja template for the model.

1
2
3
curl -fL \
  https://huggingface.co/DavidAU/Qwen3.6-27B-Fable-Fusion-711-Uncensored-Heretic-NM-DAU-MTP/raw/main/chat_template-instruct.jinja \
  -o qwen3.6-codex.jinja

replace

1
2
3
4
{%- if message.role == "system" or message.role == "developer" %}
    {%- if not loop.first %}
        {{- raise_exception('System message must be at the beginning.') }}
    {%- endif %}

with

1
2
{%- if message.role == "system" or message.role == "developer" %}
    {# The initial system/developer message was rendered above. #}

and put the file to the models directory

the inference engine

same setup as in opencode setup but need to add some flags to entrypoint.sh:

1
2
--jinja \
--chat-template-file /models/qwen3.6-codex.jinja \

the harness on the dev machine

prepare the files for the image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
cat > models.json <<'EOF'
{
  "models": [
    {
      "slug": "Qwen3.6-27B-Fable-Fus-711-UnHeretic-NM-DAU-NEO-MAX-NEO-MTP-Q4_K_M",
      "display_name": "Qwen3.6 27B MTP via llama.cpp",
      "description": "Local Qwen3.6 coding model served by llama.cpp",
      "default_reasoning_level": "low",
      "supported_reasoning_levels": [
        {
          "effort": "low",
          "description": "Use the model's normal local reasoning behavior"
        }
      ],
      "shell_type": "shell_command",
      "apply_patch_tool_type": "freeform",
      "visibility": "list",
      "supported_in_api": true,
      "priority": 0,
      "base_instructions": "You are a coding agent working in the user's repository. Inspect relevant files before changing them, use the provided tools for shell commands and edits, preserve unrelated changes, and verify your work.",
      "supports_parallel_tool_calls": false,
      "context_window": 102400,
      "max_context_window": 102400,
      "effective_context_window_percent": 90,
      "auto_compact_token_limit": 90000,
      "reasoning_summary_format": "none",
      "default_reasoning_summary": "none",
      "supports_reasoning_summaries": false,
      "supports_reasoning_summary_parameter": false,
      "support_verbosity": false,
      "default_verbosity": null,
      "truncation_policy": {
        "mode": "tokens",
        "limit": 10000
      },
      "input_modalities": ["text"],
      "supports_image_detail_original": false,
      "prefer_websockets": false,
      "experimental_supported_tools": [],
      "supports_search_tool": false,
      "use_responses_lite": false,
      "include_skills_usage_instructions": false,
      "auto_review_model_override": null,
      "tool_mode": null,
      "multi_agent_version": null,
      "availability_nux": null,
      "minimal_client_version": "0.144.0",
      "upgrade": null
    }
  ]
}
EOF


cat > config.toml <<'EOF'
model = "Qwen3.6-27B-Fable-Fus-711-UnHeretic-NM-DAU-NEO-MAX-NEO-MTP-Q4_K_M"
model_provider = "llamacpp_qwen"
model_catalog_json = "/root/.codex/models.json"
model_context_window = 102400
model_auto_compact_token_limit = 90000
model_reasoning_summary = "none"
model_supports_reasoning_summaries = false

# The local llama.cpp service has no Codex hosted-search backend.
web_search = "disabled"
check_for_update_on_startup = false

[feedback]
enabled = false

[model_providers.llamacpp_qwen]
name = "Qwen3.6 via host llama.cpp"
base_url = "http://host.docker.internal:8081/v1"
wire_api = "responses"
requires_openai_auth = false
supports_websockets = false
supports_standalone_web_search = false
stream_idle_timeout_ms = 600000
request_max_retries = 1
stream_max_retries = 1

[projects."/workspace"]
trust_level = "trusted"
EOF


cat > Dockerfile <<'EOF'
FROM node:22-bookworm-slim

ARG CODEX_VERSION=0.147.0-alpha.6.5

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        bash \
        ca-certificates \
        curl \
        git \
        jq \
        less \
        openssh-client \
        python3 \
        ripgrep \
    && npm install --global "@openai/codex@${CODEX_VERSION}" \
    && npm cache clean --force \
    && rm -rf /var/lib/apt/lists/*

ENV HOME=/root \
    CODEX_HOME=/root/.codex \
    TERM=xterm-256color

RUN mkdir -p /root/.codex /workspace \
    && git config --system --add safe.directory /workspace

COPY --chmod=600 config.toml /root/.codex/config.toml
COPY --chmod=644 models.json /root/.codex/models.json

WORKDIR /workspace

# Docker is the outer sandbox. This lets Codex use all tools inside the
# container without trying to create a second sandbox inside it.
ENTRYPOINT ["codex", "--dangerously-bypass-approvals-and-sandbox"]
EOF

build the image

1
2
3
4
docker --context rootless build \
  --build-arg CODEX_VERSION=0.147.0-alpha.6.5 \
  -t localhost/codex-qwen-llamacpp:0.147.0-alpha.6.5 \
  .

verify

1
2
3
4
docker --context rootless run --rm \
  --entrypoint codex \
  localhost/codex-qwen-llamacpp:0.147.0-alpha.6.5 \
  --version

using the agent

add rootless docker the same way as in the opencode setup.

forward the port to the local dev machine as before:

1
ssh -N inference -L 127.0.0.1:8081:localhost:8081

cd into a directory with a project we want to work on with an uncensored model, then run

1
2
3
4
5
6
7
docker --context rootless run --rm -it --init \
  --add-host=host.docker.internal:10.0.2.2 \
  --mount "type=bind,src=$(pwd -P),dst=/workspace" \
  --workdir /workspace \
  --env HOME=/root \
  --env CODEX_HOME=/root/.codex \
  localhost/codex-qwen-llamacpp:0.147.0-alpha.6.5

for a particular project remove -rm and add --name projectname for persistence, and then docker --context rootless start -ai projectname.

This post is licensed under CC BY 4.0 by the author.