Remote SSH to (DevPod) devcontainer fails

Resolved 💬 2 comments Opened May 15, 2026 by heyarny Closed May 23, 2026

What version of the Codex App are you using (From “About Codex” dialog)?

26.513.20950

What subscription do you have?

Pro

What platform is your computer?

Darwin 25.4.0 arm64 arm

What issue are you seeing?

Codex App Remote SSH fails against a (DevPod) devcontainer with:

SSH codex app - server websocket closed

Local app:

Codex App 26.513.20950
Embedded CLI: codex-cli 0.131.0-alpha.9

Remote devcontainer:

codex-cli 0.131.0-alpha.19
Linux 6.12.76-linuxkit aarch64
SSH host: <devpod-host>.devpod

Direct SSH can find Codex:

ssh <devpod-host>.devpod 'command -v codex && codex --version'
/usr/local/bin/codex
codex-cli 0.131.0-alpha.19

But daemon startup fails:

ssh <devpod-host>.devpod 'codex app-server daemon start'

Error: app server did not become ready on /home/vscode/.codex/app-server-control/app-server-control.sock
Caused by:
  No such file or directory (os error 2)

What steps can reproduce the bug?

Codex App -> Settings -> Connections -> SSH -> DevContainer

What is the expected behavior?

Additional information

_No response_

View original on GitHub ↗

2 Comments

heyarny · 3 months ago

I could find a workaround for the issue.
It seems to be related to https://github.com/openai/codex/issues/20617 .
So I wrote this helper script to fix the start up of the app server.

devcontainer.json

...
"postStartCommand": "/workspace/.devcontainer/scripts/start-codex-app-server.sh"
...
#!/usr/bin/env bash
set -euo pipefail

if ! command -v codex >/dev/null 2>&1; then
    echo "codex not found; skipping app-server startup"
    exit 0
fi

cd /workspace

codex_home="${CODEX_HOME:-$HOME/.codex}"
control_dir="${codex_home}/app-server-control"
standalone_codex_path="${codex_home}/packages/standalone/current/codex"
runtime_control_dir="${CODEX_APP_SERVER_RUNTIME_DIR:-/tmp/codex-app-server-control}"
socket_path="${control_dir}/app-server-control.sock"
runtime_socket_path="${runtime_control_dir}/app-server-control.sock"
log_path="${control_dir}/app-server.log"
listen_url="unix://${runtime_socket_path}"
codex_bin="$(command -v codex)"

find_running_pids() {
    if command -v pgrep >/dev/null 2>&1; then
        pgrep -u "$(id -u)" -f "codex app-server --listen ${listen_url}" || true
    fi
}

stop_running_server() {
    local pids
    pids="$(find_running_pids)"
    if [ -n "${pids}" ]; then
        # shellcheck disable=SC2086
        kill ${pids} >/dev/null 2>&1 || true
    fi
}

start_server() {
    if command -v setsid >/dev/null 2>&1; then
        setsid -f codex app-server --listen "${listen_url}" >"${log_path}" 2>&1
    else
        nohup codex app-server --listen "${listen_url}" >"${log_path}" 2>&1 &
    fi
}

wait_for_socket() {
    for _ in $(seq 1 50); do
        if [ -S "${runtime_socket_path}" ]; then
            return 0
        fi
        sleep 0.1
    done
    return 1
}

backup_corrupt_state_db() {
    local timestamp
    timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
    for state_file in "${codex_home}"/state_5.sqlite*; do
        if [ -e "${state_file}" ]; then
            mv "${state_file}" "${state_file}.corrupt-${timestamp}.bak"
        fi
    done
}

ensure_standalone_codex() {
    if [ -x "${standalone_codex_path}" ]; then
        return 0
    fi

    mkdir -p "$(dirname "${standalone_codex_path}")"
    rm -f "${standalone_codex_path}"
    ln -s "${codex_bin}" "${standalone_codex_path}"
}

umask 077
mkdir -p "${control_dir}"
mkdir -p "${runtime_control_dir}"
chmod 700 "${control_dir}"
chmod 700 "${runtime_control_dir}"
ensure_standalone_codex

running_pids="$(find_running_pids)"

socket_link_ok=false
if [ -L "${socket_path}" ] && [ "$(readlink "${socket_path}")" = "${runtime_socket_path}" ]; then
    socket_link_ok=true
fi

if [ -n "${running_pids}" ] && [ -S "${runtime_socket_path}" ]; then
    if [ "${socket_link_ok}" != "true" ]; then
        rm -f "${socket_path}"
        ln -s "${runtime_socket_path}" "${socket_path}"
    fi
    echo "codex app-server is already running"
    exit 0
fi

if [ -n "${running_pids}" ]; then
    echo "stopping stale codex app-server process without a live socket"
    stop_running_server
fi

rm -f "${socket_path}"
rm -f "${runtime_socket_path}"
ln -s "${runtime_socket_path}" "${socket_path}"
start_server

if wait_for_socket; then
    echo "started codex app-server; log: ${log_path}"
    exit 0
fi

if [ -f "${log_path}" ] && grep -q "file is not a database" "${log_path}"; then
    echo "codex state DB is corrupt; backing it up and retrying app-server startup"
    stop_running_server
    rm -f "${runtime_socket_path}"
    backup_corrupt_state_db
    start_server
    if wait_for_socket; then
        echo "started codex app-server after DB repair; log: ${log_path}"
        exit 0
    fi
fi

echo "warning: codex app-server did not create ${runtime_socket_path}; see ${log_path}"
exit 0
heyarny · 3 months ago

Ok, got the mistake.
Codex cli needs full access to its folders. Better not mount the whole .codex folder to the host.
It would be good to know what best practice are for devcontainers. What can we mount and what not..
Right now I'm trying to mount selective folders to the host (sessions, plugins, skills, ..).