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 tooBrowse 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 + debugging —
ping,dig,traceroute,ss,curl,wget,nc,socat - Process inspection —
ps,top,htop,lsof,psmisc - Source control —
git,git-lfs, andsafe.directoryalready configured - SSH —
openssh-serverpreinstalled, so enabling SSH needs no download and works even on a workspace with internet disabled - Build chain — a C/C++ compiler,
make,pkg-configandpython3, sonpm install,pip installandgem installcan compile native extensions - Editors and archives —
vim,nano,less,tar,zip,unzip,zstd,rsync,jq - A UTF-8 locale and UTC timezone
- Repo tooling —
add-apt-repositoryon 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 toolchainAlso 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 corePick 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| Category | What's included | Examples |
|---|---|---|
| os | Base operating systems, one per packaging family | Ubuntu 24.04 / 26.04, Debian 13, Alpine 3.24, Fedora 44, AlmaLinux 10, Arch, openSUSE Leap 16 |
| devtools | Ready-to-work environments | Dev Universal, VS Code (code-server) |
| runtime | Language runtimes and their build chains | Node, Bun, Python, PHP, Go, Rust, Java, Ruby, .NET, C/C++ |
| ai | Data science and ML | Python ML + JupyterLab |
| browser | Headless browser automation | Playwright + Chromium/Firefox/WebKit |
| database | Data stores | PostgreSQL + pgvector, MariaDB, Redis, MongoDB, ClickHouse |
| infra | Web servers and infrastructure | Nginx |
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.
| Was | Use instead |
|---|---|
claude-code, codex, gemini-cli, opencode, copilot | oblien/dev:latest, then npm i -g <the tool> |
langchain, agno | oblien/dev:latest or oblien/python:3.13, then pip/uv pip install |
n8n, convex | oblien/node:24 + the vendor's install command, or the vendor image directly |
browserless | oblien/playwright:1.62 |
qdrant | oblien/postgres:18 — pgvector is built in |
supabase-postgres | oblien/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