opencode-ansible: Agent-Native Infrastructure as Code with OpenCode
~5 min readThe Infrastructure Gap in Agentic Coding
AI coding agents have transformed how we write application code. They scaffold projects, implement features, fix bugs, and refactor with increasing reliability. But there's a domain where most agents still struggle: infrastructure management.
The reason is straightforward. Application codebases have well-understood conventions — TypeScript projects follow src/, frameworks dictate file locations, and linters enforce formatting. Infrastructure repositories, by contrast, are idiosyncratic. Every DevOps engineer has their own Ansible style, their own variable naming, their own role structure. An AI agent dropped into an unfamiliar Ansible repo has no way to infer whether variables go in defaults/main.yml or vars/main.yml, whether tasks use name: with a colon or without, or whether the convention is one role per playbook or many.
opencode-ansible solves this by providing a agent-native infrastructure skeleton — a public Ansible repository structured specifically for AI-first operations.
Architecture
The repository is organized around three layers of agent interaction:
Layer 1: The Agent Contract
Two files define how agents interact with the repository:
AGENTS.md — The agent context contract. It states the project overview, operating principles (declarative over imperative, idempotent operations, agent-friendly YAML), the complete role structure convention, task naming requirements with examples, and the "making changes" protocol that agents must follow. An agent entering the repository reads this file first.
SKILL.md — An OpenCode skill definition that loads automatically when the agent detects it's working in an Ansible repository. It provides capability documentation, quick commands, and links to available scripts.
Layer 2: The Execution Structure
opencode-ansible/
├── .opencode/agents/ # Three agent roles
│ ├── sysadmin.yaml # Day-to-day operations
│ ├── deployer.yaml # Application deployment
│ └── security.yaml # Security posture
├── inventory/
│ ├── hosts.yml # Host definitions
│ └── group_vars/ # Hierarchical variables
├── roles/ # Reusable roles
│ ├── sysctl/ # Kernel tuning
│ ├── docker/ # Docker engine
│ └── ... # User-defined roles
├── playbooks/
│ ├── site.yml # Master playbook
│ ├── bootstrap.yml # Initial host setup
│ ├── security.yml # Firewall + SSH hardening
│ ├── monitoring.yml # Prometheus/node_exporter
│ └── docker.yml # Docker deployment
├── role-skeletons/ # Scaffold templates
│ ├── config-deploy/ # Config file deployment
│ ├── docker-compose-service/ # Docker Compose services
│ ├── systemd-wrapper/ # Systemd service units
│ ├── pip-package/ # Python package installation
│ └── sysctl-tune/ # Kernel parameter tuning
├── scripts/
│ ├── new-role # Scaffold new roles
│ └── validate # Lint + syntax check
├── AGENTS.md
├── SKILL.md
├── Makefile
└── ansible.cfg
The .opencode/agents/ directory defines three specialized agent personas. When OpenCode receives an infrastructure request, it routes to the appropriate agent based on intent — a security audit goes to the security agent, a deployment to the deployer agent, and routine maintenance to the sysadmin agent.
Layer 3: Guardrails
The skeleton embeds safety mechanisms that agents must use before making changes:
make try— Runsansible-playbook --check --diffagainst localhost.make lint— Runsansible-lintplus syntax validation../scripts/validate— Combined lint + syntax check for CI pipelines.
These are referenced in AGENTS.md as mandatory pre-commit steps. An agent that skips validation violates its own operating protocol.
Why Conventional Ansible Repos Fail Agents
Traditional Ansible repositories encode conventions implicitly. Common anti-patterns that confuse AI agents:
| Anti-pattern | Why It Fails | Agent-Native Fix |
|---|---|---|
Vague task names ("Setup monitoring") | Agent can't determine intent or verify completeness | "Install node_exporter for hardware metrics — exposes :9100" |
| Variables in random locations | Agent doesn't know where to add new config | All variables in defaults/main.yml per role |
| Mixed role structures | Agent produces inconsistent new roles | role-skeletons/ provide canonical templates |
| No task naming convention | Agent generates names that don't match | Strict <verb> <noun> for <purpose> pattern |
| No pre-commit checks | Agent applies broken YAML | make lint + make try enforced |
The opencode-ansible skeleton eliminates these failure modes by making every convention explicit and discoverable.
The Role Skeleton System
The most important design decision is the role skeleton system. When an agent needs to create a new role (e.g., nginx), it doesn't invent the structure from scratch. It references the appropriate skeleton:
# Example: Agent creating an nginx role
# 1. Agent reads role-skeletons/config-deploy/
# 2. Agent copies structure to roles/nginx/
# 3. Agent populates:
# - defaults/main.yml with all configurable values
# - tasks/main.yml with idempotent tasks
# - handlers/main.yml with reload/restart handlers
# - templates/nginx.conf.j2 with service-specific Jinja2
# 4. Agent validates with make lint
# 5. Agent dry-runs with make try
# 6. Agent commits with descriptive message
This guarantees that every role in the repository follows the same conventions, regardless of which agent created it or when.
Real-World Use Case: Memory Management
A practical example of why this structure matters comes from managing memory across a heterogeneous cluster.
Consider a setup with 7 hosts: GPU servers (DGX Spark, AI nodes), web servers, database hosts, and development workstations. Each has different memory requirements:
- GPU servers need hugepage configuration and vm.overcommit for vLLM/llama.cpp
- Database hosts need swappiness tuning for Neo4j
- Workstations need memory limits for containerized services
Without a structured approach, these configurations scatter across sysctl files, Docker daemon configs, and service unit files. With opencode-ansible, the sysctl role already has the structure for kernel parameter tuning, the group_vars/ hierarchy separates per-host-type configuration, and the monitoring playbook provides the observability layer.
An agent can add a new memory tuning parameter by editing roles/sysctl/defaults/main.yml, adding the host group variable in inventory/group_vars/, and verifying with make try — all within the established conventions.
Integrating with Existing Ansible Deployments
The skeleton is designed to be adapted, not adopted wholesale. If you already have an Ansible setup:
- Copy
AGENTS.mdandSKILL.mdinto your existing repo — these files are additive and don't conflict with existing structure. - Align your role structure to match the skeleton's conventions over time — start with task naming, then role skeletons.
- Add
role-skeletons/— create templates from your existing best roles so agents can replicate them. - Add the
.opencode/agents/directory — define agent roles that match your operational patterns.
The value compounds as the repo evolves, because every new role and playbook follows the same conventions that agents already understand.
The Bigger Picture: Operational Consistency at Scale
Agent-native infrastructure management is not about replacing DevOps engineers. It's about removing the context-switching tax that makes infrastructure work slow.
A senior engineer managing 30 servers across 3 environments spends most of their time context-switching: checking which servers have which configurations, verifying that a change won't break something, remembering the exact convention this particular repo uses. An AI agent that understands the conventions, runs validation automatically, and produces changes that match the established patterns reduces this overhead dramatically.
The opencode-ansible skeleton is the infrastructure equivalent of a well-structured application template — it doesn't write your playbooks for you, but it makes every interaction between human, agent, and infrastructure predictable and reliable.
The repository is public on GitHub under MIT license.