---
title: UserContainer
description: "The User Container in its object form: its image, and its part of the Workspace's Size."
---

# `UserContainer`

Type, in `@jr2/orchestrator/src/size.ts`. It is an export of `@jr2/orchestrator`, on the [Authoring surface](../authoring-surface.md) page, in Composition.

```ts
type UserContainer = {
  image: string;
  resources?: Size;
};
```

The User Container in its object form: its image, and its part of the Workspace's Size. The User
Container is an optional container of your own in the Sandbox pod. It runs its own entrypoint
with `/work` mounted, and jr2 never builds into it, probes it or sends it commands. Use it for a
service that must run without an Agent, such as an sshd.

Write it in the `user` option of `workspace()` or of `customize()`. That option also takes the
image as a string; use this object form only to give the User Container a part of the Size. A
`customize()` that names `user` replaces the whole User Container, its part of the Size
included.

It refuses, where you write it. `<where>` is `workspace()` or `customize()`:

- an image that is not a non-empty string. The message starts with
  `` <where>: `user` must be a non-empty string (an image), or { image, resources? } ``.
- `<where>: user.<key> is not accepted — the User Container is { image, resources? } (ADR-0005,
  ADR-0060)`

`resources` is checked as a [`Size`](type-size.md), with the same refusals.

`jr2 up` refuses a part that leaves the Harness container less than its floor. The message starts
with `the Size leaves the Harness container <memory> memory, below its 256Mi floor:` or with
`the Size leaves the Harness container no cpu:`. The text that follows names each number and
where it came from.

**Fields**

- `image`: `string`

  The image: a `file:` URL to a docker context that your module ships
  (`import.meta.resolve("./sshd")`), which `jr2 up` builds, or a registry ref. It must not be
  empty.
- `resources` (optional): [`Size`](type-size.md)

  This container's part of the Workspace's Size, as a Size. jr2 takes it from the Harness
  container's part. A field you leave out gives this container no limit of its own for it: it
  shares the pod's budget. Default: no part.

**Example**

```ts
import { customize, type UserContainer } from "@jr2/orchestrator";
import { task } from "@jr2/machines";

const sshd: UserContainer = {
  image: "registry.example.com/sshd:1.0",
  resources: { limits: { memory: "256Mi", cpu: "250m" } },
};

export const machine = customize(task, {
  repos: { app: { url: "https://github.com/you/app.git" } },
  agents: { coder: { model: "anthropic/claude-haiku-5-5" } },
  user: sshd,
});
```

**See also**

- [Workspaces](../../concepts/workspaces.md)
- [Size a Workspace](../../guides/size-a-workspace.md)
- [ADR-0005: The Sandbox pod's containers: Harness, Custodian, an optional User Container, and an optional Sandbox Effector](../decisions/0005-sandbox-pod-composition.md)
- [ADR-0060: A Workspace states its Size, and jr2 reserves all of it](../decisions/0060-a-workspace-states-its-size-and-jr2-reserves-all-of-it.md)
