I isolated the data environment, not the agent process
When an AI coding agent can read code, run commands, and change a database, the first risk I wanted to reduce was straightforward: an ordinary feature test should not require access to the production database or bring real customer details into everyday development.
I introduced Docker to run a local PostgreSQL instance. It mirrors the production database structure, roles, and access rules. When a test needs realistic business relationships, I load a masked seed. The local project has no direct connection to the production database; engineers and AI agents work against the same rebuildable local environment.
The distinction matters: Docker runs the local database; it does not, in this setup, sandbox the AI agent itself. This reduces the risk of accidental production writes and unnecessary exposure of customer information. It does not, on its own, stop a host-running agent from reading other host files or sending data out.
Production database
│ Human-controlled export and column-level masking
▼
Controlled seed file (outside Git; still sensitive)
│ Import
▼
Local Docker PostgreSQL ← migrations, roles, access rules
▲
│ Local connection
Applications, engineers, and AI coding agents
Local project ──no direct connection── Production database
Why not just use an empty database?
An empty database can tell you whether tables exist. It cannot easily expose broken assumptions about cross-market orders, member relationships, coupons, permissions, or state transitions. If an agent sees only a small piece of code and no reproducible business data, it may produce a change that passes a narrow unit test but fails the existing workflow.
The local database therefore keeps the real schema, migrations, database roles, and row-level access rules, plus clearly synthetic test accounts and cases. The seed preserves relationships needed for testing, while names, contact details, free-text fields, and other sensitive values are handled by a masking policy. A repeatable setup and reset path let us recover from a bad local migration or experiment without “just checking production.”
The masking policy is not an improvised list of search-and-replace commands. Every column has to be classified; a new column without a decision is caught by type checks and tests. Seed generation also scans for patterns that resemble personal data. Those checks are defenses, not proof of perfect anonymization. Free text may escape a pattern scan, and preserved transaction figures and business relationships remain commercially sensitive. The seed stays out of public repositories. Permission to use it locally does not automatically authorize sending it to an external model.
A local tool can still send results to a remote model
There is another data path to inspect: an agent runs SQL, reads a file or executes tests locally, then includes tool output in its next model request. Not pasting data by hand does not mean the model never receives it. Even a failed test that prints a complete object can expose information intended to stay local. With Claude’s client tools, for example, the application executes a tool and sends its output back in a tool_result. Anthropic: tool use
Local database → query / test output → agent context → remote model service
Before choosing an AI tool, check:
- Does the test need production-derived data? Prefer synthetic accounts, orders and state transitions when they reproduce the behavior. A masked seed needs separate authorization and a defined access scope.
- What will reach the model? Limit the data a tool can query and the fields it returns. Avoid full-table dumps, source files and complete objects in context; a check may only need to return pass/fail and a non-sensitive summary.
- Is the recipient approved for this data? Check the service and account settings, retention and training policies, processing locations and contractual requirements. Allowing network access only to a model provider does not authorize sending it every kind of data.
These are additional checks for a remote-agent workflow, not guarantees supplied by Docker or the existing masking process.
What this boundary actually buys us
An agent can run migrations, test queries, and validate features against a production-like structure without having production database credentials. A seed refresh is a separate, human-controlled export, masking, verification, and handoff process. That is stronger than a prompt saying “do not touch production”: the restriction comes from the environment and data flow, not only from instructions.
But an agent running on the host may still read other files it has permission to access, or try to use the network after consuming untrusted content. Simon Willison calls the combination of private data, untrusted input, and external communication the lethal trifecta; Anthropic likewise argues that filesystem and network boundaries need to be considered together. Putting the database in Docker does not remove those risks. Simon Willison, Anthropic Engineering
Running the agent itself in a container or purpose-built sandbox would be a separate next step: mount only the needed workspace, control network egress, withhold production credentials, and do not casually expose the host Docker socket. Docker bind mounts are writable by default; the word “container” alone is not a security guarantee. Docker bind mounts, Docker Engine security
My takeaway is to identify which data and systems an AI development task actually needs, then remove production from the normal working path. A local Docker database solves an important part of that problem. Agent-process isolation, outbound access, and human review still need their own controls.