부록 C. 환경변수·config.toml 설정 레퍼런스
Codex를 매번 손으로 길들이는 팀은 오래 못 간다. 어떤 모델을 쓸지, 언제 승인받을지, 어디까지 파일을 건드릴지, 어떤 MCP 서버를 붙일지 같은 규칙을 config.toml과 환경변수로 먼저 고정해두면, 에이전트가 훨씬 덜 제멋대로 움직인다. 한 번만 잘 정리해두면 개인 생산성도 오르고, 팀 전체 시행착오도 눈에 띄게 줄어든다.

설정은 어떻게 합쳐지나
Codex는 한 군데 설정만 읽지 않는다. 여러 레이어를 겹쳐 읽고, 더 우선순위가 높은 값이 최종 동작을 결정한다.12
| 우선순위 | 레이어 | 설명 |
|---|---|---|
| 1 | CLI 플래그, -c/--config | 가장 강하다. 한 번 실행할 때만 덮어쓴다. |
| 2 | --profile | 미리 묶어둔 설정 세트를 적용한다. |
| 3 | 프로젝트 .codex/config.toml | 저장소별 규칙. 가까운 디렉터리의 파일이 더 강하다. |
| 4 | 사용자 ~/.codex/config.toml | 개인 기본값. 가장 자주 손대는 파일이다. |
| 5 | 시스템 /etc/codex/config.toml | 공용 머신, 서버, 실습실 같은 환경에서 쓴다. |
| 6 | 내장 기본값 | 아무것도 안 적었을 때의 기본 동작이다. |
중요한 포인트가 하나 더 있다. 프로젝트 수준 설정은 신뢰한 프로젝트에서만 로드된다. 저장소를 처음 열었을 때 신뢰를 거부했다면, .codex/config.toml을 잘 써놔도 조용히 무시될 수 있다.3
파일 위치와 기본 디렉터리
기본 사용자 설정 파일은 보통 ~/.codex/config.toml에 있고, CODEX_HOME을 지정했다면 $CODEX_HOME/config.toml로 위치가 바뀐다.12 프로젝트별 설정은 저장소 안의 .codex/config.toml에 둔다.13
Codex는 설정만 저장하지 않는다. 로그, 인증 정보, 상태 DB도 함께 들고 다닌다. SQLite 기반 상태 DB는 sqlite_home 설정이나 CODEX_SQLITE_HOME 환경변수로 옮길 수 있고, 값을 지정하지 않으면 샌드박스 모드에 따라 임시 디렉터리 또는 CODEX_HOME 아래를 사용한다.4 로그는 보통 ~/.codex/log/codex-tui.log에서 먼저 확인하게 된다.5
가장 무난한 시작점은 아래 정도다.
# ~/.codex/config.toml
model = "gpt-5-codex"
model_provider = "openai"
approval_policy = "on-failure"
sandbox_mode = "workspace-write"
[profiles.review]
sandbox_mode = "read-only"
approval_policy = "never"
[profiles.auto]
sandbox_mode = "danger-full-access"
approval_policy = "never"이 정도만 있어도 평소 개발, 읽기 전용 리뷰, 강한 자동화 작업을 꽤 깔끔하게 분리할 수 있다.
CLI에서 한 번만 덮어쓰기
설정 파일을 고치지 않고 이번 실행에만 값을 바꾸고 싶다면 -c 또는 전용 플래그를 쓰면 된다. 이 값이 최우선이다.2
# 모델만 바꿔서 한 번 실행
codex --model o3 "이 쿼리 최적화해줘"
# 중첩 키 덮어쓰기
codex -c model_reasoning_effort="high" \
-c model_reasoning_summary="detailed" \
"이 테스트가 왜 불안정한지 분석해줘"
# 프로필 위에 일부 값만 추가 덮어쓰기
codex --profile review -c approval_policy="on-request" "변경점 리뷰해줘"여기서 많이 헷갈리는 부분이 있다. -c 값은 JSON이 아니라 TOML 문법으로 해석된다. 그래서 배열이나 객체를 넘길 때도 TOML 스타일로 적는 편이 안전하다.2
codex -c shell_environment_policy.include_only='["PATH", "HOME", "NODE_ENV"]' "테스트 실행해줘"쉘 따옴표가 한 겹 벗겨진다는 점도 꼭 기억하자. 겉보기엔 사소하지만, 이 한 줄 때문에 설정이 문자열인지 불리언인지 달라지는 일이 잦다.2
인증과 provider 설정
Codex는 크게 두 가지 방식으로 인증해 쓴다. 하나는 ChatGPT 계정 로그인이고, 다른 하나는 API/provider 기반 연결이다. 공식 Quickstart도 ChatGPT 계정 로그인을 가장 자연스러운 시작점으로 안내하고, 필요하면 API 키 기반 설정도 가능하다고 설명한다.6
실무에서는 이렇게 나누면 편하다.
| 방식 | 언제 쓰면 좋은가 | 핵심 포인트 |
|---|---|---|
| ChatGPT 로그인 | 개인 개발, 빠른 시작, Plus/Pro/Business 중심 | 가장 진입장벽이 낮다. |
| API/provider | 사내 게이트웨이, Azure, 지역 라우팅, 로컬 모델 | model_provider와 env_key를 정확히 잡아야 한다. |
provider를 직접 설정할 때 핵심 축은 세 개다. model, model_provider, 그리고 [model_providers.<id>] 블록이다.12
| 키 | 의미 | 실무 팁 |
|---|---|---|
model | 사용할 모델 ID | provider를 바꾸면 보통 함께 바꾼다. |
model_provider | 어느 provider 정의를 쓸지 | 기본값은 보통 openai다. |
base_url | API 엔드포인트 | Azure, 사내 프록시, 로컬 런타임 연결에 중요하다. |
env_key | API 키를 읽을 환경변수 이름 | 키를 config에 박아 넣지 말고 환경변수로 뺀다. |
wire_api | chat 또는 responses | provider가 기대하는 프로토콜과 맞아야 한다. |
http_headers, env_http_headers | 추가 헤더 | 사내 게이트웨이나 기능 토글 헤더에 유용하다. |
query_params | URL 쿼리 파라미터 | Azure의 api-version 같은 경우에 필요하다. |
request_max_retries, stream_max_retries, stream_idle_timeout_ms | 네트워크 재시도와 유휴 타임아웃 | 스트리밍이 자주 끊길 때 조정한다. |
예를 들어 Azure는 쿼리 파라미터로 api-version을 넘겨야 하고, wire_api = "responses"를 쓰는 구성이 대표적이다.2
model = "gpt-5-codex"
model_provider = "azure"
[model_providers.azure]
name = "Azure"
base_url = "https://YOUR_PROJECT_NAME.openai.azure.com/openai"
env_key = "AZURE_OPENAI_API_KEY"
query_params = { api-version = "2025-04-01-preview" }
wire_api = "responses"
request_max_retries = 4
stream_max_retries = 10
stream_idle_timeout_ms = 300000로컬 모델을 붙일 때는 대체로 chat 계열 wire API를 쓰게 된다.2
model = "mistral"
model_provider = "ollama"
[model_providers.ollama]
name = "Ollama"
base_url = "http://localhost:11434/v1"
wire_api = "chat"한 줄 요약을 하면 이렇다. 모델이 안 뜨면 model을 보고, 인증이 안 되면 env_key를 보고, 응답 형식이 이상하면 wire_api를 의심하라.
모델, 추론, 프로필 설정
모델 관련 키는 단순히 "무슨 모델을 쓸까"에서 끝나지 않는다. 얼마나 깊게 생각할지, 요약을 얼마나 보여줄지, 답변을 얼마나 길게 낼지도 함께 조절할 수 있다.24
| 키 | 대표값 | 의미 |
|---|---|---|
model | 예: gpt-5-codex, o3 | 사용할 모델 ID |
model_reasoning_effort | minimal, low, medium, high | 생각 깊이 |
model_reasoning_summary | auto, concise, detailed, none | 추론 요약 표시 수준 |
model_verbosity | low, medium, high | 출력 설명량 |
plan_mode_reasoning_effort | low~high, none | Plan 모드 전용 추론 강도 |
profile | 임의 이름 | 기본 프로필 지정 |
실무 감각으로 보면 이렇게 쓰면 편하다.
| 상황 | 추천 설정 |
|---|---|
| 빠른 수정, 타입 에러, 사소한 리팩터링 | model_reasoning_effort = "low", model_verbosity = "low" |
| 복잡한 버그, 아키텍처 검토 | model_reasoning_effort = "high", model_reasoning_summary = "detailed" |
| CI나 배치 자동화 | model_verbosity = "low", 필요하면 model_reasoning_summary = "none" |
프로필을 쓰면 훨씬 덜 피곤해진다.
model = "gpt-5-codex"
approval_policy = "on-failure"
sandbox_mode = "workspace-write"
profile = "dev"
[profiles.dev]
model_reasoning_effort = "high"
model_verbosity = "medium"
[profiles.quick]
model_reasoning_effort = "low"
model_verbosity = "low"
approval_policy = "never"
[profiles.review]
sandbox_mode = "read-only"
approval_policy = "never"
model_reasoning_effort = "high"이렇게 해두면 codex --profile quick, codex --profile review만으로도 분위기가 완전히 달라진다.
승인과 sandbox 설정
Codex에서 가장 자주 사고를 내는 부분이 바로 승인과 샌드박스다. 둘은 같은 게 아니다. sandbox는 기술적 경계이고, approval policy는 언제 멈춰서 물어볼지를 정하는 규칙이다.7
approval_policy는 다음 네 가지를 중심으로 생각하면 된다.2
| 값 | 동작 | 추천 상황 |
|---|---|---|
untrusted | 신뢰된 명령이 아니면 물어본다 | 낯선 저장소, 처음 보는 코드베이스 |
on-failure | 샌드박스에서 실패하면 승격 실행 여부를 묻는다 | 일상적인 개발 기본값 |
on-request | 모델이 필요하다고 판단할 때 승격을 요청한다 | 균형형 |
never | 묻지 않는다 | CI, 격리된 컨테이너, 강한 자동화 |
sandbox_mode는 보통 이렇게 고른다.7
| 값 | 의미 | 추천 상황 |
|---|---|---|
read-only | 읽기 중심 | 구조 파악, 코드 리뷰 |
workspace-write | 작업 디렉터리 안에서 수정 | 일상 개발 기본값 |
danger-full-access | 시스템 전반에 강한 권한 | 컨테이너, 임시 VM, 자동화 전용 |
네트워크는 별도 축으로 보는 게 좋다. workspace-write라고 해서 항상 인터넷이 되는 건 아니다. 패키지 설치, 원격 문서 조회, 외부 API 호출이 필요하면 네트워크를 따로 열어주는 구성이 흔하다.8
sandbox_mode = "workspace-write"
approval_policy = "on-failure"
[sandbox_workspace_write]
network_access = true이 설정은 npm install, pnpm install, pip install, 원격 문서 접근 같은 작업이 들어갈 때 특히 유용하다. 반대로 보안이 중요한 프로젝트라면 기본은 닫아두고 필요할 때만 CLI로 잠깐 여는 편이 더 안전하다.
MCP 설정
MCP 서버 설정도 같은 config.toml 안에 들어간다. 기본 위치는 ~/.codex/config.toml이고, 신뢰한 프로젝트에서는 .codex/config.toml로 프로젝트 범위 설정도 가능하다. CLI와 IDE 확장은 이 설정을 함께 쓴다.9
먼저 큰 그림부터 보자.
| 방식 | 핵심 키 | 언제 유리한가 |
|---|---|---|
| STDIO 서버 | command, args, env, cwd | 로컬 도구, 파일시스템 접근, 사내 래퍼 |
| HTTP 서버 | url, bearer_token_env_var, http_headers | SaaS형 MCP, 원격 호스팅 서버 |
실무에서 자주 보는 MCP 공통 키는 아래 정도다.10
| 키 | 의미 |
|---|---|
command | STDIO 서버 실행 명령 |
args | 실행 인자 배열 |
env | 서버 프로세스에 넘길 환경변수 |
cwd | 서버 작업 디렉터리 |
url | HTTP MCP 서버 주소 |
bearer_token_env_var | HTTP 서버 인증 토큰을 읽을 환경변수 |
startup_timeout_sec | 서버 기동 대기 시간 |
tool_timeout_sec | 각 도구 호출 제한 시간 |
enabled | 서버 활성화 여부 |
enabled_tools, disabled_tools | 특정 도구만 허용 또는 차단 |
예시는 이렇게 시작하면 무난하다.
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
startup_timeout_sec = 30
tool_timeout_sec = 60
enabled = true
[mcp_servers.sentry]
url = "https://mcp.sentry.dev/sse"
bearer_token_env_var = "SENTRY_AUTH_TOKEN"
startup_timeout_sec = 30
tool_timeout_sec = 60
enabled = true병렬 호출이 안전한 MCP 서버라면 서버 단위로 병렬 도구 호출을 허용할 수도 있다. 다만 같은 파일, DB, 외부 리소스를 동시에 읽고 쓰는 도구라면 레이스 컨디션이 날 수 있으니 조심해야 한다.4
[mcp_servers.docs]
command = "docs-server"
supports_parallel_tool_calls = true
default_tools_approval_mode = "approve"
[mcp_servers.docs.tools.search]
approval_mode = "prompt"이런 식으로 두면 "서버 전체 기본 승인 규칙"과 "특정 도구 예외"를 나눠서 관리할 수 있다.4
shell 환경변수 전달 정책
Codex가 실행하는 하위 프로세스에 어떤 환경변수를 넘길지도 신중하게 정해야 한다. 여기를 대충 열어두면, 테스트 하나 돌리다가 민감한 비밀값이 불필요하게 전달될 수 있다. 실무에서는 inherit = "core"로 최소한만 물려주고, 필요한 값만 include_only로 화이트리스트하는 방식이 가장 다루기 쉽다.23
[shell_environment_policy]
inherit = "core"
include_only = ["PATH", "HOME", "NODE_ENV", "DATABASE_URL"]감각적으로 이해하면 이렇다. PATH, HOME 같은 생존 필수품은 챙겨주고, AWS 키나 결제 토큰 같은 건 괜히 따라가지 않게 막는 것이다. 에이전트에게 도시락은 주되, 회사 금고 열쇠까지 쥐여주지는 말자는 이야기다.
상태 저장, 로그, 인증서
설정을 오래 만지다 보면 결국 부딪히는 것이 저장 경로와 로그다. 여기서 기억할 값은 네 가지다.45
| 항목 | 어디서 제어하나 | 용도 |
|---|---|---|
CODEX_HOME | 환경변수 | Codex 홈 디렉터리 이동 |
sqlite_home | config 키 | 상태 DB 저장 위치 변경 |
CODEX_SQLITE_HOME | 환경변수 | 상태 DB 저장 위치 변경 |
CODEX_CA_CERTIFICATE | 환경변수 | 커스텀 루트 CA 번들 지정 |
사내 프록시나 TLS 가로채기 장비가 있는 환경에서는 CODEX_CA_CERTIFICATE가 의외로 중요하다. 이 값이 있으면 지정한 PEM 파일을 신뢰하고, 없으면 SSL_CERT_FILE, 그것도 없으면 시스템 루트 인증서를 사용한다. 빈 파일이거나 읽을 수 없는 PEM이면 외부 연결이 이상하게 실패할 수 있다.4
로그를 볼 때는 먼저 ~/.codex/log/codex-tui.log를 확인하면 된다. 세션 중에는 /feedback로 Request ID, Session ID, 마지막 오류 상태를 바로 확인할 수 있어서, 재현 버그를 잡거나 이슈를 올릴 때 꽤 쓸모가 있다.5
[notice] 테이블에는 "다시 보지 않기"로 처리한 UI 알림 플래그가 저장된다. 별것 아닌 것 같지만, 팀원 한 명은 자꾸 경고가 뜨고 다른 한 명은 안 뜨는 미묘한 차이가 여기서 생기기도 한다.4
환경변수 빠른 레퍼런스
아래 표만 기억해도 절반은 먹고 들어간다.
| 환경변수 | 의미 | 자주 쓰는 상황 |
|---|---|---|
CODEX_HOME | Codex 사용자 홈 이동 | 설정, 로그, 인증 정보 위치를 바꾸고 싶을 때 |
CODEX_SQLITE_HOME | 상태 DB 위치 지정 | 로컬 디스크 정책, 임시 디렉터리 분리 |
CODEX_CA_CERTIFICATE | Codex 전용 CA 번들 경로 | 사내 프록시, TLS 검사 장비 |
SSL_CERT_FILE | 일반 SSL CA 번들 경로 | CODEX_CA_CERTIFICATE가 없을 때 대체 |
OPENAI_BASE_URL | 내장 openai provider의 base URL 재정의 | 게이트웨이, 프록시, 지역 라우팅 |
OPENAI_API_KEY 같은 provider 키 | provider 인증용 API 키 | env_key가 이 변수를 읽도록 설정했을 때 |
AZURE_OPENAI_API_KEY | Azure OpenAI 인증 | Azure provider 사용 시 |
MISTRAL_API_KEY 등 | 타 provider 인증 | 커스텀 provider 사용 시 |
SENTRY_AUTH_TOKEN 같은 MCP 토큰 | HTTP MCP 서버 인증 | bearer_token_env_var를 쓸 때 |
핵심 규칙은 단순하다. 비밀은 config 파일보다 환경변수 쪽에 두는 편이 낫다. 특히 팀 저장소에 .codex/config.toml을 커밋할 때는 더 그렇다.12410
실전 템플릿 세 가지
1) 개인 개발용 기본 템플릿
가장 무난한 출발점이다. 한국 개발자 기준으로는 Next.js나 Express 프로젝트를 일상적으로 돌릴 때 딱 좋은 균형이다.
model = "gpt-5-codex"
model_provider = "openai"
approval_policy = "on-failure"
sandbox_mode = "workspace-write"
profile = "dev"
[sandbox_workspace_write]
network_access = true
[profiles.dev]
model_reasoning_effort = "high"
model_verbosity = "medium"
[profiles.review]
sandbox_mode = "read-only"
approval_policy = "never"
model_reasoning_effort = "high"2) 팀 저장소 공용 템플릿
개인 취향보다 팀 규칙을 먼저 맞추고 싶다면 저장소에 .codex/config.toml을 둔다. 이 파일은 신뢰한 프로젝트에서만 적용된다는 점을 잊지 말자.39
approval_policy = "on-failure"
sandbox_mode = "workspace-write"
model_reasoning_effort = "high"
[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
enabled = true이 템플릿은 팀이 같은 문서 검색 도구를 쓰고, 비슷한 보안 수준에서 개발할 때 효과가 좋다.
3) CI·컨테이너 자동화 템플릿
이건 로컬 맥북이 아니라 격리된 실행 환경에서만 권장한다. 속도는 빠르지만, 잘못 쓰면 에이전트가 너무 신나 버린다.
approval_policy = "never"
sandbox_mode = "danger-full-access"
model_verbosity = "low"
model_reasoning_summary = "none"이 구성은 사람이 버튼을 눌러주지 않아도 끝까지 달릴 수 있게 해준다. 대신 PR 리뷰, CI 체크, 격리된 실행기 같은 바깥쪽 안전장치가 꼭 필요하다.
설정이 안 먹힐 때 체크리스트
설정 문제는 대부분 "문법"보다 "적용 레이어"에서 터진다. 아래 순서대로 보면 빨리 잡힌다.23510
| 증상 | 먼저 볼 것 | 흔한 원인 |
|---|---|---|
| 값 바꿨는데 반응이 없다 | CLI 플래그, --profile, 프로젝트 신뢰 여부 | 더 높은 우선순위 값이 덮어씀 |
| 모델 호출이 실패한다 | model, model_provider, env_key | 모델 ID 오타, 키 누락 |
| 연결은 되는데 응답 형식이 이상하다 | wire_api | chat/responses 불일치 |
| MCP 서버가 안 뜬다 | command, args, startup_timeout_sec | npx 경로 문제, 기동 시간 초과 |
| 스트리밍이 자주 끊긴다 | provider 재시도 설정 | stream_idle_timeout_ms가 너무 짧음 |
| 사내망에서만 SSL 오류가 난다 | CODEX_CA_CERTIFICATE, SSL_CERT_FILE | 커스텀 CA 미설정 |
| 프로젝트 설정이 무시된다 | .codex/config.toml, trust 상태 | 저장소를 신뢰하지 않음 |
특히 "설정은 맞는 것 같은데 자꾸 무시된다"는 문제는 거의 항상 우선순위 싸움이다. Codex가 말을 안 듣는 게 아니라, 위에서 누가 더 크게 말하고 있는 경우가 많다.
정리
config.toml은 Codex의 성격을 정하는 파일이고, 환경변수는 그 성격이 실제 인프라와 만나는 접점이다. 모델과 provider는 뇌를 정하고, approval과 sandbox는 손발 범위를 정하고, MCP는 외부 세계와의 연결 고리를 정한다. 여기에 로그와 저장 경로까지 정리해두면, 개인용 장난감 같던 에이전트가 비로소 팀의 작업 시스템으로 바뀐다.
처음부터 모든 키를 외울 필요는 없다. 다만 다음 네 줄은 꼭 기억하자. 우선순위를 먼저 보고, 비밀은 환경변수로 빼고, 자동화는 프로필로 묶고, 위험한 권한은 격리된 환경에서만 켠다. 이 네 가지만 지켜도 Codex는 꽤 믿을 만한 동료가 된다.
참고
1Codex Configuration Reference
3Configuration | Developer Toolkit
4codex/docs/config.md at main · openai/codex · GitHub
5Codex CLI Logs: Location, Debug Flags & 401 Error Fix (2026)
6GitHub - openai/codex: Lightweight coding agent that runs in your terminal
7Sandbox - Codex | OpenAI Developers
이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.