Deploy from git

The dashboard's New instance page deploys your agent from a repository on GitHub or GitLab. Give it a name, an image (Claude Code, which poema's hosts carry, or Shell; Codex, Pi and Hermes once the hosts hold the runtime they install on; or one of your own on invited accounts), an optional repository's https URL and a branch (or its default one), an optional description and a Model where the image takes them, and a shape. Under Advanced are its runtime, its setup and start commands and its environment. The instance clones the head of the branch into repo (an empty directory with no repository), runs the setup command there once, then the start command. Its page shows its phase and status, the commit it runs, a timeline of what happened to it and, while it runs, its log; Recreate deletes it and starts it again from a fresh clone of the branch.

Runtimes and shapes

  • Ubuntu 24.04, the default, and Debian 13 slim (agent-minimal): the images poema's hosts hold, each with git, curl, jq and Claude Code, Python 3 on the first and ripgrep on the second. A host starts no other image.
  • Python 3.12 and Node 22, not on poema's hosts yet: the official python:3.12-bookworm and node:22-bookworm images, pinned to one build each, python@sha256:dbbe4ceb97851e2e5fa83798b239811f871cb743b259ba3563737349f6bcfaa0 and node@sha256:363e1587494626837fa7f9a23bdb453d13b0ff3c67c705c2805cfc69c2d2fad7, with setup presets that install into the clone: python -m venv .venv && .venv/bin/pip install -r requirements.txt, and npm ci.
  • Shapes S 1 vCPU and 1 GiB, M 2 vCPU and 2 GiB, L 2 vCPU and 4 GiB.

MCP servers and skills

For an agent that reads them (Claude Code), New instance takes MCP servers, one per line: a name and the URL of a remote server, or a name and the command that starts one in the instance. It writes them for the agent to find, and the agent uses no others. A remote server's host has to be on Network, as the instance reaches it through poema. Skills are repositories on GitHub or GitLab with a SKILL.md at their root, which the instance clones into repo/.claude/skills. Another agent, Shell, or an image of your own does not read them, and New instance refuses them. Your organization and its projects can offer skills and servers to every instance too, and an instance starts with them (Projects and the library). These are the servers and skills of the agent in the instance; poema-mcp is for the agent that drives poema.

The Terminal box

With Terminal on, the instance's page opens a shell in it in the browser, and poema terminal NAME and poema forward NAME reach it from a shell with your API key. The agent images start with it on and stay up for it when they have no description to work on. Shell stays up doing nothing, for you to work in.

Private repositories

On the Connections page, paste a GitHub fine-grained personal access token for the repositories your instances clone, with Contents read-only, or read and write if your agent pushes. The instance never holds it: poema adds it to git's requests to github.com on their way out. It applies to instances made after it, and is not shown again. GitLab repositories are public ones for now.

Limits

  • The setup command has 15 minutes, once; the setup and start commands 4 KiB each.
  • At most 32 environment variables, none of those poema sets (POEMA_*, GIT_CONFIG*, the proxies', the CA bundle's, HOME, PATH and the models'). Their values are plain text in the instance's spec, which your API keys read: a model's key goes in a Model.
  • Sessions of up to 60 minutes. At its end an instance is suspended, not deleted, and one left suspended for 14 days is deleted. So an agent that must always answer, such as a support bot, stops at the end of each session until you resume it.
  • Each instance takes two of your 12 objects, its Task and its Workspace, and deleting it frees both.
  • It reaches the free tier's hosts (Concepts), and on an invited plan the hosts you list on Network too. That page shows them, and each instance's page shows what it reached and what was refused, with an Allow button where your plan takes hosts of its own.

The same from code

New instance writes a Workspace, which clones the repository, and a Task, which runs a bash script with your setup and start commands as its arguments, so their text is never quoted into it: in the clone it prints the commit, runs the setup command unless it ran before, then the start command. The Gateway portal and the secret git-github it uses are the dashboard's, which writes them again whenever your connections change, so from code use names of your own. Deleting an instance deletes the Workspace that New instance made for it, labelled poema-cloud/instance, never one of yours. With your key, from the Python SDK:

from poema import Poema

p = Poema()
p.set_secret("github", {"token": "github_pat_..."})
p.set_secret("anthropic", {"api-key": "sk-ant-..."})
p.apply({"kind": "Model", "metadata": {"name": "claude"},
         "spec": {"provider": "anthropic", "model": "claude-sonnet-5-5", "secretKey": {"name": "anthropic", "key": "api-key"}}})
p.apply({"kind": "Gateway", "metadata": {"name": "git"}, "spec": {"egress": {"allowlist": {"hosts": [
    {"host": "github.com", "port": 443, "credential": {
        "secretKey": {"name": "github", "key": "token"}, "type": "basic", "username": "x-access-token"}},
    {"host": "pypi.org", "port": 443},
    {"host": "files.pythonhosted.org", "port": 443},
]}}}})
p.apply({"kind": "Workspace", "metadata": {"name": "my-agent"},
         "spec": {"git": [{"name": "repo", "repo": "https://github.com/you/my-agent", "branch": "main", "depth": 1}]}})
script = "export HOME=\"$PWD/.home\" PIP_NO_CACHE_DIR=1 && mkdir -p \"$HOME\" && mkdir -p repo && cd repo && { [ ! -e .git ] || git rev-parse HEAD; } && t() { if command -v timeout >/dev/null; then timeout 900 \"$@\"; else \"$@\"; fi; } && { [ -e ../.setup-done ] || { t bash -c -- \"$0\" && touch ../.setup-done; }; } && exec bash -c -- \"$1\""
t = p.create_task(
    "my-agent",
    ["bash", "-c", script, "python -m venv .venv && .venv/bin/pip install -r requirements.txt", ".venv/bin/python main.py"],
    image="python@sha256:dbbe4ceb97851e2e5fa83798b239811f871cb743b259ba3563737349f6bcfaa0",
    workspaces=["my-agent"],
    gateway="git",
    model="claude",
    resources={"requests": {"cpu": "1", "memory": "1Gi"}, "limits": {"cpu": "1", "memory": "1Gi"}},
)
print(t.logs())

And from the CLI:

poema secret set github token=github_pat_...
poema secret set anthropic api-key=sk-ant-...
poema apply -f - <<'EOF'
kind: Model
metadata: {name: claude}
spec:
  provider: anthropic
  model: claude-sonnet-5-5
  secretKey: {name: anthropic, key: api-key}
EOF
poema apply -f - <<'EOF'
kind: Gateway
metadata: {name: git}
spec:
  egress:
    allowlist:
      hosts:
        - host: github.com
          port: 443
          credential: {secretKey: {name: github, key: token}, type: basic, username: x-access-token}
        - {host: pypi.org, port: 443}
        - {host: files.pythonhosted.org, port: 443}
EOF
poema apply -f - <<'EOF'
kind: Workspace
metadata: {name: my-agent}
spec:
  git:
    - {name: repo, repo: https://github.com/you/my-agent, branch: main, depth: 1}
EOF
poema apply -f - <<'EOF'
kind: Task
metadata: {name: my-agent}
spec:
  image: python@sha256:dbbe4ceb97851e2e5fa83798b239811f871cb743b259ba3563737349f6bcfaa0
  command:
    - bash
    - -c
    - 'export HOME="$PWD/.home" PIP_NO_CACHE_DIR=1 && mkdir -p "$HOME" && mkdir -p repo && cd repo && { [ ! -e .git ] || git rev-parse HEAD; } && t() { if command -v timeout >/dev/null; then timeout 900 "$@"; else "$@"; fi; } && { [ -e ../.setup-done ] || { t bash -c -- "$0" && touch ../.setup-done; }; } && exec bash -c -- "$1"'
    - python -m venv .venv && .venv/bin/pip install -r requirements.txt
    - .venv/bin/python main.py
  workspaces: [{name: my-agent}]
  gateway: {name: git}
  model: {name: claude}
  resources:
    requests: {cpu: "1", memory: 1Gi}
    limits: {cpu: "1", memory: 1Gi}
EOF
poema logs my-agent