Skip to content

Introduction

o‑o is a command line interface for running jobs on ephemeral cloud instances with tracked inputs and outputs. Building data or MLOps pipelines is as simple as stringing together multiple commands, as easily as running locally, but with the power of cloud compute and storage.

Highlights

  • Support for Scaleway and Google Cloud (more to come...)
  • Easily run any command in any language in cloud compute environments
  • Flexible. Define your own run environments with Docker images and machine types
  • Traceable. Trace all inputs and outputs to the commands that produced them
  • You control your data. Data and source code is stored in your managed buckets

Examples

Let's see o-o in action, first follow the installation instructions. Then define a run environment and datastore (for Scaleway or Google Cloud) in an .ooconfig file:

.ooconfig
project: test-project
environments:
  - name: my-simple-env
    provider: Scaleway
    image: docker.io/debian:stable-slim
    machinetype: STARDUST1-S
    region: fr-par-1
    default: true
datastores:
  - name: my-datastore
    provider: Scaleway
    bucket: o-o-data
    region: fr-par
    default: true
.ooconfig
project: test-project
environments:
  - name: my-simple-env
    provider: gcp
    image: docker.io/debian:stable-slim
    machinetype: e2-highcpu-2
    region: northamerica-northeast1-b
    default: true
datastores:
  - name: my-datastore
    provider: gcp
    bucket: o-o-data
    default: true

Hello world

We are now able to run a simple hello world example in our configured environment:

$ o-o run --message "example run" -- echo "Hello World"
Hello World

And that's it. This is an extremely inefficient hello world, and it will likely take a couple of minutes to complete, but it demonstrates how to easily run commands in a cloud environment (under the hood, o-o started and configured our environment, executed the command, and deleted the environment).

Multi-step pipelines

The real power of o‑o comes when stringing together commands into data pipelines. So let's try a multi-step example to demonstrate connecting outputs to inputs of another step. The following steps create files in the special o://output/ directory:

$ o-o run --message "create hello" -- 'echo "Hello" > o://output/hello.txt'
$ o-o run --message "create world" -- 'echo "World" > o://output/world.txt'

We can see the history of our runs with o-o run --list:

$ o-o run --list
cn7gnwiapo example run
ntus965ryy create hello
cxdbx8am38 create world

Note

cn7gnwiapo, ntus965ryy and cxdbx8am38 are unique identifiers that will be different for your runs. Similar to Git commits, a shortened version of the full 45 character identifier is printed here. While using o‑o commands, you only need enough characters to uniquely identify a run.

o://output/ is the output space for the current step, any files placed in this directory are copied to the configured datastore. To use these files as inputs, we reference them with the run id (ntus965ryy and cxdbx8am38). For example, a third step prints out the files contents:

$ o-o run --message "print files" -- \
    'cat o://ntus965ryy/hello.txt && cat o://cxdbx8am38/world.txt'
Hello
World

Tip

To avoid having to lookup run ids of our inputs (hello.txt and world.txt), you can tag runs with more suitable and memorable names.

Again, we can show our run history:

$ o-o run --list
cn7gnwiapo example run
ntus965ryy create hello
cxdbx8am38 create world
7it9dmgncy print files

and get more detailed information of our "print files" step with o-o show:

$ o-o show 7it9dmgncy --inputs
Run 7it9dmgncynuu7j54njgizi5s1ai3b3ajg4auwbw5oggc
Creator: Jon Doe <mail@example.com>
Started: Sun, 1 Feb 10:00:00 2026 -0500
Ended:   Sun, 1 Feb 10:02:00 2026 -0500
Command: cat o://ntus965ryy/hello.txt && cat o://cxdbx8am38/world.txt

    print files

Inputs:
  |\
  o | cxdbx8am38 create world
   /
  o ntus965ryy create hello

GPU environments

It is easy to run commands on GPU environments. Simply configure an environment with a machine type with GPUs included. For example, let's add a new environment with a L4 GPU machine type to our .ooconfig:

.ooconfig
project: test-project
environments:
  - name: my-simple-env
    provider: Scaleway
    image: docker.io/debian:stable-slim
    machinetype: STARDUST1-S
    region: fr-par-1
    default: true
  - name: my-gpu-env
    provider: scaleway
    image: docker.io/nvidia/cuda:11.0.3-base-ubuntu20.04
    machinetype: L4-1-24G
    region: fr-par-1
datastores:
  - name: my-datastore
    provider: Scaleway
    bucket: o-o-data
    region: fr-par
    default: true
.ooconfig
project: test-project
environments:
  - name: my-simple-env
    provider: gcp
    image: docker.io/debian:stable-slim
    machinetype: e2-highcpu-2
    region: northamerica-northeast1-b
    default: true
  - name: my-gpu-env
    provider: gcp
    image: docker.io/nvidia/cuda:11.0.3-base-ubuntu20.04
    machinetype: g2-standard-4
    region: northamerica-northeast1-b
datastores:
  - name: my-datastore
    provider: gcp
    bucket: o-o-data
    default: true

We can now run GPU workloads with the new my-gpu-env environment:

$ o-o run --environment my-gpu-env --message "try gpu" -- nvidia-smi --list-gpus
GPU 0: NVIDIA L4 (UUID: GPU-11f9a1d6-7b30-e36e-d19a-ebc1eeaa1fe1)

And that's the basics. Find out more with o-o --help and in this documentation.