Skip to content

Codex automates deployment by writing and testing the machinery around a release: the Dockerfile, a CI image smoke test, deploy and rollback scripts, expand/contract migrations, and a risk brief for the approver. The deploy runs in your CI pipeline behind a protected environment. Codex never holds production credentials, and CI evidence, not the agent’s summary, decides whether a release ships.

Your deployment process is a wiki page with 23 manual steps, including “SSH into the server and run these four commands.” The last time someone skipped step 14, production was down for 20 minutes, and the person who knew why was on holiday. This page is for the developer who owns a service’s release path and the tech lead who approves production changes. It assumes you use the Codex CLI or desktop app and already run GitHub Actions; if you do not, start with running Codex in CI/CD.

  • A split of duties that says what Codex writes, which gate proves it, and who signs off.
  • Three copy-paste prompts: container configuration, deploy and rollback scripts, and a rollback-safe migration.
  • A CI smoke job that proves the image builds, starts healthy, runs as non-root, fits a size budget and shuts down cleanly.
  • A production workflow where Codex writes a read-only risk brief and a named human approves the deploy.
  • A weekly automation prompt that catches a rotting pipeline before release day.

Who does what when Codex automates a deploy?

Section titled “Who does what when Codex automates a deploy?”

The rule that keeps this safe: Codex produces artifacts and evidence, and the pipeline runs them. Every row has a check that runs without anyone reading the generated code line by line.

StepCodex doesGate that proves itWho signs off
Container configWrites Dockerfile, compose.yaml, compose.override.yamlCI image smoke job (step 3)Reviewer of the PR
Deploy and rollback scriptsWrites scripts/deploy.sh, scripts/rollback.shshellcheck, --dry-run in CI, a rollback rehearsal in stagingService owner
MigrationsWrites expand/contract migrationsPrevious release’s tests pass against the new schemaReviewer of the PR
Release notesDrafts them from git logA person publishes the draftRelease manager
Production deployWrites a read-only risk brief, nothing elseGitHub environment with required reviewersA named approver
After the deployNothing at run timeSmoke test in deploy.sh, automatic rollback on failureOn-call engineer

Release notes already have a hardened workflow on the Codex CI/CD page; this page does not repeat it.

How do you automate a deployment with Codex, step by step?

Section titled “How do you automate a deployment with Codex, step by step?”
  1. Record the deploy rules in AGENTS.md. Codex reads AGENTS.md before it does any work, so the constraints below reach every thread, cloud task and CI run. Treat them as guidance for the agent. The locks are the environment and the credentials in step 6.

    ## Deployment
    - Production deploys run only from `.github/workflows/deploy.yml`. Never run
    `scripts/deploy.sh` without `--dry-run`.
    - Images are tagged with the full git SHA. Never deploy or push `:latest`.
    - Migrations follow expand/contract: a release may add tables and nullable
    columns; dropping or renaming waits for the release after next.
    - Before you call deployment work done, run `shellcheck scripts/*.sh`,
    `scripts/deploy.sh --dry-run "$(git rev-parse HEAD)"` and the test suite,
    and paste the exit codes.
    - Test database: `postgres://localhost:5432/app_test` (the `postgres` service
    in `compose.yaml`).
  2. Generate the container configuration. Start a thread in the Codex CLI or desktop app, in a worktree if you have other work in flight, and paste the prompt below. It names the constraints that matter in production, so you get a working setup rather than a textbook example.

  3. Prove the image in CI, not in the chat. A thread summary that says “the container starts fine” is a claim. The job below is the evidence, and it runs on every pull request that touches the container. Setting COMPOSE_FILE keeps the development override out of CI.

    .github/workflows/image-smoke.yml
    name: Image smoke test
    on:
    pull_request:
    paths: ['Dockerfile', 'compose*.yaml', 'package*.json', 'src/**']
    permissions:
    contents: read
    jobs:
    smoke:
    runs-on: ubuntu-latest
    env:
    COMPOSE_FILE: compose.yaml
    APP_IMAGE: app:ci
    MAX_IMAGE_MB: 250
    steps:
    - uses: actions/checkout@v7
    with:
    persist-credentials: false
    - name: Build and start the stack
    run: docker compose up --build --wait --wait-timeout 90
    - name: Health endpoint answers
    run: curl -fsS http://localhost:3000/health
    - name: App does not run as root
    run: test "$(docker compose exec -T app id -u)" != "0"
    - name: Image fits the size budget
    run: |
    size_mb=$(( $(docker image inspect "$APP_IMAGE" --format '{{.Size}}') / 1000000 ))
    echo "Image size: ${size_mb} MB"
    test "$size_mb" -le "$MAX_IMAGE_MB"
    - name: Shuts down cleanly on SIGTERM
    run: |
    docker compose stop --timeout 10 app
    code=$(docker inspect --format '{{.State.ExitCode}}' "$(docker compose ps -aq app)")
    echo "Exit code: $code"
    test "$code" = "0"
    - name: Logs on failure
    if: failure()
    run: docker compose logs --no-color

    Anything other than exit code 0 means the SIGTERM handler failed; 137 specifically means Docker had to send SIGKILL after the timeout. When the job fails, paste its log into the Codex thread and ask for a fix; the next push reruns the same checks.

  4. Replace the wiki with deploy and rollback scripts. The prompt asks for a dry-run mode, immutable SHA tags and an automatic rollback, because those three properties are what make a script testable before it touches production.

    The rolling-restart command depends on your platform. Name it in the prompt (kubectl rollout, fly deploy, a load-balancer API) so Codex does not guess. Before the first production run, rehearse rollback.sh in staging and record the result in docs/runbooks/rollback-rehearsals.md (date, SHA, outcome); the rehearsed rollback contract describes what “rehearsed” has to mean.

  5. Make migrations safe to roll back. A rollback script that runs down-migrations can destroy data written by the new release. The safer pattern is expand/contract: the release that adds a column never removes one, so the previous image keeps working on the new schema, and rollback is only an image swap.

    The schema-compat job is the proof that rollback still works: it runs the code you would roll back to against the schema you are about to create. The migration patterns cookbook has more expand/contract recipes.

  6. Gate production outside the model. Create a GitHub environment called production with required reviewers, and store DEPLOY_TOKEN and DATABASE_URL as secrets of that environment only. Codex runs in a separate, read-only job that writes a risk brief to the run summary, so the approver reads a one-screen brief instead of the whole diff. The deploy job never runs Codex.

    .github/workflows/deploy.yml
    name: Deploy
    on:
    push:
    branches: [main]
    permissions:
    contents: read
    concurrency:
    group: production
    cancel-in-progress: false
    jobs:
    risk-brief:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    with:
    fetch-depth: 0
    persist-credentials: false
    - id: codex
    uses: openai/codex-action@v1
    with:
    openai-api-key: ${{ secrets.OPENAI_API_KEY }}
    permission-profile: ":read-only"
    prompt: |
    You are writing a deploy risk brief for a human approver.
    Compare ${{ github.event.before }}..${{ github.sha }} with git log and git diff.
    Output Markdown, at most 25 lines:
    1. Risk: low, medium or high, with one sentence why.
    2. Migrations in this range: file names, and whether each is expand-only.
    3. Changed config, environment variables or feature flags.
    4. What the approver should check in staging before approving.
    Do not speculate beyond what the diff shows.
    - name: Publish the brief to the run summary
    env:
    BRIEF: ${{ steps.codex.outputs.final-message }}
    run: printf '%s\n' "$BRIEF" >> "$GITHUB_STEP_SUMMARY"
    deploy:
    needs: risk-brief
    runs-on: ubuntu-latest
    environment: production
    steps:
    - uses: actions/checkout@v7
    with:
    persist-credentials: false
    # The image for this SHA was built and pushed by your build workflow.
    - name: Deploy
    env:
    DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
    DATABASE_URL: ${{ secrets.DATABASE_URL }}
    run: scripts/deploy.sh "$GITHUB_SHA"

    If github.event.before is all zeros (the first push of a branch) or unreachable after a force-push, the range is invalid; point the prompt at git log -1 or the previous release tag instead.

    The concurrency group stops two pushes from deploying at once, where whichever upload lands last wins. The :read-only permission profile (beta; checked against codex-action v1.12 and Codex CLI 0.157.1, 2026-09-26) is the input openai/codex-action recommends over the legacy sandbox input; the action’s default drop-sudo safety strategy stays on. The Codex CI/CD page explains the action’s other hardening options.

  7. Schedule a weekly deployment-readiness check. Deploy tooling rots between releases: a base image moves, a script stops passing shellcheck, a migration no longer applies to an empty database. Create a Codex automation in the desktop app on a worktree, following the Codex automations guide, and paste this prompt unchanged after you have run it once by hand.

    If the automation’s sandbox cannot reach the Docker daemon or the network, leave the image checks to the CI smoke job; this prompt reads that job’s results through gh instead of rebuilding the image.

How do you know a release is safe without reading every line?

Section titled “How do you know a release is safe without reading every line?”

Nobody on the team has to read the generated Dockerfile or deploy.sh line by line. The release is safe when each of these holds, and each one is visible in CI or the repository:

  • Image smoke job green: the image builds, /health answers, the user is not root, the size is inside the budget, and SIGTERM exits cleanly.
  • Scripts proven before use: shellcheck passes, deploy.sh --dry-run prints the plan, and the rollback rehearsal log is under 30 days old.
  • Schema compatibility green: the previous release’s tests pass against the new schema, so rollback is an image swap.
  • A named approver: the production environment’s required reviewer reads the risk brief, checks staging and approves. The brief is advisory; the approval is the gate.
  • Post-deploy evidence: deploy.sh checks /api/version against the SHA and rolls back on its own, and .deploy/log.jsonl records every attempt.

The service owner signs off on the scripts and the rehearsal; the environment’s reviewer signs off on each release. For the cross-tool version of this gate, see gating production in the deploy stage. For canaries and SLO-triggered rollback on top of it, see progressive delivery for agent-written changes.

What breaks when Codex automates deployment?

Section titled “What breaks when Codex automates deployment?”

Codex reports “all checks passed” but nothing ran. A summary is not evidence. The prompts on this page ask for commands and exit codes; if the thread shows neither, ask Codex to rerun the checks and paste the output. The CI jobs are the source of truth either way.

Docker commands fail inside openai/codex-action. Up to v1.12, the action’s default drop-sudo strategy on Linux removed access to root-owned service sockets under /run, including the Docker daemon (codex-action README, read 2026-09-26). v1.13 (2026-10-05) preserves system-service access under drop-sudo and requires setfacl (the acl package) and ACL support on /run; this page did not test whether that restores Docker on your runner. Build and run images in a normal step or job, then give Codex the logs to read. Do not switch to safety-strategy: unsafe to get Docker back.

The image builds locally but fails in CI. Your local layer cache hides a missing file, or your laptop is ARM and the runner is x86. Keep --build in CI, set platform: linux/amd64 on the app service in compose.yaml (or pass --platform linux/amd64 to docker build) when your production hosts are x86, and ask Codex to explain the first failing layer from the log.

/health is green but the service is not ready. A health route that returns 200 immediately passes every check and then serves errors. The container prompt makes /health depend on PostgreSQL and Redis; if you inherited a shallow check, ask Codex to add readiness checks and return 503 until they pass.

A rollback fails because the schema moved. The new release dropped or renamed a column, and the old image cannot start. Recover by rolling forward with a fix, or by restoring the dropped column in a new migration. Prevent it with the expand/contract prompt and the schema-compat job.

The agent can reach production. Someone put DEPLOY_TOKEN in a Codex cloud environment or in the job that runs openai/codex-action. Remove it, rotate it, and keep it as a secret of the production environment only, where Codex never runs. This is why the gate lives outside the model. When the Financial Times reported (2026-02-20, secondary via Engadget) that Amazon’s Kiro agent chose to “delete and recreate the environment” and caused a 13-hour AWS Cost Explorer outage, Amazon called it user error, “specifically misconfigured access controls”. Either way, access control is the layer that decides what an agent can break.