Architecture overview
A high-level map of how Openship fits together — the interfaces you drive, the API that runs everything, the platform adapters, and the shared packages.
Openship is one codebase that plays several parts. Think of it like a restaurant: there's the counter where you place an order (the things you click and type), the kitchen that actually cooks (the API), and the appliances the kitchen uses to do the work (the platform layer). This page is the map. The other architecture pages zoom into one area each.
Here's the whole thing on one screen:
You drive Openship three ways
┌───────────┐ ┌───────────┐ ┌───────────┐
│ Dashboard │ │ CLI │ │ Desktop │
│ (Next.js) │ │ (openship)│ │ (Electron)│
└─────┬─────┘ └─────┬─────┘ └─────┬─────┘
└───────────────┼───────────────┘
│ HTTP, under /api/*
┌─────▼─────┐
│ API │ the control plane
│ (Hono) │ projects · deployments · domains · env · auth
└─────┬─────┘
│ getPlatform()
┌─────▼──────┐
│ Adapters │ runtime · routing · SSL · system checks
└─────┬──────┘
┌──────────┼──────────┐
┌────▼───┐ ┌───▼────┐ ┌──▼──────┐
│ local │ │ server │ │ cloud │ where your app ends up
└────────┘ └────────┘ └─────────┘The control plane: the API
The API is the brain. It's a Hono app (a small, fast web framework) that owns everything that matters — projects, deployments, domains, environment variables, backups, and the permission model — and it stores that data in a database (see Data ownership).
Everything else in the picture is a way to talk to this API, or a tool the API uses. Nothing writes to the database directly except the API, and every piece of the UI reaches it over plain HTTP under /api/*. That single front door is why the dashboard, the CLI, and the desktop app always agree with each other: they're all reading and writing the same source.
One door, many keys
The API accepts a browser session cookie (from the dashboard) or a bearer token (from the CLI and scripts). Same endpoints, same rules, whichever you use. See the API overview for the auth model.
The three ways you drive it
All three interfaces are thin — they render screens and send requests. The real work happens behind the API.
- Dashboard — a Next.js web app. This is the full graphical UI, built for teams, and what you see in most screenshots in these docs.
- CLI — the
openshipcommand you install from npm. It wears two hats:openship upcan stand up a whole self-hosted instance on your machine — it bundles the API and fetches the dashboard for you — while everyday commands likeopenship deployare a thin HTTP client that calls the same API endpoints the dashboard does. Great for self-hosting, scripting, and CI. - Desktop app — an Electron wrapper that shows the same dashboard and runs its own local copy of the API on your machine, so you can deploy from a laptop with no server at all.
Because they share the API, you can start a deploy in the dashboard and check on it from the CLI — it's one system, not three.
The platform layer: adapters
When the API needs to do something to the outside world — build an image, start a container, wire up a domain, get an SSL certificate — it doesn't do it directly. It asks the platform layer (the @repo/adapters package). This is the "appliances" in the kitchen analogy, and it has three parts behind a single entry point:
- Runtime — builds and runs your app. There are three runtimes: Docker (your app runs in a container), Bare (your app runs as a plain process, lighter for small machines and the desktop), and Cloud (the work is handed off to Openship Cloud).
- Infra — routing and certificates. On self-hosted instances this is OpenResty (an nginx-based web server) for routing and certbot for automatic Let's Encrypt HTTPS. On cloud, the edge handles it.
- System — prerequisite checks and setup (is Docker installed? is git present?). Self-hosted only.
At startup the API picks the right combination of these once, and the rest of the code just calls getPlatform(). For the full story of how a deploy resolves to a concrete runtime, see Runtime model.
Where an app ends up
A project deploys to one of three targets, and the platform layer above is what makes each one work:
- local — the instance's own machine.
- server — a server you've connected over SSH.
- cloud — Openship Cloud compute.
The target is chosen once per deploy and snapshotted, so the build, the routing, and any later rollback can never disagree about where the app lives.
Shared packages
Openship is a monorepo. Common logic lives in @repo/* packages that every app reuses, so there's one implementation of each thing rather than four copies that drift apart.
| Package | What it holds |
|---|---|
@repo/core | Pure shared logic — types, stack detection, constants, error classes. No side effects. |
@repo/db | All database access, via Drizzle ORM over Postgres (or an embedded PGlite for zero-setup installs). Every query goes through typed repositories. |
@repo/adapters | The platform layer described above: runtime, infra, and system. |
@repo/ui | The shared React component library used by the dashboard and the marketing/docs site. |
@repo/onboarding | The setup and first-run flow shared by the CLI, dashboard, and desktop. |
One codebase, three roles
The same code runs as Openship Cloud (the managed SaaS), as a self-hosted instance on your own server, or as the desktop app. Configuration at startup decides which role an instance plays and which runtime it uses (for self-hosted, DEPLOY_MODE=docker or bare). Nothing is forked — the cloud and the version you self-host are literally the same source.
The one thing that changes with the role is where data is canonical. Local and server projects live entirely in your own instance's database; cloud projects are owned by Openship Cloud and your instance acts as a gateway in front of them. That split is the subject of the next two pages.
What next?
Runtime model
How a deployment resolves to Docker, a bare process, or cloud — and how routing and SSL are handled.
Cloud-as-source
Why cloud projects are owned by the SaaS and your instance is a gateway, not a mirror.
Data ownership
Exactly what lives where, and how projects move between self-hosted and cloud.