Zero to Professional Grade: A Docker Compose Agent Lab on Kali
Part 1 showed what three autonomous agents found running in parallel against a single target. This part is the build log: how we containerized the whole stack on Kali with Docker Compose, the advanced configuration that made it repeatable, and what it means for red and blue teams once agentic tooling is part of the threat model.
A one-off agent running in a raw terminal is fine for a demo. It is not something you can hand to a second operator, snapshot before a client engagement, or tear down cleanly when you're done. The fix is the same one we'd apply to any other piece of test infrastructure: put it in containers, define it as code, and isolate it from everything else on the box.
01 / FoundationsPreparing Kali
Kali ships with most of what you need for the host side; the main gap is a current Docker Engine plus the Compose plugin, since the distro repos lag behind upstream releases.
Update the base system
Run a full apt update && apt full-upgrade first. An agent lab that pulls images and talks to the internet is not somewhere you want stale kernel or libc packages.
Install Docker Engine from Docker's own repo
Add Docker's official apt repository rather than relying on Kali's package, so you get current Engine and Compose plugin versions together, then install docker-ce, docker-ce-cli, containerd.io, and docker-compose-plugin.
Create a non-root operator group
Add your working user to the docker group rather than running everything as root. Agent tooling that can reach the Docker socket is effectively root on the host — treat group membership as a privileged grant, not a convenience step.
Carve out a dedicated bridge network
Don't let the lab share a network namespace with your primary Kali tooling. A dedicated Compose network keeps agent traffic, target traffic, and your own interactive tooling from colliding or leaking into each other.
02 / The compose fileDefining the stack as code
The shape that held up best across engagements is four services: an orchestrator running the agent harness and skill definitions, a dashboard for the status-board view from Part 1, a logging sidecar that captures every action an agent takes, and one or more target services — the application under test, whether that's a purpose-built demo or something like a standard vulnerable-by-design app used for training.
# docker-compose.yml — structure, not a drop-in config services: orchestrator: image: roo/agent-harness:latest env_file: .env # model keys, never committed volumes: - ./skills:/skills:ro # scoped skill definitions, read-only - agent-state:/state networks: [lab_net] deploy: resources: limits: { cpus: "2", memory: 4G } dashboard: image: roo/agent-dashboard:latest ports: ["127.0.0.1:7860:7860"] # loopback only networks: [lab_net] logging: image: roo/audit-sink:latest volumes: [./logs:/var/log/agents] networks: [lab_net] target-app: image: your-registry/demo-vuln-app:latest networks: [target_net] read_only: true tmpfs: [/tmp] networks: lab_net: { internal: false } target_net: { internal: true } # target has no route out volumes: agent-state:
A few choices here are load-bearing rather than stylistic. The dashboard binds to loopback only — there is no reason a status board showing live exploitation attempts needs to be reachable from anywhere but the operator's own machine. The target network is marked internal, so a compromised or over-eager target container has no path to the internet even if something inside it tries. And the orchestrator's skill directory mounts read-only, so an agent that starts editing its own instructions mid-run can't quietly rewrite its own guardrails.
In Part 1, one of our agents read files off disk that were technically in scope but weren't the ask. Compose-level network isolation doesn't fix a scope problem, but it does mean that if an agent (or the target app itself) ever tried to reach out — for callbacks, exfiltration, or anything else — there's no route for it to use.
03 / Advanced usageWhat separates a demo from a professional-grade setup
Skill files as policy, not documentation
Treat the /skills directory as configuration that ships through the same review process as any other infrastructure change. Each skill file should encode scope (which vulnerability class, which hosts), explicit prohibitions (no reverse shells, no filesystem access beyond the target's own directory, no destructive payloads), and the output format you expect back. Version them in git alongside the compose file so a finding can always be traced to the exact instructions the agent was operating under.
Human-in-the-loop checkpoints
Wire a checkpoint into the orchestrator before any agent moves from "confirm" to "exploit," and again before it writes a report. Compose makes this easy to enforce structurally — the orchestrator service can require an operator to approve a state transition (via the dashboard) before continuing, rather than relying on prompt wording alone to hold an agent back.
Full action audit trail
Every request an agent sends, every command it runs, and every file it touches should land in the logging sidecar as structured, timestamped events — not just the agent's own narrative summary. When a finding needs to be reproduced for a client report six weeks later, the audit log is the source of truth, not the agent's memory of what it did.
Snapshotting and teardown
Because the whole stack is defined in Compose, an engagement environment becomes disposable: docker compose up to stand it up against a new target, docker compose down -v to tear it down completely between clients, and a tagged git commit of the skills directory to snapshot exactly what ran. That's the difference between a repeatable service offering and a one-off terminal session nobody can reconstruct.
Scaling to multiple targets
Compose profiles let you stand up several target services behind the same orchestrator — useful when a single engagement covers a staging environment, a public-facing app, and an internal admin panel. Keep each target on its own isolated network rather than a shared one, so agents scoped to one target genuinely cannot reach another.
Containerizing the lab doesn't make the agents smarter. It makes the whole practice auditable, repeatable, and safe to hand to someone other than the person who built it.
04 / MitigationsWhat this means for red and blue teams
Agentic tooling changes the operating tempo on both sides of an engagement. Here's what we're telling our own red team operators, and what we're telling client blue teams to watch for.
Red team — operational discipline
- Never expose the dashboard or orchestrator API beyond loopback or a VPN-gated interface — it's a full record of live exploitation activity.
- Run agent service accounts with least privilege; an agent's container credentials should never exceed what that specific skill file needs.
- Rotate model and infrastructure API keys per engagement, and keep them out of images entirely — env files only, never baked in.
- Treat every "helpful" scope expansion (an agent reading extra files, trying an unrequested technique) as a finding about your prompt and skill design, not just about the target.
- Log everything. A client will ask for proof of what was and wasn't done — the audit trail is your answer.
Blue team — detection signal
- Multi-agent testing produces a distinct traffic cadence: several parallel, narrowly-themed request bursts against different endpoints in the same short window, rather than one operator's slower manual rhythm.
- Rate limiting and WAF rules tuned for human testers may under-trigger on agents that pace themselves; alert on burst patterns per source, not just raw request volume.
- Hard-coded secrets are still the cheapest win for any scanner, human or automated — secret-scanning in CI and canary tokens in client-side assets catch this class before an agent (yours or someone else's) does.
- Correlate application-layer anomalies with host and container telemetry; an agent that pivots from "confirm injection" to "read schema off disk" leaves a filesystem access pattern worth alerting on independently of the web traffic.
- Assume attacker-side tooling is converging on the same agentic patterns you're reading about here — the defensive posture that catches an autonomous red team agent is largely the same posture that catches a real one.
Everything above describes lab infrastructure for authorized testing against systems you own or are contracted to assess. None of it is a substitute for a signed scope agreement, and network isolation is a safety control for your lab — not a license to point this setup at anything you don't have explicit permission to test.
Next up in this series: benchmarking a dedicated offensive-security agent profile against this general-purpose baseline, on the same containerized stack, so the comparison is apples to apples.
Want this stack built for your team?
ROO SECURE IT SOLUTIONS Solutions sets up and operates agent-assisted testing labs for internal red teams, with the audit trail and isolation controls your compliance team will actually sign off on.
Talk to our team