Case study
AI Outstaff for web development: automating Docker test environments with Codex and n8n
A web developer was spending too much time on work that had little to do with web development.
The applications themselves were not necessarily complicated to run, but their test environments were. Different projects depended on different combinations of PostgreSQL, MongoDB, Elasticsearch and other services. Containers had to be rebuilt, Docker Compose definitions adjusted, and each new project meant another round of infrastructure setup.
The individual changes were usually small. The problem was their frequency.
Adding a project could mean writing another section in docker-compose.yml. A dependency change could require modifying an image, volume, port or service definition. Testing a site with realistic data often started with fixing the environment before application debugging could even begin.
This was a good candidate for AI automation, but simply giving the developer access to an AI coding assistant would not solve the underlying problem. The assistant first needed to understand how these environments were supposed to work.
The problem was operational knowledge
The recurring workflow looked roughly like this:
flowchart LR
A["Developer needs to run or debug a site"] --> B["Inspect project dependencies"]
B --> C["Adjust Docker Compose"]
C --> D["Rebuild containers"]
D --> E["Fix environment differences"]
E --> F["Start application debugging"]
None of these steps is particularly difficult for an experienced DevOps engineer.
For a web developer, however, they create constant context switching. More importantly, the same infrastructure decisions are made repeatedly across projects.
We did not want to build a large internal development platform just to remove this work. Instead, we used Codex as an operational interface and moved the DevOps knowledge it needed into the project itself.
First, a DevOps engineer taught the agent how the project works
The first stage was deliberately human-led.
A DevOps engineer reviewed the existing development workflow, identified the repetitive operations and prepared Codex to work inside the project.
The workspace contained persistent project instructions such as:
The important file for day-to-day use was SKILL.md.
Instead of relying on the developer to explain the environment every time, we documented the routine operations Codex was expected to perform and the rules it should follow.
For this case, those routines centered on the work that had previously interrupted development:
- adding a new project to the Docker-based development environment;
- creating or modifying the required
docker-compose.ymlsections; - adapting containers when project dependencies changed;
- rebuilding and adjusting test environments around services such as PostgreSQL, MongoDB and Elasticsearch;
- handling the recurring environment work needed before a site could be tested or debugged.
The point was not to make Codex “know DevOps” in general. It was to give it the operating context for this particular development environment.
n8n became the developer interface
The developer did not need to work directly with the Codex CLI.
We connected an n8n Chat workflow to a persistent Codex session running on a dedicated host.
flowchart TD
A["Developer"] --> B["n8n Chat"]
B --> C["Encode request"]
C --> D["SSH"]
D --> E["n8n-codex-chat.sh"]
E --> F["Project profile"]
F --> G["Codex workspace"]
G --> H["Persistent Codex session"]
H --> I["Docker project files"]
The n8n workflow is intentionally small.
It receives the developer’s message, encodes it and executes a fixed wrapper through SSH. The wrapper selects the appropriate project workspace and resumes the existing Codex session.
Conceptually, the remote execution is:
| |
A project profile maps that request to the correct workspace and session:
The prompt itself is passed to Codex over standard input rather than being interpolated into a remote shell command.
Why we kept the Codex session persistent
A stateless AI workflow would have brought back much of the original problem.
Every request would need enough context to explain the project structure, previous decisions and expected way of working. n8n would then need its own memory layer or a separate mechanism for reconstructing that context.
Instead, the Codex session owns the conversation history.
There is no n8n Memory node in this design.
The workspace provides durable instructions. The persisted Codex session provides conversational continuity. n8n is only the transport and user interface.
This also makes multiple projects manageable. Separate profiles can point to separate workspaces and Codex sessions without building a different automation workflow for every project.
Requests targeting the same session are serialized with a lock, while independent profiles can run separately.
What changed for the developer
Previously, a typical request looked like an infrastructure task:
Add this project to the development stack, inspect its dependencies, update Docker Compose, get the containers working, then I can start debugging the application.
After the setup, the developer can describe the intended result through n8n instead.
For example:
or:
Codex receives the request inside a workspace where those conventions have already been documented.
The developer still reviews the resulting changes, but no longer needs to reconstruct the infrastructure solution from scratch for each routine operation.
The DevOps role did not disappear
This distinction is important.
The useful automation was not created by connecting n8n to an AI model and asking it to manage Docker.
A DevOps engineer first had to:
- understand the existing environment;
- identify which operations were repetitive;
- establish the project structure and operating rules for Codex;
- turn those rules into reusable skills and procedures;
- configure the execution path and project boundaries.
Once that work was done, the developer could reuse the DevOps knowledge without requiring a DevOps engineer for every small change.
New or unusual infrastructure problems can still go back to the engineer. If a repeated exception becomes routine, the corresponding procedure can be added to SKILL.md and becomes available for future requests.
flowchart LR
A["New infrastructure problem"] --> B["DevOps engineer solves it"]
B --> C["Procedure becomes repeatable"]
C --> D["Add or update SKILL.md"]
D --> E["Developer can reuse it through n8n"]
This is the part that makes the approach useful over time. The automation can accumulate operational knowledge instead of starting from a generic prompt on every request.
Result
The main result was not a new Docker platform. It was the removal of a recurring category of infrastructure work from the developer’s normal workflow.
Routine Docker environment changes can now be requested through n8n using normal task descriptions. Codex works with the project-specific instructions and persistent context prepared by DevOps.
That changed the division of work:
| |
We deliberately did not measure the result as an artificial percentage improvement. The benefit is more practical: repeated infrastructure setup no longer has to be solved manually every time a developer starts or changes a project.
Why this fits the AI Outstaff model
Traditional outstaffing adds another person to a team.
In this case, a relatively small amount of DevOps engineering was used differently. The engineer established the operating model once, encoded repeatable knowledge into the project, and made that knowledge available to the developer through an AI agent.
The human engineer remains responsible for the difficult part: infrastructure decisions, new patterns and exceptions.
Codex handles the work that has already become routine.
For teams running many small or medium web projects with different Docker dependencies, this can be more practical than either keeping a DevOps engineer involved in every environment change or expecting web developers to maintain all of the infrastructure themselves.