DEV Community

Reed Dev
Reed Dev

Posted on

Per-User Docker Container Isolation: A Pattern for Multi-Tenant AI Agents

When you need true isolation between users in a multi-tenant AI system, shared processes with user IDs are not enough. Here is a pattern that gives each user complete isolation using Docker containers.

The Problem

I was building an AI companion on Telegram (Adola) where each user has ongoing conversations with their own AI agent. A shared instance had three fatal problems:

  1. Context contamination between users
  2. One slow request blocks everyone
  3. No persistent filesystem per user

The Pattern

User Message -> Gateway -> Docker Container (per user) -> Response
Enter fullscreen mode Exit fullscreen mode

Container Lifecycle

async function ensureContainer(userId: string) {
  const name = `app-user-${userId.slice(0, 8)}`;
  const container = docker.getContainer(name);

  try {
    const info = await container.inspect();
    if (info.State.Running) return container;
    await container.start();
  } catch {
    // Container does not exist, create it
    await docker.createContainer({
      name,
      Image: CONTAINER_IMAGE,
      HostConfig: {
        Binds: [`${DATA_DIR}/${userId}/workspace:/workspace`],
        NetworkMode: NETWORK,
      },
    });
  }
  return container;
}
Enter fullscreen mode Exit fullscreen mode

Idle Cleanup

Stopped containers use zero CPU/memory. A cleanup loop stops containers after 30 minutes of inactivity:

setInterval(async () => {
  for (const [userId, lastActivity] of activeUsers) {
    if (Date.now() - lastActivity > IDLE_TIMEOUT) {
      await docker.getContainer(`app-user-${userId.slice(0,8)}`).stop();
    }
  }
}, 300_000); // every 5 min
Enter fullscreen mode Exit fullscreen mode

Cold Start Performance

Restarting a stopped container takes ~3 seconds. For a chat application, this is acceptable -- the user sees a typing indicator while the container boots.

Workspace Persistence

Each user gets a bind-mounted workspace directory:

/data/users/{userId}/workspace/
  MEMORY.md      # Agent-maintained notes
  SCHEDULES.json # Reminders
  session.jsonl  # Conversation history
Enter fullscreen mode Exit fullscreen mode

Bind mounts (not volumes) let the gateway read user files directly without going through docker exec.

Resource Usage

Users Running Containers Stopped RAM Used
7 1-3 (active) 4-6 ~1.2 GB
50 5-10 (active) 40-45 ~3 GB
100 10-20 (active) 80-90 ~5 GB

Stopped containers cost only disk space for their writable layer (usually <100MB each).

When to Use This

  • Users need persistent filesystem access
  • Strong isolation between users matters
  • Per-user resource limits are important
  • Agent/AI workloads with stateful processes

When Not to Use This

  • Thousands of concurrent users (use Kubernetes with pod-per-user instead)
  • Stateless request-response APIs
  • Latency-sensitive applications where 3s cold start is unacceptable

Try It

If you want to see this pattern in action: t.me/adola2048_bot is a Telegram AI companion built this way. Each user gets their own container with persistent memory. Free, no signup.

Top comments (0)