# How Openship's Multi-Runtime Architecture Works: DockerRuntime vs BareRuntime vs CloudRuntime

> Discover Openship's multi-runtime architecture. Learn how DockerRuntime BareRuntime and CloudRuntime unify builds and deployments for local Docker bare-metal or cloud execution.

- Repository: [oblien/openship](https://github.com/oblien/openship)
- Tags: internals
- Published: 2026-07-30

---

**Openship unifies build and deployment operations behind a common `RuntimeAdapter` interface, enabling workloads to run locally via Docker, directly on bare-metal hosts, or in Oblien's managed cloud through three specialized adapters that declare environment-specific capabilities.**

Openship's multi-runtime architecture allows developers to deploy applications across different infrastructure types without changing application code. The `oblien/openship` repository implements this abstraction through a capability-driven adapter pattern defined in [`packages/adapters/src/runtime/types.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/types.ts). By isolating runtime-specific logic behind a uniform interface, the platform supports seamless execution whether targeting a local Docker socket, a remote VPS, or Oblien's cloud workspaces.

## The RuntimeAdapter Interface Foundation

The architecture centers on the **`RuntimeAdapter`** interface defined in [`packages/adapters/src/runtime/types.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/types.ts). This contract standardizes lifecycle operations including `build`, `deploy`, `stop`, and `streamLogs` across all execution environments.

### Capability-Driven Feature Gates

Each runtime declares specific **capabilities** that determine available functionality. For example, `DockerRuntime` supports `multiServiceDeploy` and `serviceShell`, while `BareRuntime` manages only single processes without multi-service composition. The UI queries `runtime.supports(capability)` before enabling features, preventing runtime-specific errors at the interface level.

## The Three Runtime Implementations

Openship provides three concrete adapters in `packages/adapters/src/runtime/`, each optimized for distinct infrastructure patterns.

### DockerRuntime: Container-Native Execution

The **`DockerRuntime`** class in [`packages/adapters/src/runtime/docker.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/docker.ts) executes builds and deployments against a Docker Engine. It handles:

- Building images via `docker build` streamed through local sockets, TCP, or SSH-tunneled connections
- Managing container lifecycles including start, stop, restart, and destroy operations
- Supporting multi-service Docker Compose-like deployments through `multiServiceDeploy`

### BareRuntime: Direct Host Execution

The **`BareRuntime`** class in [`packages/adapters/src/runtime/bare.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/bare.ts) operates directly on host machines via local processes or SSH:

- Cloning and building source code on the target host using a `CommandExecutor`
- Deploying processes supervised by **systemd** or **nohup**
- Managing releases through hard-link deduplication and atomic release directories

### CloudRuntime: Managed Cloud Workspaces

The **`CloudRuntime`** class in [`packages/adapters/src/runtime/cloud.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/cloud.ts) provisions temporary workspaces in Oblien's cloud infrastructure:

- Provisioning isolated workspaces for running the full `runBuildPipeline`
- Deploying as cloud-managed workloads with automatic port exposure and custom domain support
- Handling Dockerfile-based builds within ephemeral cloud environments

## Runtime Selection and Factory Pattern

Runtime mode is determined by project configuration and instantiated through a centralized factory that enables lazy loading.

### Configuration Schema

The runtime mode is defined in [`packages/core/src/openship-config/schema.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/openship-config/schema.ts) via the `runtime?: OpenshipRuntime` field. Allowed values are `"bare"` or `"docker"`. Cloud mode activates automatically when `DeployConfig.target === "cloud"`.

### Lazy Loading via createRuntime

The factory function `createRuntime` in [`packages/adapters/src/runtime/index.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/index.ts) implements lazy loading to minimize bundle size by importing only the required runtime code:

```typescript
// packages/adapters/src/runtime/index.ts
export async function createRuntime(opts: CreateRuntimeOptions): Promise<RuntimeAdapter> {
  if (opts.mode === "docker") {
    const { DockerRuntime } = await import("./docker");
    return await DockerRuntime.create(opts.docker, opts.systemManager);
  }
  if (opts.mode === "bare") {
    const { BareRuntime } = await import("./bare");
    return new BareRuntime(opts.bare);
  }
  // Cloud mode (used only when DeployConfig.target === "cloud")
  const { CloudRuntime } = await import("./cloud");
  return new CloudRuntime(opts.client, opts.adminProxy);
}

```

The caller passes concrete connection options—such as Docker socket paths, SSH executors, or Oblien API clients—and receives a `RuntimeAdapter` that can be used uniformly regardless of the underlying environment.

## Execution Flow Comparison

Each runtime implements distinct strategies for building and deploying applications, optimized for their specific environments.

### Build Strategies

- **DockerRuntime**: Streams build contexts to the Docker daemon via `DockerRuntime.build()`, using either `buildViaDockerode` or fast SSH-tar pipes for remote engines
- **BareRuntime**: Either builds server-side via `buildOnTarget` using the supplied `CommandExecutor`, or builds locally then transfers files to the target host
- **CloudRuntime**: Provisions a workspace via `CloudRuntime.build()` and executes the shared `runBuildPipeline` inside it, or uploads pre-built bundles to the cloud environment

### Deployment Strategies

- **DockerRuntime**: Creates containers from built images, forwards ports, and manages container state via `DockerRuntime.deploy()`
- **BareRuntime**: Uses `ProcessSupervisor` to start processes, writes environment variables to release directories, and manages atomic deployments through `makeActive`, `archive`, and `purge` operations
- **CloudRuntime**: Makes workspaces permanent, connects custom domains via `publicEndpoints`, and registers workloads with Oblien's routing layer

## Practical Implementation Examples

### Initializing a Runtime

```typescript
import { createRuntime } from "@repo/adapters/runtime";

const runtime = await createRuntime({
  mode: "docker", // "docker" | "bare" | "cloud"
  docker: { socketPath: "/var/run/docker.sock" },
  systemManager: featureManager,
});

```

### Building with DockerRuntime

```typescript
await runtime.build({
  projectId: "proj123",
  sessionId: "sess456",
  repoUrl: "https://github.com/example/app",
  branch: "main",
  stack: "node",
});

```

### Deploying with BareRuntime

```typescript
await runtime.deploy({
  deploymentId: "dep789",
  projectId: "proj123",
  port: 3000,
  envVars: { NODE_ENV: "production" },
  startCommand: "npm start",
});

```

### Cloud Deployment with Custom Domain

```typescript
await runtime.deploy({
  deploymentId: "dep999",
  projectId: "proj123",
  port: 8080,
  publicEndpoints: [{ 
    domain: "myapp", 
    domainType: "custom", 
    customDomain: "app.example.com" 
  }],
  envVars: { NODE_ENV: "production" },
});

```

## Summary

- Openship's multi-runtime architecture relies on the **`RuntimeAdapter`** interface in [`packages/adapters/src/runtime/types.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/types.ts) to unify Docker, bare-metal, and cloud execution behind a single contract.
- **`DockerRuntime`** manages container lifecycles and multi-service deployments via Docker Engine APIs in [`packages/adapters/src/runtime/docker.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/docker.ts).
- **`BareRuntime`** executes processes directly on hosts using systemd or nohup supervision with release directory management in [`packages/adapters/src/runtime/bare.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/bare.ts).
- **`CloudRuntime`** provisions Oblien cloud workspaces and registers workloads with managed routing layers in [`packages/adapters/src/runtime/cloud.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/cloud.ts).
- The **`createRuntime`** factory in [`packages/adapters/src/runtime/index.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/index.ts) enables lazy loading and runtime selection based on the `runtime` configuration field.
- **Capability detection** prevents unsupported operations by checking `runtime.supports()` before execution, ensuring UI elements align with runtime functionality.

## Frequently Asked Questions

### How does Openship decide which runtime to use?

Runtime selection depends on the `runtime` field in [`packages/core/src/openship-config/schema.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/openship-config/schema.ts), which accepts `"bare"` or `"docker"`. Cloud mode activates when `DeployConfig.target === "cloud"`. The `createRuntime` factory in [`packages/adapters/src/runtime/index.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/index.ts) instantiates the appropriate adapter based on this mode, passing connection options like Docker sockets, SSH executors, or Oblien API clients to the constructor.

### Can I switch between runtimes without modifying my application code?

Yes. Because all runtimes implement the same **`RuntimeAdapter`** interface defined in [`packages/adapters/src/runtime/types.ts`](https://github.com/oblien/openship/blob/main/packages/adapters/src/runtime/types.ts), applications remain agnostic to the execution environment. The platform handles runtime-specific concerns like containerization versus process supervision internally, allowing you to migrate from Docker to bare-metal or cloud by changing configuration rather than application code.

### What happens if a runtime doesn't support a specific feature?

Each runtime declares its **capabilities** (such as `multiServiceDeploy` or `serviceShell`) through the `supports()` method. The UI queries `runtime.supports(capability)` before enabling features. For example, because `BareRuntime` lacks `multiServiceDeploy`, the interface disables multi-service deployment options when targeting bare-metal hosts, preventing runtime-specific errors before they occur.

### Where are the runtime implementations located in the source code?

The three adapters reside in `packages/adapters/src/runtime/`: [`docker.ts`](https://github.com/oblien/openship/blob/main/docker.ts) contains **DockerRuntime**, [`bare.ts`](https://github.com/oblien/openship/blob/main/bare.ts) contains **BareRuntime**, and [`cloud.ts`](https://github.com/oblien/openship/blob/main/cloud.ts) contains **CloudRuntime**. The shared interface definitions are in [`types.ts`](https://github.com/oblien/openship/blob/main/types.ts), while the factory function is exported from [`index.ts`](https://github.com/oblien/openship/blob/main/index.ts). Configuration validation for the `runtime` field is defined in [`packages/core/src/openship-config/schema.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/openship-config/schema.ts).