Skip to main content
Ona Environments are secure, ephemeral, pre-configured development environments that run in the cloud. Understanding how they work and why they work this way transforms how you develop software.

Understanding Ona Environments

Architecture: How it works

When you start an Ona Environment, here’s what happens: Your IDE connects to a Linux VM in the cloud. Inside that VM runs a Dev Container - a configured environment with all your tools. Your code lives in /workspace, and any services (databases, servers) run alongside it. All code execution happens in the cloud, not on your device.

Try it: Explore your environment

Let’s look around inside your Ona Environment to understand what you’re working with.
Make sure you’re connected to an Ona Environment before continuing. If you haven’t started one yet, go back to Setup, Step 4 and create one first. The commands below run inside your environment, not on your local machine.

Step 1: Check your environment’s resources

Open a terminal in your IDE and run:
nproc  # Number of CPU cores
free -h  # Available memory
df -h /workspace  # Disk space
You’re seeing the resources of your cloud VM, likely 4+ cores and 8+ GB RAM, depending on the environment class you selected. Notice that these resources are consistent: every team member who starts an environment with the same class gets the same specs.

Step 2: See where you are

pwd  # Where am I?
ls -la  # What's in this directory?
ps aux | head -n 10  # What processes are running?
You’re in /workspace; your repository is cloned here. Any processes you see are running in this isolated environment, not on your laptop. Nothing you do here affects your local machine.

Why this matters

Now that you’ve seen what’s inside an environment, consider what this enables: Work from any device Your iPad, a borrowed laptop, a coffee shop computer, it doesn’t matter. The environment lives in the cloud. You just connect to it. “Works on my machine” is gone Everyone gets the same environment: same OS, same tools, same versions. If it works in your environment, it works in theirs. Powerful compute when you need it Building a large codebase? Training a model? Testing with heavy data? Your environment can have 32 cores and 64 GB RAM. Your laptop doesn’t need to. Secure isolation Each environment is its own VM. Experiment freely. Install anything. Break things. It’s isolated from your laptop and from your other environments.

Development environments as code

Ona uses Dev Containers, an open standard for defining development environments. Your environment’s configuration lives in .devcontainer/devcontainer.json in your repository.

Why this matters

Traditional development requires manual setup: install Node, install Python, configure databases, set environment variables. This takes hours and drifts over time. With Dev Containers, you declare what your environment needs. Ona provisions it automatically. Every developer gets the same setup. Every time.

Anatomy of a Dev Container

Your repository likely already has a .devcontainer/devcontainer.json file. If you selected the ona-samples/workshop repo, one is included. If you used your own repository, Ona created a basic one for you when you started your first environment.
We’ll go deeper into Dev Container configuration in Lab 2: Configuration as Code, where you’ll customize features, lifecycle commands, and IDE settings. For now, just familiarize yourself with the structure.
Open .devcontainer/devcontainer.json in your IDE and take a look. Here’s what a typical Dev Container configuration looks like:
{
  "name": "My Project",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:20",
  "features": {
    "ghcr.io/devcontainers/features/docker-in-docker:2": {}
  },
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": ["dbaeumer.vscode-eslint"]
    }
  }
}
Here’s what each section does:
  • image: The base Docker image for your environment (OS, language runtime, core tools)
  • features: Additional tools installed on top of the base image (e.g., Docker, GitHub CLI, language runtimes)
  • postCreateCommand: A command that runs after the environment is created (e.g., installing dependencies)
  • customizations: IDE-specific settings like extensions and editor preferences
Your file may look different depending on the repository you chose; that’s expected.

Try it: Explore your Dev Container

  1. Open .devcontainer/devcontainer.json in your IDE
  2. Identify the base image (or Dockerfile), any features, and lifecycle commands
  3. Try adding a feature, for example GitHub CLI:
    "features": {
      "ghcr.io/devcontainers/features/github-cli:1": {}
    }
    
  4. Save the file
  5. Rebuild the container to apply your changes:
    • VS Code / Cursor: Open the Command Palette (Cmd+Shift+P / Ctrl+Shift+P) and run Ona: Rebuild Container. VS Code may also prompt you automatically when it detects changes to devcontainer.json.
    • Terminal: Run gitpod environment devcontainer rebuild
    • Browser IDE: Stop and restart the environment from the Ona dashboard.
  6. Once the rebuild completes and you’re reconnected, verify: run gh --version in the terminal
If you’re using the ona-samples/workshop repo, be careful not to remove existing features or settings, just add to them. If your repository didn’t have a devcontainer.json and Ona created a minimal one for you, feel free to experiment by adding features from containers.dev/features.

Automations: Beyond tools

Dev Containers define tools. Automations define tasks and services; commands that run automatically or on demand when your environment starts.
We’ll cover automations in depth in Lab 2: Configuration as Code, including service dependencies, ready checks, and manual triggers. For now, just understand the concept.
Automations are configured in .ona/automations.yaml. If you’re using the ona-samples/workshop repo, this file already exists. Check your repository to see if you have one. Here’s what a typical automations file looks like:
tasks:
  install:
    name: Install Dependencies
    command: npm install
    triggeredBy:
      - postDevcontainerStart

services:
  dev:
    name: Dev Server
    triggeredBy:
      - postEnvironmentStart
    commands:
      start: npm run dev
      ready: curl -sf http://localhost:3000

  database:
    name: PostgreSQL
    triggeredBy:
      - postDevcontainerStart
    runsOn:
      docker:
        image: postgres:15
        environment:
          - POSTGRES_PASSWORD=postgres
    commands:
      start: postgres
      ready: pg_isready -h localhost
Here’s what each section does:
  • tasks: One-time commands with a single command field. Use triggeredBy to control when they run (e.g. postDevcontainerStart for setup tasks, manual for on-demand tasks).
  • services: Long-running processes with commands.start and an optional commands.ready health check. Services can run directly on the VM or in a Docker container via runsOn.docker.
The result: your environment is ready to use the moment you connect. No manual setup required. If your repository doesn’t have an .ona/automations.yaml file yet, don’t worry, you’ll create one in Lab 2.

Ephemerality: One environment per task

Most developers use one local environment for all work. Ona inverts this: create a new environment for each task, then discard it when done.

Why ephemeral environments matter

Isolation
Each task gets a clean environment. No leftover state from previous work. No conflicts between branches.
Parallelism
Work on multiple tasks simultaneously. Each runs in its own environment. Switch between them instantly.
Reproducibility
Every environment starts from the same configuration. If it works in your environment, it works in everyone’s.
Safety
Experiment freely. Break things. Delete the environment and start fresh. Nothing persists unless you commit it.

The workflow

  1. Start an environment for a new task (bug fix, feature, code review)
  2. Do the work in that environment
  3. Commit and push your changes
  4. Delete the environment (or let it auto-delete after inactivity)
Your work persists in Git. The environment is disposable.

Try it: Create a task-specific environment

  1. Open a new browser tab
  2. Go to app.ona.com
  3. Create a new environment for the same repository (or launch from your Project)
  4. You now have two environments running simultaneously
  5. Make different changes in each environment
  6. Switch between them by switching browser tabs or IDE windows
Each environment is independent. Changes in one don’t affect the other.

Managing environments

Your environments are listed in the left side panel of the Ona dashboard. From there you can:
  • See running and stopped environments
  • Connect to any environment
  • Delete environments you no longer need
  • Pin important environments to prevent auto-deletion
Environments stop automatically after 30 minutes of inactivity. Stopped environments can be restarted. Deleted environments are gone forever (but your code is in Git).

Troubleshooting

Environment won’t start
  • Check your Dev Container config syntax (JSON must be valid)
  • Try a simpler base image: mcr.microsoft.com/devcontainers/base:ubuntu
  • View startup logs from the environment’s detail view in the Ona dashboard
Can’t connect to environment
  • Make sure the Ona extension is installed in your IDE (VS Code, Cursor, etc.)
  • Try connecting via browser first: app.ona.com
  • Check your network; some corporate firewalls block SSH connections
Automation not running
  • Check .ona/automations.yaml syntax (YAML is whitespace-sensitive)
  • View automation logs: gitpod automations service logs <service-name>
  • Ensure triggers are set correctly (e.g., triggeredBy: [postEnvironmentStart] for tasks that should run on start)
Environment is slow
  • Check if you’re using the right environment class (see environment classes)
  • Large builds? Consider upgrading to more CPU/RAM
  • Check if background processes are consuming resources: htop
Need to reset everything?
  • Delete the environment from the left side panel: hover over the environment, click the three-dot menu (), and select Delete
  • Start a fresh environment; it rebuilds from your configuration
  • Your code is safe in Git; environments are disposable

What you’ve learned

You now understand:
  • Remoteness: Environments run in the cloud, unlocking flexibility and power
  • Configuration as code: Dev Containers and Automations define reproducible environments
  • Ephemerality: One environment per task, disposable and isolated
These concepts — remoteness, configuration as code, and ephemerality — are the foundation for everything that follows.
Next: Lab 2: Configuration as Code