mirror of
https://github.com/oblien/openship.git
synced 2026-10-01 23:34:46 +08:00
159 lines
6.4 KiB
YAML
159 lines
6.4 KiB
YAML
# ──────────────────────────────────────────────────────────
|
|
# Openship — Docker Compose (SaaS / from-source control plane)
|
|
#
|
|
# cp .env.example .env # then edit
|
|
# docker compose up -d --build
|
|
#
|
|
# This is the CONTROL PLANE: it BUILDS api + dashboard + web from source and
|
|
# runs postgres + redis. It's what we deploy for the openship.io SaaS, and what
|
|
# a from-source contributor runs locally. The MODE is decided entirely by .env —
|
|
# no profiles, no per-mode service variants:
|
|
# • SaaS (our env): CLOUD_MODE=true + OPENSHIP_TARGET=cloud-saas + Oblien /
|
|
# GitHub App / secrets. Deployed apps run in Oblien
|
|
# sandboxes — the control plane never runs them here, so
|
|
# there is deliberately NO Docker socket and NO edge.
|
|
# • From-source dev: CLOUD_MODE=false (the .env.example default).
|
|
#
|
|
# SELF-HOSTING (run your own apps as host containers with the OpenResty edge on
|
|
# :80/:443) is a DIFFERENT stack — the pull-based images + `edge` + Docker-out-
|
|
# of-Docker. That lives in docker/docker-compose.yml (or install it with the
|
|
# `openship up` CLI). Don't add the edge/socket here; this stays a clean,
|
|
# unprivileged control plane.
|
|
#
|
|
# Services:
|
|
# • postgres → private :5432 (storage)
|
|
# • redis → private :6379 (queue + cache + rate-limit)
|
|
# • api → :4000 (override: API_PORT)
|
|
# • dashboard→ :3001 (override: DASHBOARD_PORT)
|
|
# • web → :3000 (override: WEB_PORT) landing site (openship.io)
|
|
#
|
|
# The API + dashboard bind fixed internal ports via PORT (the API honors PORT
|
|
# regardless of OPENSHIP_TARGET); a reverse proxy maps the public domains to
|
|
# them. postgres/redis are internal-only. The API auto-migrates Postgres on boot.
|
|
#
|
|
# REMOTE ACCESS: reaching this from another machine (LAN IP or a reverse proxy)
|
|
# requires OPENSHIP_PUBLIC_URL in .env — else the dashboard loads but login is
|
|
# rejected (403 ORIGIN_REJECTED). Behind a proxy also set TRUST_PROXY=true.
|
|
#
|
|
# BIND INTERFACE: the api/dashboard/web ports publish on 127.0.0.1 (loopback)
|
|
# by default, so nothing is exposed off-box unless you say so — a same-host
|
|
# reverse proxy still reaches them over loopback. If your proxy runs on ANOTHER
|
|
# machine (or you want direct off-box access), set OPENSHIP_BIND_ADDR to the
|
|
# interface to publish on, e.g. OPENSHIP_BIND_ADDR=0.0.0.0 (all) or a specific
|
|
# LAN IP. Prefer fronting with a proxy over binding 0.0.0.0.
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
x-logging: &default-logging
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "20m"
|
|
max-file: "3"
|
|
|
|
services:
|
|
# ─── Database (storage, private) ──────────────────────
|
|
postgres:
|
|
image: postgres:16-alpine
|
|
restart: unless-stopped
|
|
logging: *default-logging
|
|
environment:
|
|
POSTGRES_USER: ${POSTGRES_USER:-openship}
|
|
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-openship}
|
|
POSTGRES_DB: ${POSTGRES_DB:-openship}
|
|
# PRIVATE: not published to the host. api reaches it at postgres:5432.
|
|
expose:
|
|
- "5432"
|
|
volumes:
|
|
- postgres_data:/var/lib/postgresql/data
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-openship} -d ${POSTGRES_DB:-openship}"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 12
|
|
|
|
# ─── Cache / Queue / Rate-limit (private) ─────────────
|
|
redis:
|
|
image: redis:7-alpine
|
|
restart: unless-stopped
|
|
logging: *default-logging
|
|
command: ["redis-server", "--appendonly", "yes"]
|
|
# PRIVATE: not published to the host. api reaches it at redis:6379.
|
|
expose:
|
|
- "6379"
|
|
volumes:
|
|
- redis_data:/data
|
|
healthcheck:
|
|
test: ["CMD", "redis-cli", "ping"]
|
|
interval: 5s
|
|
timeout: 3s
|
|
retries: 12
|
|
|
|
# ─── API (control plane) ──────────────────────────────
|
|
api:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/api/Dockerfile
|
|
restart: unless-stopped
|
|
logging: *default-logging
|
|
ports:
|
|
# Loopback by default (nothing exposed off-box); OPENSHIP_BIND_ADDR opts
|
|
# into a public/LAN interface. See the "BIND INTERFACE" note above.
|
|
- "${OPENSHIP_BIND_ADDR:-127.0.0.1}:${API_PORT:-4000}:${API_PORT:-4000}"
|
|
# Mode + secrets (CLOUD_MODE, DEPLOY_MODE, OPENSHIP_TARGET, Oblien, GitHub
|
|
# App, auth secrets, …) all come from .env. The values below OVERRIDE it
|
|
# with the fixed internal port + in-cluster service DNS (compose
|
|
# `environment` wins over `env_file`).
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: "${API_PORT:-4000}"
|
|
DATABASE_URL: postgresql://${POSTGRES_USER:-openship}:${POSTGRES_PASSWORD:-openship}@postgres:5432/${POSTGRES_DB:-openship}
|
|
REDIS_URL: redis://redis:6379
|
|
depends_on:
|
|
postgres:
|
|
condition: service_healthy
|
|
redis:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "bun -e \"fetch('http://127.0.0.1:${API_PORT:-4000}/api/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))\""]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 12
|
|
start_period: 40s
|
|
|
|
# ─── Dashboard (app UI) ───────────────────────────────
|
|
dashboard:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/dashboard/Dockerfile
|
|
restart: unless-stopped
|
|
logging: *default-logging
|
|
ports:
|
|
- "${OPENSHIP_BIND_ADDR:-127.0.0.1}:${DASHBOARD_PORT:-3001}:${DASHBOARD_PORT:-3001}"
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: "${DASHBOARD_PORT:-3001}"
|
|
INTERNAL_API_URL: http://api:${API_PORT:-4000}
|
|
depends_on:
|
|
api:
|
|
condition: service_healthy
|
|
|
|
# ─── Web / Landing ────────────────────────────────────
|
|
web:
|
|
build:
|
|
context: .
|
|
dockerfile: apps/web/Dockerfile
|
|
restart: unless-stopped
|
|
logging: *default-logging
|
|
ports:
|
|
- "${OPENSHIP_BIND_ADDR:-127.0.0.1}:${WEB_PORT:-3000}:${WEB_PORT:-3000}"
|
|
environment:
|
|
NODE_ENV: production
|
|
PORT: "${WEB_PORT:-3000}"
|
|
|
|
volumes:
|
|
postgres_data:
|
|
redis_data:
|