Concepts

Images

Every workspace starts from a base image: an operating system plus whatever runtime that image is for. The catalog is a curated set of about two dozen — systems, languages, databases, and a few environments that are genuinely tedious to assemble by hand.

image takes a Docker reference, not a catalog id:

await ws.create({ image: 'oblien/node:24' });     // catalog image
await ws.create({ image: 'n8nio/n8n:latest' });   // any public image works too

Browse the live catalog from the Dashboard creation flow, the Images API, or the CLI. The examples below are a subset — and the catalog is curated, not a whitelist: any public Docker image can be used.

What's in every image

Catalog images are built for this platform rather than pulled straight from Docker Hub, because a workspace is a real machine and a stock base image is not one. Stock ubuntu:24.04 has no ping, no dig, no ps, no ssh, no CA certificates and no compiler.

Every oblien/* image ships with the same userland, whichever distro it is built on — the package names differ wildly between apt, apk, dnf, pacman and zypper, but what you get does not:

  • Network + debuggingping, dig, traceroute, ss, curl, wget, nc, socat
  • Process inspectionps, top, htop, lsof, psmisc
  • Source controlgit, git-lfs, and safe.directory already configured
  • SSHopenssh-server preinstalled, so enabling SSH needs no download and works even on a workspace with internet disabled
  • Build chain — a C/C++ compiler, make, pkg-config and python3, so npm install, pip install and gem install can compile native extensions
  • Editors and archivesvim, nano, less, tar, zip, unzip, zstd, rsync, jq
  • A UTF-8 locale and UTC timezone
  • Repo toolingadd-apt-repository on Ubuntu/Debian, EPEL already enabled on AlmaLinux, a fresh pacman keyring on Arch: the step-one-before-installing-anything of each distro is done

Two deliberate exceptions: Alpine ships no compiler, so it stays ~70 MB (apk add build-base when you need one), and database / web-server images get the diagnostic half of the list but no toolchain — a Postgres workspace stays a Postgres workspace.

Choosing an image

Start with the language

await ws.create({ image: 'oblien/node:24' });      // Node 24 LTS + npm/pnpm/yarn
await ws.create({ image: 'oblien/python:3.13' });  // Python + uv, poetry, pipx
await ws.create({ image: 'oblien/php:8.4' });      // PHP + php-fpm + nginx, already wired
await ws.create({ image: 'oblien/go:1.26' });      // Go toolchain

Also available: oblien/bun:1, oblien/rust:1, oblien/java:25, oblien/ruby:3.4, oblien/dotnet:10, oblien/cpp:13.

Start with the OS

For a clean slate you will install into yourself. Unlike everything else in the catalog, this row is about completeness — you cannot install Fedora into an Ubuntu workspace, so every packaging family is available:

// apt
await ws.create({ image: 'oblien/ubuntu:24.04' }); // the default machine
await ws.create({ image: 'oblien/ubuntu:26.04' }); // newest LTS
await ws.create({ image: 'oblien/debian:13' });    // minimal and predictable

// apk
await ws.create({ image: 'oblien/alpine:3.24' });  // smallest (~70 MB); musl libc

// dnf / rpm
await ws.create({ image: 'oblien/fedora:44' });    // current release, newest RPM toolchains
await ws.create({ image: 'oblien/almalinux:10' }); // RHEL 10-compatible, EPEL enabled

// pacman
await ws.create({ image: 'oblien/arch:base' });    // rolling, AUR-ready

// zypper
await ws.create({ image: 'oblien/opensuse:16.0' }); // SUSE family, SLES-compatible core

Pick by the distro your target environment actually runs: AlmaLinux if production is RHEL, openSUSE if it is SLES, Arch if you need today's package versions, Alpine if size matters more than glibc compatibility.

Running a CLI agent

There is no per-agent image, on purpose: an agent is one install command, and a dedicated image would be stale the day that agent ships a release. oblien/dev is the environment they all need — Node and Python, with npx, uvx, pip and pipx all working:

const ws = await client.workspaces.create({ image: 'oblien/dev:latest' });
const rt = await client.workspaces.runtime(ws.id);

await rt.exec.run(['npm', 'install', '-g', '@anthropic-ai/claude-code']);
// or: uvx <tool>, pipx install <tool>, npm i -g @openai/codex, ...

Databases and services

await ws.create({
  image: 'oblien/postgres:18',                       // ships with pgvector
  config: { env: ['POSTGRES_PASSWORD=secret'] },
});

await ws.create({ image: 'oblien/redis:8' });
await ws.create({ image: 'oblien/mongodb:8' });
await ws.create({ image: 'oblien/mariadb:12',  config: { env: ['MARIADB_ROOT_PASSWORD=secret'] } });
await ws.create({ image: 'oblien/clickhouse:26' });

Some images declare required environment variables — POSTGRES_PASSWORD, MARIADB_ROOT_PASSWORD, PASSWORD for code-server, JUPYTER_TOKEN for the ML image. Create fails with MISSING_REQUIRED_ENV if one is absent, rather than booting a service that either crash-loops or, worse, listens without a password.

Specialist environments

// VS Code in the browser on :8080
await ws.create({ image: 'oblien/code-server:latest', config: { env: ['PASSWORD=secret'] } });

// Chromium + Firefox + WebKit, ready for Playwright or Puppeteer
await ws.create({ image: 'oblien/playwright:1.62' });

// Precompiled data stack + JupyterLab on :8888
await ws.create({ image: 'oblien/python-ml:3.13', config: { env: ['JUPYTER_TOKEN=secret'] } });

// Static hosting / reverse proxy
await ws.create({ image: 'oblien/nginx:1.30' });

Image categories

const all       = await ws.images.list();
const runtimes  = await ws.images.list({ category: 'runtime' });
const databases = await ws.images.list({ category: 'database' });

// Search matches labels, descriptions and keywords
const php = await ws.images.list({ search: 'laravel' });   // → oblien/php:8.4
const db  = await ws.images.list({ search: 'mysql' });     // → oblien/mariadb:12
CategoryWhat's includedExamples
osBase operating systems, one per packaging familyUbuntu 24.04 / 26.04, Debian 13, Alpine 3.24, Fedora 44, AlmaLinux 10, Arch, openSUSE Leap 16
devtoolsReady-to-work environmentsDev Universal, VS Code (code-server)
runtimeLanguage runtimes and their build chainsNode, Bun, Python, PHP, Go, Rust, Java, Ruby, .NET, C/C++
aiData science and MLPython ML + JupyterLab
browserHeadless browser automationPlaywright + Chromium/Firefox/WebKit
databaseData storesPostgreSQL + pgvector, MariaDB, Redis, MongoDB, ClickHouse
infraWeb servers and infrastructureNginx

If you were using a retired image

Per-tool images were removed in favour of environments that install them in seconds. Existing workspaces are unaffected — they keep running on the image they were created with — and any of these still works as a custom image reference.

WasUse instead
claude-code, codex, gemini-cli, opencode, copilotoblien/dev:latest, then npm i -g <the tool>
langchain, agnooblien/dev:latest or oblien/python:3.13, then pip/uv pip install
n8n, convexoblien/node:24 + the vendor's install command, or the vendor image directly
browserlessoblien/playwright:1.62
qdrantoblien/postgres:18 — pgvector is built in
supabase-postgresoblien/postgres:18 + CREATE EXTENSION
ubuntu:24.04, node:22, postgres:latest, …the oblien/* equivalent, which adds the userland above

Installing additional software

const rt = await client.workspaces.runtime(workspaceId);
await rt.exec.run(['sh', '-lc', 'apt-get update && apt-get install -y ffmpeg']);

Use the package manager of the image you picked — apt-get (Ubuntu, Debian, and every language image), apk add (Alpine), dnf install -y (Fedora, AlmaLinux), pacman -S --noconfirm (Arch), zypper --non-interactive install (openSUSE).

Or bake it into the startup command:

await ws.create({
  image: 'oblien/ubuntu:24.04',
  config: {
    cmd: ['bash', '-c', 'apt-get update && apt-get install -y ffmpeg && sleep infinity'],
  },
});

A startup command must not exit — it is PID 1 in the microVM. End long-running setup with sleep infinity (or your own server process), which is what the catalog images do.

Software installed at runtime persists across restarts and is lost on delete or reinstall. Use snapshots to preserve a complex setup.

What an image cannot give you

The kernel comes from the host, not from the image, so it is the same for every distro you pick. Anything that needs to match a running distro kernel will not work: apt-get install linux-headers-$(uname -r) finds nothing, out-of-tree kernel modules (DKMS, custom filesystems) cannot be built or loaded, and eBPF tooling that expects distro BTF is out of scope. Everything in userspace is unaffected — which is nearly everything.

Tips

  • Pick the language image over OS + manual install — the toolchain and headers are already there
  • Alpine is the smallest option, but it uses musl libc: prebuilt glibc binaries and some npm packages will not run on it
  • Database images auto-start their service, and their required password env is enforced at create time
  • Custom images work everywhere the catalog does; they simply do not come with the userland above