Agentic AI AcademyAgentic AI Academy

Cloud Environments & Internet Access

Base images, setup scripts, secrets, and network policy

Intermediate 12 minBuilder
What you'll be able to do
  • Explain what a Codex cloud environment is and walk through the container task lifecycle from checkout to diff
  • Configure the `universal` base image and rely on (or override) automatic dependency installation for npm, pip, and friends
  • Write a setup script and an optional maintenance script, accounting for the separate-session rule where `export` does not persist
  • Apply the secrets-only-during-setup rule and the agent-phase internet policy (allowlist presets and HTTP-method restrictions) correctly
  • Reason about container caching, what invalidates the cache, and why environments are configured per repository before delegating tasks
At a glance

A Codex cloud environment is the reproducible recipe for the container that runs a background task: which base image, which dependencies, which setup script, which secrets, and what the agent is allowed to reach on the network. This lesson shows you how to configure one per repo so cloud tasks build reliably and stay safe — with special attention to the two rules people most often get wrong: secrets live only during setup, and the agent's internet is off by default.

  1. 1The mental model: an environment is a reproducible recipe
  2. 2The base image and automatic dependency install
  3. 3Setup vs. maintenance scripts (and the `export` trap)
  4. 4Secrets: available only during setup, removed before the agent runs
  5. 5Agent-phase internet access: off by default
  6. 6Container caching and the per-repo model

The mental model: an environment is a reproducible recipe

When you delegate a task to Codex cloud, it does not run on your laptop. It spins up a fresh cloud sandbox container — its own isolated machine — does the work there, and hands you back a diff and (optionally) a pull request. A cloud environment is the saved recipe that tells Codex how to build that container every time.

Think of it like a Dockerfile plus a network policy that you configure once and reuse for every task on a repo. Get the recipe right and your tasks are reproducible and safe: the same dependencies install, the same tools are present, and the agent can only reach the network you allow. Get it wrong and tasks fail in confusing ways — a missing compiler, a test suite that can't reach its database, a setup that worked once and then broke.

Environments are configured in Codex settings → Environments, and they are per repository: you set one up for a repo before delegating cloud tasks to it. Here is the lifecycle Codex runs for every task:

  1. Create a container and check out your repo at the chosen branch/commit.
  2. Run your setup script (and an optional maintenance script when resuming a cached container).
  3. Apply internet-access settings — note that setup scripts have internet on by default.
  4. Run the agent loop — Codex edits files and runs terminal commands to validate, repeating until done.
  5. Show results and a file-change diff you can review and turn into a PR.

The rest of this lesson walks each layer of that recipe: the base image, dependencies, scripts, secrets, caching, and the network policy.

Key insight

Why this is its own lesson

The cloud agent's reliability is decided before it writes a line of code — at environment-build time. A task that 'can't find pytest' or 'can't reach the package registry' is almost never a model problem; it's an environment-recipe problem. Mastering this layer is what makes cloud delegation dependable.

The base image and automatic dependency install

Every environment starts from a base image — the pre-built container with an operating system, languages, and tooling already installed. Codex's default is the universal image (published as openai/codex-universal on GitHub), which ships a broad set of pre-installed languages, packages, and developer tools so most repos work with zero extra configuration. If your project pins a specific runtime, you can pin specific versions of Python, Node.js, and so on rather than taking the image defaults.

On top of the image, Codex tries to install your project's dependencies automatically. It detects and runs the common package managers it finds:

EcosystemAuto-detected managers
JavaScript / Nodenpm, yarn, pnpm
Pythonpip, pipenv, poetry

If a lockfile or manifest for one of these is present, Codex installs from it without you writing any script. You only need a custom Bash setup script when auto-install isn't enough — for example, native build dependencies, a private registry, a database to seed, or a monorepo with an unusual layout (covered next).

Because exact image contents and the pre-installed toolchain versions shift over time, treat them as volatile and check the live reference rather than memorizing a list.

Note

Start with auto-install, add scripts only when it breaks

For a typical Node or Python repo with a lockfile, you often need no setup script at all — the universal image plus auto-install just works. Reach for a custom script only when a task fails because something the build needs is missing.

Tip

Volatile: verify the image contents live

The exact languages, versions, and tools in universal change. Check openai/codex-universal and the environments docs rather than hard-coding what you expect to be present.

Setup vs. maintenance scripts (and the `export` trap)

Two script hooks let you customize the container beyond auto-install:

  • Setup script — a custom Bash script that runs when the container is first built. Use it for anything auto-install can't do: installing system libraries, seeding a test database, building native modules, configuring a private registry.
  • Maintenance script (optional) — runs when Codex resumes a cached container (see caching below) instead of rebuilding from scratch. Use it for fast, incremental refresh steps — e.g. pulling the latest branch state or re-seeding ephemeral data — so a resumed container is up to date without paying the full setup cost again.

Now the gotcha that bites almost everyone. The setup script runs in a separate Bash session from the agent. That means a variable you set with export in the setup script does not persist into the agent phase — the agent runs in a different shell and never sees it.

bash
# setup script — this DOES NOT reach the agent phase:
export MY_FLAG=1            # ❌ gone once setup's shell exits

# Instead, persist it so the agent's shell picks it up:
echo 'export MY_FLAG=1' >> ~/.bashrc   # ✅ survives into the agent session

For non-secret configuration, the cleaner path is to set environment variables in the environment settings themselves — those are available for the full task, both setup and agent phases, with no ~/.bashrc trick required.

Watch out

`export` in setup is invisible to the agent

Setup runs in its own shell, so export FOO=bar evaporates before the agent starts. Persist values via ~/.bashrc (or the equivalent profile), or — better for non-secrets — declare them as environment variables in the environment settings, which apply to the whole task.

Secrets: available only during setup, removed before the agent runs

This is the single most important safety rule in the whole lesson, so internalize it: Codex distinguishes plain environment variables from secrets, and they have different lifetimes.

Environment variablesSecrets
StoredPlaintext configExtra encryption at rest
Available during setupYesYes
Available during the agent phaseYes (full task)No — removed first
Use forNon-sensitive flags, paths, feature togglesAPI keys, registry tokens, DB passwords

A secret gets extra encryption and is exposed only to your setup script — it is removed before the agent phase begins. So a secret can authenticate to a private package registry or fetch a private dependency during setup, but by the time the model is reading your code and running commands, that credential is gone.

Why design it this way? Because the agent phase is where untrusted content enters — code, files, and (if you enable it) the web. A model influenced by a prompt-injection payload could try to read and exfiltrate any secret it can see. Stripping secrets before the agent runs removes that prize from the table entirely. Plain (non-secret) environment variables, by contrast, are available for the full task — which is exactly why you must never put a credential in one.

The practical workflow: do all credentialed work in the setup script (install the private package, clone the private repo, write a non-secret derived artifact the agent can use), and let the agent phase run with no live credentials present.

Watch out

Secret vs. env var is a security boundary, not a label

Put a token in a plain environment variable and it stays readable for the entire agent phase — visible to anything that influences the model. Put it in Secrets and it is encrypted, used only by setup, then deleted before the agent starts. When in doubt, it's a secret.

Agent-phase internet access: off by default

The second rule people get wrong: during the agent phase, internet access is OFF by default. Setup scripts keep internet on (so dependency installs work), but once the agent starts editing and validating, it cannot reach the network unless you explicitly turn it on. This is a deliberate safety default — an offline agent can't be steered by malicious web content into exfiltrating data.

Internet access is configured per environment, and when enabled all outbound traffic routes through an HTTP/HTTPS proxy so policy can be enforced. The setting has three states:

  • Off — block all outbound traffic in the agent phase (the default).
  • On (unrestricted) — allow everything (use sparingly).
  • On (restricted) — allow, but constrained by two independent controls below.

When restricted, you tighten access along two axes:

ControlOptions
Domain allowlistNone (empty — you add domains manually) · Common dependencies (a preset of ~80+ popular dev domains: npmjs.com, pypi.org, rubygems.org, ghcr.io, github.com, gitlab.com, major cloud providers, etc.) · All (unrestricted)
Allowed HTTP methodsOptionally restrict to GET, HEAD, OPTIONS only — which blocks POST/PUT/PATCH/DELETE

The HTTP-method restriction is a clever, underused control: limiting the agent to read-style methods (GET/HEAD/OPTIONS) lets it fetch documentation or packages while preventing it from writing data to an external service — a strong brake on exfiltration even when a domain is allowed.

The documented risks of opening internet access are concrete: prompt injection from untrusted web content, credential exfiltration, malware, and license-compliance problems. The standing recommendation is to restrict to trusted domains, prefer the read-only method set when you can, and review the agent's output.

The allowlist preset size (~80+ domains) and exact membership are volatile — verify the current list against the live internet-access docs.

Watch out

The lethal-trifecta default

An agent with private data + untrusted input + a way to send data out is the classic exfiltration setup. Off-by-default internet removes the 'send data out' leg for the agent phase. Re-enabling it — especially unrestricted, with write methods allowed — re-arms that risk. Open only the domains you need, and prefer GET/HEAD/OPTIONS.

Container caching and the per-repo model

Building a container from scratch — image + dependency install + setup script — is slow. To keep delegated tasks fast, Codex caches container state for up to 12 hours. When a new task lands within that window, Codex can resume the cached container (running your optional maintenance script to refresh it) instead of rebuilding everything.

The cache is not magic; it invalidates when the recipe meaningfully changes. The cache is dropped when you change any of:

  • the setup script or the maintenance script,
  • the environment variables, or
  • the secrets.

You can also manually reset the cache from the environment page when you want a guaranteed-clean rebuild (for example, after you suspect stale state). The 12-hour TTL is volatile — confirm the current value in the environments docs.

Finally, zoom back out to placement. Environments are per repository, configured before you delegate. That ordering matters: a cloud task with no environment set up (or a broken one) is exactly how you get the 'missing dependency / can't reach the network' failures from the start of this lesson. Configure the environment first, validate it once with a small task, and then delegate real work against a recipe you trust.

Tip

Edit the recipe → expect a rebuild

Changing your setup script, maintenance script, env vars, or secrets invalidates the cache, so the next task rebuilds from scratch (slower, but clean). Use the maintenance script for cheap incremental refresh, and the manual reset when you want to force a clean container.

Try it: Configure a safe, reproducible cloud environment for one repo

Goal: build a cloud environment recipe you trust, and feel the secrets and internet rules firsthand. 1) Pick a repo with a lockfile (a Node repo with package-lock.json/pnpm-lock.yaml or a Python repo with requirements.txt/poetry.lock). In Codex settings → Environments, create an environment for it. 2) Lean on auto-install first. Don't write a setup script yet. Delegate a tiny cloud task like "list the installed dependency versions and write them to deps.txt" and confirm the universal image plus auto-install picked up your packages. 3) Prove the export trap. Add a setup script with export LAB_FLAG=hello, then ask a task to run echo "$LAB_FLAG". Watch it come back empty. Fix it two ways and re-test: (a) echo 'export LAB_FLAG=hello' >> ~/.bashrc in setup, and (b) declaring LAB_FLAG as an environment variable in settings. 4) Exercise secrets. Add a dummy value as a Secret and a second as a plain environment variable. Ask one task to echo each during the agent phase. Confirm the env var prints and the secret does not — that's the setup-only boundary in action. 5) Toggle internet. With the agent phase at its default (off), ask the agent to curl https://example.com and watch it fail. Then enable internet restricted: set the allowlist to Common dependencies and methods to GET/HEAD/OPTIONS, and confirm a GET to an allowed domain succeeds while a POST is blocked. 6) Touch the cache. Edit your setup script, note that the next task rebuilds (cache invalidated), and find the manual reset on the environment page. 7) Write four sentences: what auto-installed for free, how you persisted a variable correctly, what the secret-vs-env-var test showed, and which internet restriction you'd ship by default. This is the muscle memory that makes cloud delegation reliable instead of flaky.

Key takeaways

  1. 1A cloud environment is a per-repo, reproducible recipe for the task container: base image, dependencies, setup/maintenance scripts, secrets, and network policy — configure it before delegating cloud tasks.
  2. 2The default `universal` base image (openai/codex-universal) ships broad tooling and auto-installs deps for npm/yarn/pnpm and pip/pipenv/poetry; add a custom Bash setup script only when auto-install can't do the job.
  3. 3The setup script runs in a separate shell, so `export` does not reach the agent — persist values via ~/.bashrc or, better, declare environment variables in the environment settings (available for the whole task).
  4. 4Secrets get extra encryption and are available ONLY to setup scripts; they are removed before the agent phase, so do credentialed work in setup and never store a credential in a plain environment variable.
  5. 5Agent-phase internet is OFF by default (setup keeps it ON for installs); when enabled, all traffic routes through an HTTP/HTTPS proxy and can be restricted by domain allowlist (None / Common dependencies ~80+ / All) and by HTTP method (GET/HEAD/OPTIONS only).
  6. 6Container state is cached up to ~12 hours and resumed via the optional maintenance script; changing the setup/maintenance script, env vars, or secrets invalidates the cache, and you can reset it manually. (Image contents, allowlist size, and the TTL are volatile — verify live.)

Quiz

Lock in what you learned

Check your understanding

0 / 4 answered

1.Your setup script sets `export DATABASE_URL=postgres://localhost/test`, but during the task the agent's commands behave as if `DATABASE_URL` is unset. What is the most likely cause?

2.You need the cloud agent to install a package from a private registry that requires an auth token. Where should the token go, and what is the consequence?

3.During the agent phase of a cloud task, the agent tries to `curl` a documentation site to look something up and the request fails. Assuming a fresh, default environment, why?

4.You enable agent internet but want the agent to be able to fetch packages and read docs while making it hard to exfiltrate data to an external service. Which restriction most directly achieves this?

Go deeper

Hand-picked sources to keep learning