Files
sbbb/docs/deployment.md
Sienna Meridian Satterwhite 2f7785774b docs: add production deployment guide — Serving Looks in Production 👠
Scaleway setup, k3s, kustomize structure, deployment phases, DNS,
cert-manager, backup strategy, image registry.
2026-03-24 11:46:47 +00:00

7.1 KiB
Raw Blame History

Serving Looks in Production 👠

The Super Boujee Business Box runs on a single Scaleway Elastic Metal server in Paris. One box, one bill, European data sovereignty by default. No multi-cloud, no multi-region, no per-seat fees — just a server that belongs to you.


The Server

Property Value
Provider Scaleway Elastic Metal
Region Paris (PAR1/PAR2)
RAM 64GB minimum
Storage Local NVMe
Network Public IPv4, configurable reverse DNS
Monthly cost ~€5080 (server) + ~€716 (services)

External Scaleway Services

Service Purpose Cost
Object Storage PostgreSQL backups (barman), cold asset overflow ~€510/mo
Transactional Email (TEM) Outbound SMTP relay ~€1/mo
Generative APIs AI inference for all components ~€15/mo

Total: under €100/month for the entire collaboration suite. That's less than three Slack seats. 💅


k3s Installation

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--disable=traefik" sh -

Traefik is disabled because we use Pingora. k3s gives us single-binary Kubernetes without the overhead of a full cluster setup.


Deployment Flow

First-Time Setup

# 1. Install k3s (above)

# 2. Configure sunbeam
sunbeam config set \
  --domain sunbeam.pt \
  --host admin@{SERVER_IP} \
  --infra-dir /path/to/sbbb \
  --acme-email ops@sunbeam.pt

# 3. Bring up core infrastructure
sunbeam up              # cert-manager, Linkerd, TLS

# 4. Deploy everything
sunbeam apply           # all namespaces

# 5. Seed credentials
sunbeam seed            # all secrets into OpenBao

# 6. Bootstrap services
sunbeam bootstrap       # Gitea orgs, repos, service accounts

# 7. Verify
sunbeam verify          # OpenBao + VSO sync test
sunbeam check           # functional health probes

Ongoing Deployments

The daily driver. Most deploys are just one command.

# Deploy all namespaces
sunbeam apply

# Deploy specific namespace
sunbeam apply lasuite
sunbeam apply monitoring
sunbeam apply matrix

# Build, push, and deploy a service
sunbeam build sol --push --deploy

Kustomize Structure

sbbb/
├── base/                   # Canonical manifests (environment-agnostic)
│   ├── data/               # PostgreSQL, Valkey, OpenSearch, OpenBao, SearXNG
│   ├── devtools/           # Gitea
│   ├── ingress/            # Pingora proxy
│   ├── lasuite/            # All La Suite apps + Hive + Postfix
│   ├── matrix/             # Tuwunel + Sol☀
│   ├── media/              # LiveKit
│   ├── mesh/               # Linkerd
│   ├── monitoring/         # Prometheus, Grafana, Loki, Tempo, Alloy
│   ├── ory/                # Kratos + Hydra
│   ├── storage/            # SeaweedFS
│   ├── cert-manager/       # Certificate management
│   ├── longhorn/           # Volume management
│   └── vso/                # Vault Secrets Operator
├── overlays/
│   ├── local/              # Local dev patches (resource limits, mkcert)
│   └── production/         # Production patches (SSL, full resources)
├── scripts/                # Bash automation
└── docs/                   # You are here ✨

The Pattern

  • Base holds canonical config — deployments, services, configmaps, values files, vault secrets, alert rules, service monitors
  • Overlays hold only patches — resource overrides, domain-specific values, environment differences
  • DOMAIN_SUFFIX placeholder gets replaced by sed at deploy time
# What sunbeam apply does under the hood
kustomize build overlays/production \
  | sed 's/DOMAIN_SUFFIX/sunbeam.pt/g' \
  | kubectl apply --server-side -f -

Deployment Phases

The stack deploys in order of dependency — foundation first, glamour later:

Phase 1 — Core Infrastructure

  1. Kubernetes & networking (k3s, Linkerd)
  2. Secrets (OpenBao, Vault Secrets Operator)
  3. Database (CloudNativePG)
  4. Cache (Valkey)
  5. Storage (SeaweedFS)

Phase 2 — Identity & Edge

  1. Ory Kratos & Hydra
  2. Pingora edge proxy
  3. cert-manager (Let's Encrypt)

Phase 3 — Applications

  1. Docs (Collabora)
  2. Meet (LiveKit)
  3. Drive
  4. Messages (full email stack)
  5. Calendars
  6. People
  7. Projects
  8. Find
  9. Integration navbar

Phase 4 — Connectivity & Sync

  1. Hive (Drive ↔ S3 sync)
  2. Postfix (outbound SMTP)

Phase 5 — Matrix & AI

  1. Tuwunel (Matrix homeserver)
  2. Sol☀️ (AI agent)

Phase 6 — Development Tools

  1. Gitea
  2. SearXNG

Phase 7 — Observability

  1. Prometheus + Grafana
  2. Loki + Alloy
  3. Tempo
  4. AlertManager

DNS Setup

All subdomains point to the server's public IP via A records:

docs.sunbeam.pt          → A → {SERVER_IP}
meet.sunbeam.pt          → A → {SERVER_IP}
drive.sunbeam.pt         → A → {SERVER_IP}
mail.sunbeam.pt          → A → {SERVER_IP}
messages.sunbeam.pt      → A → {SERVER_IP}
people.sunbeam.pt        → A → {SERVER_IP}
src.sunbeam.pt           → A → {SERVER_IP}
auth.sunbeam.pt          → A → {SERVER_IP}
cal.sunbeam.pt           → A → {SERVER_IP}
projects.sunbeam.pt      → A → {SERVER_IP}
metrics.sunbeam.pt       → A → {SERVER_IP}
livekit.sunbeam.pt       → A → {SERVER_IP}
s3.sunbeam.pt            → A → {SERVER_IP}
# ... and the rest

Email DNS

Email requires additional records:

Record Value
MX Points to server IP (messages-mta-in listens on port 25)
SPF v=spf1 ip4:{SERVER_IP} include:tem.scaleway.com ~all
DKIM Generated by Postfix/Rspamd, published as TXT record
DMARC v=DMARC1; p=quarantine; rua=mailto:dmarc@sunbeam.pt
PTR Reverse DNS configured in Scaleway console

TLS Certificates

cert-manager handles Let's Encrypt certificates in production:

  • ACME HTTP-01 challenges routed by Pingora itself (watches Ingress objects for solver pods)
  • Certificates stored in K8s Secret pingora-tls
  • Pingora watches the Secret and hot-reloads on renewal (zero downtime)

Backups

PostgreSQL

  • Tool: barman (continuous archiving)
  • Destination: Scaleway Object Storage
  • Retention: 30 days (see ops.md for the 7-year cold storage gap)
  • Recovery: CloudNativePG handles point-in-time recovery

Secrets

  • OpenBao (Vault) data backed up separately
  • Root token and unseal keys stored securely offline

Object Storage

  • SeaweedFS volumes on local NVMe
  • Game assets also exist in Drive (Hive keeps them synced — bless that fairy)
  • Consider periodic snapshots for critical buckets

Image Registry

Container images are built and stored in the Gitea registry at src.sunbeam.pt:

# Build and push
sunbeam build proxy --push
sunbeam build sol --push

# Images tagged as
src.sunbeam.pt/studio/proxy:latest
src.sunbeam.pt/studio/sol:latest

For arm64 targets, sunbeam mirror rebuilds amd64-only La Suite images for multi-arch support. We don't discriminate against architectures.