The short version
- codex exec runs Codex without the interactive screen and defaults to a read-only sandbox, so a CI job cannot change files unless you grant it.
- In GitHub Actions, the docs recommend the openai/codex-action over installing and signing in the CLI yourself. Set its sandbox or permission profile explicitly: with neither, the action uses the legacy workspace-write sandbox.
- Never set the API key as a job-level environment variable in workflows that run repository-controlled code; pass it only to the step that needs it.
- Split a pull request review into two jobs: one that runs Codex with only contents: read, and one that holds the write permissions and posts the comment.
- Keep the default write-access trigger rule, and list any extra users or bots explicitly; the wildcard is not supported for bots.
On this page
To run Codex in CI, use codex exec, the non-interactive command. On GitHub, the docs recommend the openai/codex-action wrapper around it. The CLI starts in a read-only sandbox. The action does not: if you set neither sandbox nor permission-profile, it runs Codex with the legacy workspace-write sandbox. So choose the sandbox yourself, split reading from writing, and keep the API key out of any place that repository code can read.
This guide covers the commands, the action's inputs, the safety settings that matter, and a pull request review workflow you can adapt.
What does codex exec do?
codex exec runs Codex once, with no interactive screen, so scripts and CI jobs can call it. Progress streams to stderr, and only the final agent message goes to stdout, which makes it easy to pipe or redirect.
codex exec "summarize the repository structure and list the top 5 risky areas"
codex exec "generate release notes for the last 10 commits" | tee release-notes.mdCodex expects to run inside a Git repository. --skip-git-repo-check overrides that requirement; use it only when you are sure the environment is safe. --ephemeral avoids saving session rollout files to disk.
Sandbox and approvals in non-interactive mode
By default, codex exec runs in a read-only sandbox. You widen it explicitly:
codex exec --sandbox workspace-write "<task>"
codex exec --sandbox danger-full-access "<task>" # only in a controlled environment
codex exec --approve-for-me "<task>" # routes approval requests through automatic reviewTwo details matter if you have older scripts. --full-auto is no longer supported; replace it with --sandbox workspace-write. And --ignore-user-config skips $CODEX_HOME/config.toml, while --ignore-rules skips user and project execpolicy .rules files. Both help when you want a CI run that does not depend on whoever configured the runner.
If an enabled MCP server has required = true and fails to start, codex exec exits with an error rather than carrying on without it.
How do I get machine-readable output?
Three options, depending on what the next step in your pipeline needs.
JSON Lines. --json turns stdout into an event stream. Event types include thread.started, turn.started, turn.completed, turn.failed, item.* and error.
codex exec --json "summarize the repo structure" | jqFinal message to a file. -o (or --output-last-message) writes the last message to a file and still prints it.
codex exec "summarize the open TODOs" -o ./out.mdA fixed shape. --output-schema asks for a final response that conforms to a JSON Schema, which suits a script that reads the result.
codex exec "Extract project metadata" \
--output-schema ./schema.json \
-o ./project-metadata.jsonTo continue earlier work, resume a session:
codex exec "review the change for race conditions"
codex exec resume --last "fix the race conditions you found"
codex exec resume <SESSION_ID> "..."How should I handle the API key in CI?
By default codex exec reuses saved CLI sign-in. For automation, the docs say API keys are the recommended default, and CODEX_API_KEY can be set inline for a single run:
CODEX_API_KEY=<api-key> codex exec --json "triage open bug reports"The rule that matters most: do not set OPENAI_API_KEY or CODEX_API_KEY as a job-level environment variable in workflows that run repository-controlled code. Anything in that job's environment, such as a test script from a pull request, could read it.
The docs also describe ChatGPT-managed sign-in through ~/.codex/auth.json as an advanced option. That file holds access tokens, so treat it like a password: don't commit it or paste it into tickets. The docs say not to use this workflow for public or open-source repositories.
For GitHub Actions, the docs say to use the Codex GitHub Action rather than installing the CLI and authenticating it yourself.
Using the Codex GitHub Action
The action is openai/codex-action@v1. A minimal read-only job, adapted from the documented inputs:
on:
workflow_dispatch:
jobs:
codex:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
with:
persist-credentials: false
- id: run_codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt: "Summarize the open TODOs in this repository."
sandbox: read-only
safety-strategy: drop-sudo
output-file: codex-output.mdThe inputs you will use most:
promptorprompt-file: provide exactly one.prompt-filepoints to a file in the repository, which keeps long instructions reviewable.codex-args: extracodex execflags, as a JSON array or a shell-style string.output-schemaoroutput-schema-file: pass a schema to--output-schema. The two are mutually exclusive.modelandeffort: leave empty for Codex defaults.sandbox: the legacy sandbox mode (workspace-write,read-onlyordanger-full-access). Pick the narrowest that lets the task finish.permission-profile: a built-in or configured profile. It is mutually exclusive withsandbox.output-file: saves the final message to disk.codex-version: the version of@openai/codexto install; blank uses the latest. Pin it for repeatable runs.allow-users,allow-bots,allow-bot-users: who may trigger the workflow.
Safety strategy: what each option does
safety-strategy controls how much the action restricts Codex on the runner:
drop-sudo(default): on Linux and macOS, revokes the default user'ssudoaccess before Codex runs. On Linux it also blocks runner access to root-owned service sockets under/run, including the Docker daemon. These changes can persist on reused self-hosted runners, so use disposable runners and run the action as the last step in the job. On Linux it needs passwordlesssudo,setprivandsetfacl.unprivileged-user: runs Codex as the account named incodex-user. That account must be able to read and write the checkout.read-only: a read-only sandbox that cannot modify files or reach the network directly, but the API key still flows through the proxy, so Codex could read it from process memory. Do not rely on it alone to protect secrets.unsafe: no privilege reduction; Codex runs asrunner, which typically hassudo. It is the only option on Windows, where GitHub-hosted runners lack a supported sandbox.
Who can trigger it
By default, only users with write access to the repository can trigger the action. allow-users adds named GitHub usernames. allow-bots: true lets trusted bots such as github-actions[bot] skip the write-access check, and allow-bot-users names specific bots. The * wildcard is not supported, so list each trusted bot.
A two-job pull request review
The action's README includes a review workflow worth copying, built from two jobs with different permissions.
- A
codexjob with onlycontents: readchecks out the pull request's merge commit (refs/pull/<number>/merge) withpersist-credentials: false, fetches the base branch and the PR head, and runs the action withpermission-profile: ":workspace". The prompt carries the PR number, repository, title and body, and asks Codex to review only the changes the PR introduces. The README notes that the:workspaceprofile does not grant network access. - A
post_feedbackjob runs aftercodex, only when the output is non-empty. It holdsissues: writeandpull-requests: write, and posts the message as a PR comment withactions/github-script.
The review job exposes the action's final-message output as a job output, and the posting job reads it. The point of the split is that the job that reads untrusted pull request text has no write token, and the job with the write token never runs Codex.
Some rules from the README:
- Permission profiles are in beta and need Codex CLI 0.138.0 or later, so do not combine one with an older pinned
codex-version. - Setting both
permission-profileandsandboxfails before Codex starts, andsafety-strategy: read-onlycannot be combined with a profile. codex-home/config.tomland named profile definitions are trusted workflow configuration, so they must not come from an untrusted checkout.
A review prompt that works well keeps scope tight. Store it in a committed file and pass it with prompt-file:
Review only the changes in this pull request.
List correctness bugs, missing tests and security issues, most severe first.
For each, name the file and line. If you find nothing, say so briefly.
Do not suggest style changes.The PR title and body come from outside the repository's trusted code, so treat them as data, not instructions.
A checklist before you turn it on
- Set
sandbox: read-only(or a read-only profile) explicitly, and widen only if the task fails for lack of access. - Keep the API key in GitHub secrets and out of job-level
env. - Run on disposable runners, and keep the action as the last step in the job.
- Pin
codex-versionwhile you tune the prompt. - Restrict triggers: leave
allow-usersempty unless you need it, and list trusted bots by name. - Keep the reading job separate from any job that can write to the repository.
Conclusion
Codex in CI is safer when each layer is narrow: an explicit read-only sandbox, a key passed only to the step that needs it, a reviewer job with no write token, and a short list of who can trigger it. Start with the read-only review, check the comments for a week, and only then give a job write access.
If you want a different approach to checking AI-written code, Atlas is a team of AI engineers built on Claude Code. It reviews every build as it finishes. It does not use Codex, and it is not a Codex CI setup.
Questions people ask
How do I run Codex in CI without a terminal?
Use codex exec, which runs once without the interactive screen and prints only the final message to stdout. On GitHub Actions, the docs recommend the openai/codex-action instead of installing and signing in the CLI yourself.
Can codex exec edit files?
Not by default: it runs in a read-only sandbox. Add --sandbox workspace-write to let it edit files, and use danger-full-access only in a controlled environment. Note that the GitHub Action uses workspace-write if you set no sandbox or profile.
Where should I store the API key for Codex in GitHub Actions?
Store it as a GitHub Actions secret and pass it through the action's openai-api-key input. Do not set OPENAI_API_KEY or CODEX_API_KEY as a job-level environment variable in workflows that run repository-controlled code.
What does the drop-sudo safety strategy do?
It is the action's default. On Linux and macOS it removes the default user's sudo access before Codex runs, and on Linux it also blocks access to root-owned sockets such as Docker's. It can persist on reused self-hosted runners, so use disposable runners and run the action last.
How do I get structured output from codex exec?
Use --json for a JSON Lines event stream, -o to write the final message to a file, or --output-schema with a JSON Schema file to request a response of a fixed shape.
Sources
Researched and drafted with Claude, then checked against the sources above. Atlas is an independent product, not affiliated with Anthropic or OpenAI. Claude and Claude Code are trademarks of Anthropic; Codex is a product of OpenAI.