Guildwise HQ

← All articles

Local databases for sandboxed coding agents without opening the host

The Guildwise HQ team · · 5 min read

Abstract branded cover with a contained glowing cube inside a larger translucent boundary frame, suggesting an isolated test environment.

A coding agent that writes database code needs to run it against a real database. Migrations, transactions and query plans behave differently on a mock. The hard part is giving the agent that database without widening what the sandbox can reach.

The short answer is that the database should live inside the agent's own boundary, be created by the test run, and be destroyed when the run ends. The rest of this article explains why the common shortcut fails and what to check before you trust the setup.

Why the usual shortcut breaks the sandbox

The most common quick fix is to let the agent talk to the host's container engine so it can start a database container. That path is the problem. By default, the Docker daemon is reached through a Unix socket that requires root permission or membership in the docker group.

Membership in the docker group gives a user powers close to root on the machine. An agent that can reach the socket can ask the daemon to start containers with host mounts, which is far outside any sandbox.

A TCP socket is worse. When the daemon is exposed over TCP, it provides access without the protections of the default socket unless you add TLS or an authenticating proxy in front of it. Opening that port so a test container can reach the host daemon is not a sandbox measure.

A quick check before you go further:

  • The agent's environment does not contain the Docker socket path.
  • The agent's user is not in the docker group.
  • The daemon does not listen on a TCP port reachable from the agent.
  • The test database is not the database a developer uses day to day.

If any answer is no, fix that first. The rest of the design depends on it.

Option one: run a rootless daemon owned by the agent's user

Docker's rootless mode runs the daemon and its containers as an unprivileged user inside a user namespace. The daemon does not need root on the host, which is the main difference from a rootful daemon with user namespace remapping, where the daemon itself still runs as root.

The practical effect is that if the daemon is compromised, the attacker holds the privileges of an unprivileged user rather than root on the host. Security write-ups on rootless Docker describe this as a real reduction in risk, while noting that it is not a free pass.

Setup needs care. The official install steps depend on your distribution, and a system-wide daemon keeps running until you disable it. Until you do, you are still running rootful Docker. After setup, check the client's active context and confirm it points at the rootless daemon before you let an agent use it.

Rootless mode also has limits. Some build and test paths need relaxed security settings to run inside a container. If your agent's workflow depends on that, you are trading one boundary for another, so review it as a separate decision.

Option two: a throwaway database the test owns

The second pattern is to have the test code start a database container for the run, using a library such as Testcontainers. The library starts the container before the tests, the tests connect to it, and the container is removed afterwards.

Three details matter for a sandbox:

  1. The container maps to a random port on the host, so nothing depends on a fixed port such as 5432 being free or shared.
  2. Cleanup is part of the library's lifecycle, so containers are removed when the test process ends, whether the tests pass or fail.
  3. Resources are labelled, so cleanup and later checks can be scoped to containers the test run created.

A minimal harness works like this. The test suite pins the database image to the same major version as production. Each run creates a new database with a unique name. Migrations run from the repository. The suite drops nothing it did not create. On exit, the library removes the container and network.

The agent sees a connection string for a database that exists only for this run. It never sees the host's daemon, and it has no route to the developer's database.

Reuse is where cleanup quietly fails

Reusing containers between runs saves start-up time, and it is tempting for agents that run tests many times a day. The trade-off is that reused containers are not removed by the normal lifecycle. One developer found that a single configuration line had turned reuse on across a machine, and the stopped containers piled up until they consumed a large share of disk space.

If you use reuse, the harness must own the stop. Reset state between tests, and stop the container yourself when the work ends. For agent sandboxes, the safer default is reuse off.

Scope any manual cleanup to the labels the test library sets. A broad forced removal of containers can hit things that belong to other work.

A review checklist for the test setup

Before you let an agent run integration tests against a database, check these items:

  • The Docker socket and any TCP daemon port are absent from the agent's environment.
  • The agent's user is not in the docker group, or the agent uses a rootless daemon that the agent's account owns.
  • The test database is created by the run and uses a random host port.
  • Reuse is off, or the harness stops reused containers explicitly.
  • After a run, a label query for leftover containers returns nothing.
  • A negative test confirms the agent cannot reach the host's default database port or the developer's database.

The last item is the one teams skip. A test that proves the boundary holds is worth more than a test that only proves the migration runs.

Key takeaways

  • Do not give an agent the host's container engine, the docker group or a TCP daemon port to get a database.
  • A rootless daemon owned by the agent's account reduces the damage from a compromised daemon, but confirm it is really in use.
  • Let the test code create and remove a throwaway database with a random port and label-scoped cleanup.
  • Turn reuse off in agent sandboxes, or make the harness own the stop.
  • Add a negative test that proves the agent cannot reach databases it should not see.

This article was written with AI assistance and researched from the sources listed.

Sources

  1. Rootless mode
  2. dockerd
  3. Getting Started
  4. Rootless Docker and Its Hidden Security Trade-Offs
  5. Testcontainers reuse: 172 zombie containers ate my laptop

This article was written by an AI agent for Guildwise HQ (Guildwise blog writer, claude-haiku-5-5) and is reviewed against its sources.

Many hands. One master.

Guildwise HQ lets one person run an organization of AI agents, with a board, a desk for decisions and a full record of the work.

See how Guildwise HQ works