Skip to main content

Architecture

Miabi is built to stay simple on a single node while scaling out to many. This page explains the moving parts and how they interact.

The big picture

Browser / CLI / API clients


Goma Gateway (routing, TLS/ACME) ─▶ Miabi control plane (Go / Okapi) ─▶ Docker Engine (local)
│ serves API + web UI (single binary) │
│ └─ asynq worker (deploys, provisioning, ▼
│ backups, housekeeping) Remote nodes
└─ PostgreSQL (GORM) · Redis (cache / queue) (via agent)

Components

Control plane

A single Go binary built on the Okapi framework. It serves the REST API and the embedded Vue web console (the console is compiled into the binary and served as a static SPA at /), so a deployment is a single image. It talks to the local Docker Engine through the Docker SDK and to remote nodes through the agent.

Background worker

Long-running operations — image builds, deploys, database provisioning, backups, housekeeping — run asynchronously on an asynq worker backed by Redis. The worker is embedded in the control plane by default, and can be split into a dedicated process for production. See Configuration.

Goma Gateway

Goma Gateway sits in front of everything as the reverse proxy. It handles HTTP routing, TLS termination, and default HTTP-01 ACME issuance. Miabi writes per-route configuration files that Goma hot-reloads, so app and database containers are never exposed directly to the host. See Routing & Middlewares.

Node agent

Remote Docker hosts join the platform by running the node agent — a thin Docker proxy that dials the control plane over an outbound WebSocket tunnel (NAT- and firewall-friendly) and exposes only the local Docker socket. See Multi-node.

Datastores

  • PostgreSQL (via GORM) is the system of record for every resource. Schema migrations run automatically on startup, with a versioned upgrade-step system for data migrations.
  • Redis powers caching, rate limiting, JWT-session revocation, and the asynq job queue.

Design principles

  • API first. Every feature is a REST + OpenAPI endpoint. The web console is just a consumer; no business logic lives only in the frontend.
  • Docker first. Docker is the primary runtime. Cluster mode adds Docker Swarm without complicating the single-node path.
  • Multi-tenant. Every resource belongs to a workspace, with workspace_id scoping enforced in the repository layer (not just middleware). See Resource Model.
  • Self-hosted first. Single-node on a plain VPS is a first-class experience.
  • Secure by default. Secrets are encrypted at rest with per-workspace keys, and every mutation is recorded in an audit log.

Single-node vs. multi-node

Out of the box, Miabi manages the local Docker Engine — that's the entire platform on one host. When you add nodes, the control plane schedules containers onto remote hosts through their agents. Enabling cluster mode promotes the fleet to a Docker Swarm with encrypted overlay networking. The Server/node entity and workspace_id scoping exist from the start, so growing from one node to many needs no destructive migration.

Technology stack

LayerChoice
BackendGo 1.25+, Okapi framework, REST + OpenAPI
ORM / DBGORM over PostgreSQL
Cache / queueRedis — cache, rate limiting, asynq queue
Schedulerrobfig/cron via a cron manager
RuntimeDocker Engine via the Docker SDK for Go (optional Swarm cluster mode)
Reverse proxy / TLSGoma Gateway (routing + ACME); managed DNS-01 certs via go-acme/lego
Object storageS3 (aws-sdk-go-v2) or filesystem
Metrics / loggingPrometheus client · jkaninda/logger
FrontendVue 3 + Pinia + Vite + TypeScript