# Openship Stack Detection and Project Types: How the Platform Classifies Repositories

> Openship stack detection classifies repositories into app, docker, services, or monorepo project types. Learn how its pipeline controls your deployment lifecycle.

- Repository: [oblien/openship](https://github.com/oblien/openship)
- Tags: how-to-guide
- Published: 2026-08-19

---

**Openship analyzes every repository with a two-step detection pipeline—workspace detection followed by stack detection—to assign one of four canonical project types (`app`, `docker`, `services`, or `monorepo`) that controls the entire deployment lifecycle.**

Openship, an open-source deployment platform in the `oblien/openship` repository, automatically inspects imported codebases to determine how they should be built and served. Understanding Openship stack detection and project types is essential for teams that want to know why a repository is categorized as a monorepo, a Docker-backed service, or a single framework application. The classification logic lives in the core workspace and stack modules and propagates through the database schema, API layer, and frontend deployment context.

## How Openship Detects Repository Types

Openship identifies the nature of a codebase through two distinct phases. First, it checks whether the repository is a monorepo workspace; if not, it falls back to framework-level stack detection.

### Workspace Detection for Monorepos

The **workspace detection** phase relies on the core `WORKSPACE_DETECTORS` registry defined in [`packages/core/src/workspaces/index.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/workspaces/index.ts). Each detector implements the `WorkspaceDetector` interface and declares which manifest files to search for, along with optional sub-project parsing patterns. Individual implementations—such as the Cargo workspace detector in [`packages/core/src/workspaces/cargo.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/workspaces/cargo.ts)—run cheaply against the repository root via `project-root-detector`. When a detector matches, it contributes a `projectType` of **"monorepo"** and exposes the workspace structure for later UI rendering.

### Stack Detection for Application Frameworks

If no workspace is detected, Openship proceeds to **stack detection** via [`packages/core/src/stacks.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/stacks.ts). Each entry in the stack definitions provides optional `fileMatch` and `depMatch` patterns that trigger recognition of a primary framework such as Next.js, Remix, Vite, or Docker. The result of this step is a `projectType` classification that drives the rest of the deployment pipeline.

## The Four Canonical Project Types

Once detection completes, the repository is assigned one of four canonical values. These types influence database schema defaults, API payloads, and UI behavior throughout the platform.

### `app` — Single Application Source

The `app` type represents classic web applications that compile into a single containerized unit. This value is declared in the TypeScript `ProjectType` enum located at [`apps/dashboard/src/context/deployment/types.ts`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/context/deployment/types.ts) and is the default classification for most framework-based repositories that do not use workspaces or Docker Compose.

### `docker` — Container-First Deployments

A `docker` project signals a Dockerfile-based deployment without extra service introspection. This type directly influences the `runtimeMode` selection and is consumed by the deployment build logic in [`apps/dashboard/src/context/deployment/useDeploymentBuild.tsx`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/context/deployment/useDeploymentBuild.tsx).

### `services` — Multi-Service Compose Projects

The `services` type applies to Docker Compose projects where each service is treated as a separate deployable unit. It activates service-specific UI paths and is referenced by [`apps/dashboard/src/context/deployment/useDeploymentConfig.ts`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/context/deployment/useDeploymentConfig.ts) when normalizing build and runtime behavior for multi-container setups.

### `monorepo` — Workspace-Backed Repositories

When a workspace detector matches, the repository is labeled `monorepo`. This classification triggers monorepo-specific UI components such as [`apps/dashboard/src/components/import-project/MonorepoApps.tsx`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/components/import-project/MonorepoApps.tsx) and informs the build pipeline in [`apps/dashboard/src/context/deployment/useDeploymentBuild.tsx`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/context/deployment/useDeploymentBuild.tsx) that multiple sub-apps may need to be selected and deployed individually.

## How Project Type Propagates Through the System

The detected `projectType` is not merely a label; it acts as a control flag that shapes database records, API contracts, and runtime configuration.

### Database Schema and Flags

The `project` table schema in [`packages/db/src/schema/project.ts`](https://github.com/oblien/openship/blob/main/packages/db/src/schema/project.ts) stores flags such as `hasServer`, `hasBuild`, `framework`, `packageManager`, and `composePath`. Although the schema does not contain a literal `projectType` column, the detection outcome determines the default values for these fields, including `runtimeMode` and `routeStrategy`.

### API Contract and Validation

The public API documentation in `apps/web/content/docs/api/projects.mdx` lists the allowed enum values as `"app" | "docker" | "services" | "monorepo"`. The backend endpoint for creating a project—`POST /projects` in [`apps/dashboard/src/lib/api/projects.ts`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/lib/api/projects.ts)—accepts and validates this field, ensuring downstream logic receives a supported classification.

### Build and Runtime Configuration

Throughout the dashboard, the `DeploymentConfig` object carries the `projectType` value. Helper functions such as `normalizeBuildStrategy` and `normalizeRuntimeMode` inside [`apps/dashboard/src/context/deployment/useDeploymentConfig.ts`](https://github.com/oblien/openship/blob/main/apps/dashboard/src/context/deployment/useDeploymentConfig.ts) branch on this type to select the correct build command, start command, and routing logic. For example, `docker` and `services` projects automatically receive a `"dockerfile"` build strategy.

### Routing and Runtime Selection

Routing defaults are derived directly from the detected type. According to the project schema, `routeStrategy` defaults to `"auto"` for `app` projects, `"loopback-port"` for `services`, and `"container-ip"` for `docker` deployments. This ensures that networking behavior matches the expected runtime topology without manual intervention.

## Practical Code Examples

The following snippets demonstrate how detection results are consumed across the Openship codebase.

```tsx
// Example: Determining the project type while scanning a repo (core)
import { detectStack } from "@repo/core";
import { detectWorkspace } from "@repo/core/workspaces";

async function classifyRepo(rootPath: string) {
  const workspace = await detectWorkspace(rootPath);
  const stack = await detectStack(rootPath);
  // Workspace detection overrides stack detection for monorepos
  const projectType = workspace ? "monorepo" : stack?.projectType ?? "app";
  return projectType;
}

```

```ts
// Example: Creating a project via the dashboard API (frontend)
import { api } from "@/lib/api";

await api.post("/projects", {
  name: "My Next App",
  slug: "my-next-app",
  projectType: "app",               // ← one of the four enum values
  framework: "nextjs",
  packageManager: "pnpm",
  // …other fields are auto‑filled based on detection
});

```

```ts
// Example: Normalizing build strategy based on project type (dashboard)
function normalizeBuildStrategy(
  projectType: DeploymentConfig["projectType"],
  stackDef?: StackDefinition
): BuildStrategy {
  if (projectType === "docker" || projectType === "services") {
    return "dockerfile";
  }
  // fallback to stack‑specific defaults for app / monorepo
  return stackDef?.defaultBuildStrategy ?? "npm";
}

```

## Summary

- Openship classifies repositories via **workspace detection** in [`packages/core/src/workspaces/index.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/workspaces/index.ts) and **stack detection** in [`packages/core/src/stacks.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/stacks.ts).
- The four canonical types are **`app`**, **`docker`**, **`services`**, and **`monorepo`**.
- The detected type flows through the **database schema**, **API contracts**, and **deployment configuration** helpers such as `normalizeBuildStrategy`.
- **Routing defaults** like `routeStrategy` are derived directly from the project type classification.

## Frequently Asked Questions

### What are the four Openship project types?

Openship assigns every repository one of four canonical values: `app` for single web applications, `docker` for Dockerfile-based containers, `services` for Docker Compose multi-service projects, and `monorepo` for workspace-based repositories containing multiple sub-apps.

### How does Openship detect a monorepo?

The core `WORKSPACE_DETECTORS` registry in [`packages/core/src/workspaces/index.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/workspaces/index.ts) enumerates detectors such as the Cargo detector in [`packages/core/src/workspaces/cargo.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/workspaces/cargo.ts). Each detector implements the `WorkspaceDetector` interface and scans the repo root for manifest files; a match returns `projectType = "monorepo"`.

### Which source file handles stack detection?

Generic stack detection logic resides in [`packages/core/src/stacks.ts`](https://github.com/oblien/openship/blob/main/packages/core/src/stacks.ts), where each stack definition supplies optional `fileMatch` and `depMatch` patterns to identify frameworks like Next.js, Remix, or Vite.

### How does project type affect runtime routing?

According to the project schema in [`packages/db/src/schema/project.ts`](https://github.com/oblien/openship/blob/main/packages/db/src/schema/project.ts), the `routeStrategy` field defaults to `"auto"` for `app` projects, `"loopback-port"` for `services`, and `"container-ip"` for `docker` deployments, ensuring the correct networking mode is applied automatically.