How to plan an infrastructure task without losing your mind: a breakdown using an AWS deployment as an example
The ticket fits on one line: "deploy a new web service to AWS and add jobs to deploy it." Inside that line are a network, containers, a database, secrets, a CDN, two workflows and dozens of small steps that an engineer u
The ticket fits on one line: "deploy a new web service to AWS and add jobs to deploy it." Inside that line are a network, containers, a database, secrets, a CDN, two workflows and dozens of small steps that an engineer usually keeps in their head. That works while nothing interrupts you. Then you join a call, come back, and can't remember whether you checked the security group. And when someone asks how long it will take, the honest answer is "a day or two."
I've been building IT infrastructure for about 15 years, and today I lead a DevOps team at a game studio. Tasks like "spin up a new service" or "change system X" land on my desk every week, and over the years I've settled into a fairly stable way of planning them. I tried to write that method down and then give it to an agent.
Part 1. How an engineer plans a task
The task
Let's take a concrete one:
Deploy a new web service to AWS. It has a React frontend and a Python backend, and requires PostgreSQL to run. The application repository is github.com/javdet/nib-demo-app. This should result in jobs in GitHub for deploying the infrastructure and the application.
Nothing unusual. Tasks like this come in all the time, and anyone who works with infrastructure has a rough idea of what's behind them. Let's walk through what happens in an engineer's head when this ticket arrives, step by step and slower than it happens in real life.
Step 1. Collect context
Before planning anything, the engineer answers three questions:
- What is the project? Who uses it, how critical it is, whether it has environments besides production.
- How does the current infrastructure work? Which AWS accounts exist, where the Terraform state lives, how the networks are laid out, what CI/CD is already there.
- What do we know about the products in the task? Here that's AWS, PostgreSQL and GitHub Actions: which AWS services fit, which Postgres version to use, how Actions will reach the cloud.
Some of the answers are already in their head. The rest they pull from git and the wiki: usually details of how the infrastructure is built and the company's internal rules for cloud, databases and CI/CD.

This step already shows the first problem, and we'll come back to it: a large part of the context isn't written down anywhere except in one person's head.
Step 2. Outline the stages
Once the context is collected, the task falls apart into large stages:
- Write infrastructure code for the AWS resources.
- Write the build and deploy workflows.
- Create the resources in AWS.
- Deploy the application.
- Check that everything works.
The stages depend on each other: you can't deploy the app before the cluster exists, and you can't create the cluster before the code exists. The dependencies matter more than the list itself, because they decide what can run in parallel and what can't.
Step 3. Break the stages into basic actions
Each stage is a set of simple actions you can do in one go:
- edit a file;
- run a CLI command;
- make an API call;
- change a setting in a UI.
For example, "create the resources in AWS" becomes a chain: create a bucket for the state, give GitHub Actions access to the account, run terraform plan, read the diff, run terraform apply, look at the outputs.
Step 4. Lean on rules and experience
To put the actions in the right order, the engineer relies on two things:
- Team rules. How we name resources, where we keep state, which tags are required, what we never do in production.
- Their own experience. They've done something similar before and remember where it hurt last time. ### Step 5. Build the plan: action, check, rollback
These pieces add up to a detailed plan. Usually it lives in the engineer's head, less often on paper. A good plan has three parts:
- actions: what exactly we do;
- checks before closing each stage: how to know the stage is really done, and not just "seems fine";
- a rollback plan in case something goes wrong.

The Rollback column is the one people leave empty most often. Be honest: when did you last write a rollback plan before you needed it?
Step 6. Execute
Then the engineer walks the whole list: action, check, action, check. If a check fails, they roll back or fix the problem, and check again.
Tedious, right?
Where this process breaks
On paper it's a good process. In practice it breaks in three places.
1. The action list lives in your head, and items fall out of it. A call, an alert, a colleague asking you to look at their PR. An hour later you come back to the task and you're no longer sure whether you closed the database port to the internet. A plan in your head doesn't survive context switches, and you can't hand it to a colleague if you get sick in the middle of the task.
This happened to me when I was moving asset buckets from one cloud to another. I copied the data and set up access, but forgot to add CORS. It's a couple of lines of config, and it feels so obvious that it never makes it onto any list.
2. Checks get skipped. You want to save time: "the site opened, so it works." The fact that the database is reachable from the internet, or that backups aren't set up, comes up a month later. Checks are the first thing to go under a deadline, because skipping them doesn't break anything right away.
3. Nobody knows how big the task is. Ask "how long will it take?" and you hear "a day or two." That's not an estimate. Until the task is broken into actions, its size is invisible: to you, to your team lead, and to whoever filed the ticket.
All three have the same cause: the plan exists only in the head of the person doing the work, so you can't hand it over and you can't measure it.
What if an agent builds the plan?
So here's the natural question: what if an agent does the breakdown, the planning, the execution and the checks? The engineer still controls the stages, decides which steps to run, and accepts the final result.

That's why I built nib (Neuro Infrastructure Builder). It's a tool for infrastructure work: it splits a task into stages, writes concrete actions with checks for each stage, builds a rollback plan, and then either runs the steps itself or gives the operator detailed instructions.
Inside, nib follows the same logic as the engineer from Part 1. The work is split into modes, and each mode has its own role:

The context, rules and experience the engineer relies on exist in nib too: a knowledge base, rules and skills. In this post we only use the knowledge base. The rest is a topic for the next part.
Part 2. From git clone to a working site
Now let's run the same task through nib. The whole run is on video:
What you need
- Docker;
- an API key for an LLM provider. nib works with any OpenAI-compatible API, and the installer has presets for OpenAI, OpenRouter, Google Gemini, Qwen, DeepSeek, Grok and Kimi;
- an AWS account. I strongly recommend a separate sandbox account: the agent will need keys that can create resources;
- a GitHub account and a fork of demo-app, if you want to repeat this exact task.
Installation
nib runs locally with Docker Compose or in Kubernetes with a Helm chart. The local option is enough to try it:
git clone https://github.com/javdet/nib.git
cd nib
make setup
make setup is an interactive script. It asks for the LLM provider, the model and the key (I use OpenRouter), then the name of your first project and, optionally, its environments, clouds and regions. You can skip those fields, but nib plans better with them, and you can add more projects later. Then the script generates tokens, writes .env and the config, and starts four containers: Postgres, the knowledge base server, the backend and the frontend.
At the end it prints the UI address (http://localhost:8080 by default; if the port is busy, it picks a free one) and an API token. You paste this token on the login screen. It's also saved in .env as NIB_API_TOKEN, so it's hard to lose.
Knowledge base: what to fill in for a new project
The first place to open after you log in is Knowledgebase. This is the "context" from step 1: everything the engineer knows about the company and the project, and the agent doesn't.
The Company Information card has the basic fields: company name and description, version control system and git base URL, git user, CI/CD system, issue tracker, wiki and team messenger. For our task the key ones are GitHub as the VCS and GitHub Actions as CI/CD, because they decide which workflows the agent will write.

You don't need to upload documents for a new project yet: there's nothing to describe. On a live project, this is where you put what usually sits in the wiki and in people's heads: the network diagram, naming conventions, where state lives, what's special about each environment. You can upload a Markdown document or ask the agent to generate one from a list of repositories.
When a plan finishes, nib writes a report and updates the knowledge base from it, so the next task starts with what the agent learned in the previous one.
Connecting GitHub, AWS and search over MCP
On its own, the agent can only think. To create a repository, look at AWS resources or find documentation, it needs tools. In nib they connect over MCP (Model Context Protocol). It's an open protocol: an LLM application gets a list of tools from an external server and calls them. One MCP server covers one system: GitHub, AWS, search.
If you don't know where to start, ask nib itself, for example: "How do I connect you to AWS over MCP?" Its answer is in the video.
This task needs three servers:
- GitHub: the repository, PRs and workflows;
- AWS: reading and creating resources in the account;
- SearXNG: web search, so the agent can check current documentation instead of relying only on what the model already knows.
nib supports only the HTTP transport for MCP (streamable HTTP), so we run all three servers as containers on the same network as nib. Add them to docker-compose.yml.
GitHub MCP is the official server from GitHub:
github-mcp:
image: ghcr.io/github/github-mcp-server:latest
restart: unless-stopped
command:
- http
- --port=9082
networks:
- nib
aws-mcp:
image: public.ecr.aws/awslabs-mcp/awslabs/aws-api-mcp-server:latest
restart: unless-stopped
environment:
AWS_API_MCP_TRANSPORT: "streamable-http"
AWS_API_MCP_HOST: "0.0.0.0"
AWS_API_MCP_PORT: "9081"
AUTH_TYPE: "no-auth"
AWS_REGION: "${AWS_REGION:-us-east-2}"
AWS_API_MCP_ALLOWED_HOSTS: "aws-mcp,localhost,127.0.0.1"
AWS_API_MCP_ALLOWED_ORIGINS: "aws-mcp,localhost,127.0.0.1"
READ_OPERATIONS_ONLY: "false"
REQUIRE_MUTATION_CONSENT: "false"
AWS_ACCESS_KEY_ID: "${AWS_ACCESS_KEY_ID}"
AWS_SECRET_ACCESS_KEY: "${AWS_SECRET_ACCESS_KEY}"
AWS_SESSION_TOKEN: "${AWS_SESSION_TOKEN:-}"
networks:
- nib
searxng:
image: docker.io/searxng/searxng:latest
restart: unless-stopped
environment:
SEARXNG_PORT: "9083"
SEARXNG_BASE_URL: "http://searxng:9083/"
SEARXNG_SECRET: "${SEARXNG_SECRET}"
volumes:
- ./searxng:/etc/searxng:rw
networks:
- nib
searxng-mcp:
image: docker.io/isokoliuk/mcp-searxng:latest
restart: unless-stopped
environment:
SEARXNG_URL: "http://searxng:9083"
SEARXNG_DEFAULT_RESPONSE_FORMAT: "json"
SEARXNG_MAX_RESULT_CHARS: "500"
SEARXNG_TIMEOUT_MS: "15000"
MCP_HTTP_PORT: "9084"
MCP_HTTP_HOST: "0.0.0.0"
MCP_HTTP_STATELESS: "true"
networks:
- nib
The response format is an easy place to trip up. The MCP wrapper asks SearXNG for results in JSON, but by default SearXNG returns only HTML. The json format has to be enabled in searxng/settings.yml. The nib repository already ships this file with the right setting, which is why the volume is mounted from ./searxng.
Registering the servers in nib
Now nib needs to know where to find these servers. You edit the MCP config in Tools â Config (or through Tools â MCP Servers â Add):
{
"mcpServers": {
"knowledge-base": {
"url": "http://kb-mcp:8081/mcp"
},
"aws": {
"type": "http",
"url": "http://aws-mcp:9081/mcp"
},
"searxng": {
"type": "http",
"url": "http://searxng-mcp:9084/mcp"
},
"github": {
"type": "http",
"url": "http://github-mcp:9082/",
"headers": {
"Authorization": "Bearer ${GITHUB_TOKEN}",
"X-MCP-Toolsets": "all",
"X-MCP-Readonly": "false"
}
}
}
}

knowledge-base is the built-in knowledge base server, and it's already in the config after installation. We add the other three.
Note the ${GITHUB_TOKEN} in the header. Tokens never go into the config as plain text: nib fills them in from Variables â Secrets at request time. Each secret has a list of allowed hosts, so the GitHub token goes only to github-mcp and nowhere else.
So secrets live in two places, and it's easy to mix them up:
-
AWS keys are needed by the
aws-mcpcontainer itself, so they're in.envand reach the container as environment variables; - the GitHub token travels in the HTTP header of the request from nib to the MCP server, so it's in nib's secrets.
Checking the connection
After restarting the containers, open Tools â MCP Servers. For each server you can see the list of tools it exposes. If the list is empty or the server shows an error, the problem is in the network, the address or the token, and there's no point going further: an agent without tools plans blind.
In the video, the very first check fails: GitHub MCP returns an error. The container is up and the address is right, but the GITHUB_TOKEN secret doesn't exist yet, and without it the server returns nothing. We create the secret in Variables â Secrets, check again, and the tool list appears.
You create the token in your GitHub user settings, under Developer settings (github.com/settings/tokens). I used a classic token with the repo and workflow scopes. The second one is easy to miss: without it GitHub rejects pushes to .github/workflows/, and the agent can't commit its workflows.
In my case GitHub exposed a couple of dozen tools, SearXNG five, and AWS only two. That's expected. AWS API MCP Server gives the agent a generic AWS CLI call plus a helper that picks the right command, so all of AWS fits into two tools. GitHub is the opposite, with one tool per operation, and the next section deals with that.
Tool categories and a base set per mode
Three MCP servers give the agent a lot of tools. If you send all of them with every call, the model gets confused, the context grows, and you pay tokens for descriptions of tools this task will never use. Planning cloud resources doesn't need tools for logs and metrics. nib has two mechanisms for this.
Tool categories. Tools are grouped into categories (for example, everything about AWS, everything about git). During decomposition the agent decides which categories the task touches and gets access only to those. Each action in the plan gets an even narrower set: only the categories marked on that action. You don't have to sort the tools by hand. The Auto-fill with AI button on the Tool categories card does a first pass that you can then fix.
A base set for each mode. You can think of nib's modes as subagents: some break the task down, some work out the stages, some run actions. By default every mode gets every tool. In Tools â Included tools you can narrow this: modes that should only read and plan don't need tools that change things. The Distribute with AI button can only remove tools, so the read-only modes lose the tools that change things, and nothing extra gets added.
The first plan
Everything is ready, so we can plan. As a reminder, we're deploying demo-app, an online store for art supplies with a Python backend, a React frontend and a PostgreSQL database. Click New plan and paste the task from Part 1 as is.
From here, nib repeats what the engineer did in Part 1.
Collecting context. First the decompose agent asks clarifying questions, and for most of them it suggests answers you can pick with a click. It asked me two questions an engineer would ask whoever filed the ticket in the first five minutes:
- How should the service be published? Is there a custom domain, or is the address that CloudFront generates enough? The answer decides whether DNS records and a TLS certificate show up in the plan.
- How should the jobs run? Automatically on merge to the main branch, or manually? Really, this is a question about whether the infrastructure can change without a human.
I don't have a domain, so the site lives on the CloudFront address, and the jobs run automatically.

Stages and dependencies. After the answers, the agent shows a Summary (how it understood the task) and a DAG of the stages and the dependencies between them. A DAG (directed acyclic graph) is a "what comes after what" diagram with no loops. Here you see for the first time what was implicit in the engineer's head: which stages can run in parallel and which ones block the rest. If you have no questions about the stages, move on to detailed planning.

Actions, checks, rollback. The Process plan here button starts planning. A separate agent works on each stage, in parallel with the others, and at the end the rollback plan is built. The whole thing took about two minutes. The result was four stages and a rollback plan:

Look at the last row: there are one and a half times more checks than actions. When the plan lives in your head, this is the part that shrinks first under a deadline.
Steps come in four types: run a CLI command, make an API request, work with code, or change something in a web UI. Each step has a detailed description of what to do, and if an action can cause downtime for a system that's already running, the plan warns about it separately.

This answers the third problem from Part 1. nib doesn't estimate in hours, but you see the amount of work before you start: four stages, ten steps, fifteen checks, and it's clear where you need code and where an API call is enough. You can take that to whoever asked "how long will it take," and the conversation will be more concrete than "a day or two."
Preparing access: the agent works over MCP
Each step has a run button, each stage has Execute all, and you can also start a step from the chat by asking the agent. A checkbox next to a step marks it as done, and you can leave a comment on any step. Only one action runs at a time. This is a deliberate limit, so that two agents don't change the same infrastructure in parallel.
The first stage is all API and CLI calls, so the agent ran it on its own, over MCP:
- Reconcile the GitHub Actions OIDC provider in AWS account: the OIDC provider that GitHub Actions will use to get access to AWS;
- Reconcile the remote Terraform state bucket for the prod stack: an S3 bucket for the Terraform state;
- Reconcile the infrastructure deployment role used by Terraform: the role Terraform uses to create resources;
- Reconcile the application deployment role for publishing and rolling out: a separate role for shipping the application;
-
Create or reconcile the protected GitHub production environment: a protected
productionenvironment in the repository.
If you know Kubernetes, you know the word reconcile: bring the system to the desired state. If the resource doesn't exist, create it. If it exists but is configured differently, fix it. If it's already right, do nothing. A step like this is safe to run again, which helps if the plan stops halfway. The two separate roles are deliberate too: the role that ships the application has no reason to be able to create networks and databases.
After each step, the agent reports in the chat what it did. To make the demo clearer, I ran the stage checks myself: I opened the AWS console and made sure the OIDC provider was created, the bucket was there, and the environment had appeared in the repository. The agent can run the checks too; you just have to ask. Remember the OIDC provider check. We'll need it later.
Code: Terraform and workflows
In the second stage, API calls are no longer enough: we need to write Terraform and workflows. For steps like these, nib has an executor that runs a coding agent in a separate container. The agent clones the repository, makes the changes, commits, pushes and opens a pull request.
The first time you run a code step, nib sends you to the executor settings page (Tools â Executor). The executor has three options: off (the default), Local, which means containers on your machine through Docker, and Remote, which runs as Kubernetes Jobs. Local is enough to start: the agent runs right on your computer, and you can see the container logs in the UI.
Pick the agent type and the authentication method. The tokens we need aren't in the secrets list yet, so we create them:
-
CLAUDE_CODE_OAUTH_TOKEN: the Claude Code subscription token, which you get fromclaude setup-token. You can use an Anthropic API key instead; - a GitHub token, so the agent can push its changes. It's the same one as for MCP.
You can keep the default agent image and model. Go back to the plan and run the step.
What the agent wrote
The PR has 20 files: two workflows, Terraform code in infra/terraform and an updated README. In short, here's the architecture:
-
CloudFront in front of everything: the frontend's static files come from a private S3 bucket, and
/api/*requests go to an ALB; - the ALB accepts traffic only from the CloudFront managed prefix list, so you can't reach it directly from the internet;
- the backend runs on ECS Fargate in private subnets and accepts traffic only from the ALB;
- RDS PostgreSQL 16 sits in isolated subnets with no route to the internet. It's Multi-AZ, encrypted, with backups and deletion protection. Terraform generates the password and puts it straight into Secrets Manager;
- GitHub Actions reach AWS over OIDC: the job assumes an IAM role and gets temporary credentials, so not a single long-lived AWS key is stored in GitHub. This is where the provider and the roles from the first stage come in.
And two workflows:
-
Deploy Infrastructure runs
terraform planandterraform apply. Apply runs only manually, with an explicit flag, in theproductionGitHub environment, where you can require approval. That's how the agent used my answer about running jobs; - Deploy Application builds the backend image, pushes it to ECR tagged with the commit, updates the ECS service, builds the frontend, uploads it to S3, invalidates the CloudFront cache and finally runs a smoke test against the CloudFront address.
Two decisions in this code are good examples of the "experience" from Part 1 that usually isn't written down anywhere.
The ECS service is created with desired_count = 0. When terraform apply runs, ECR doesn't have a single application image yet. If you ask for two tasks right away, ECS will keep trying to start an image that doesn't exist. So Terraform creates the service empty and ignores later changes to the image and the task count, and the deploy workflow publishes the first real image and scales the service to two. Without this detail, Terraform would undo the deploy on every run.
The cost is stated openly. A Multi-AZ database and a NAT gateway in every availability zone are a production setup, and they cost money even with no traffic. The agent put this in the README, next to the order in which to run the workflows. For a demo it's overkill, for production it's right, and it's a decision you should make on purpose instead of learning about it from the bill.
The changes look reasonable, so we merge the PR into the main branch.
First run, first failure
We run Deploy Infrastructure, and the first attempt fails.

This happens to experienced engineers too, so it's no big deal. Instead of reading the log myself, I show nib the error and ask it to figure it out. It finds the cause: the OIDC provider created in the first stage doesn't list all the thumbprints.
A thumbprint is a fingerprint of the identity provider's TLS certificate. AWS uses the list of thumbprints to verify that the token really came from GitHub. If the right thumbprint isn't on the list, AWS doesn't trust the token, and the job can't assume the role and get into the account.

We add the missing thumbprints and rerun the workflow. On the second try the infrastructure is created: the log shows the terraform plan and how many resources were created. We check that the database is ready and the load balancer is in place, and mark the steps as done.
Deploying the app: a second failure, then the site
We run Deploy Application, and it also fails the first time: the healthcheck doesn't pass. The deploy finished too quickly, and the app wasn't ready by the time of the check. This is expected, and the next step of the plan says exactly that: rerun the deploy. So we do.

On the second run the deploy passes and the smoke test is green. We open the CloudFront address and see the art-supply store, the service we set out to deploy.

We mark the remaining steps and click Finish. nib closes the plan, writes a report on the work done and updates the knowledge base. Next time a task about this project comes in, the agent will already know that it has a VPC, an ECS cluster and a database, and won't start from zero.
Wrapping up
We took an ordinary task: deploy a web service to AWS and set up GitHub jobs to deploy it. In two minutes nib split it into four stages with ten steps, fifteen checks and a rollback plan. The agent prepared AWS and GitHub access on its own over MCP, the executor wrote the Terraform and the workflows, and we worked through two failures on the way, the OIDC provider and the healthcheck, together with nib. At the end we opened the site and saw the store working.
Back to the three problems from Part 1:
- Items don't get lost. The action list is written down in the plan. You can see it, hand it to a colleague and pick up where you left off.
- Checks don't get skipped. They're written into every stage in advance, and the agent isn't tempted to save time on them. Still, the OIDC story showed that a check is only as useful as it is precise.
- The size is visible before you start. You know how many stages and steps are ahead before the first command runs.
The decisions stay with you: you choose which steps to run, you read the PR, and you accept the result.
Even if you don't plan to use agents, try writing the plan for your next task in three columns: action, check, rollback. That alone solves the first problem and makes the second one much easier.
In Part 1 I said that an engineer relies on team rules and their own experience. You can give nib the same things. In the next post I'll show how to connect your own rules, skills and knowledge base, so the plans come out more accurate and closer to how your team works.
The repository is here: github.com/javdet/nib. Try it on a task of your own, and tell me in the comments: which infrastructure task would you trust to an agent first, and which one never?
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes â full credit and traffic to the original publisher.








