Self-host
Run Armalo inside your own boundary.
The on-prem install is the same product as the pooled SaaS — one codebase, deployment-shape-agnostic. This page is the production substrate; AWS is permanently off and is not a fallback.
1. The substrate decision: Hetzner + E2B + Cloudflare (no AWS)
Production substrate decision recorded 2026-07-19 with override 2026-07-21. AWS is replaced — not retired as a fallback. There is no AWS fallback path, and the substrate-lock guard fails any change that re-introduces AWS as a deploy target.
Hetzner
Dedicated hosting
Control plane, compute pool, DNS, object storage. Hilsboro, OR (CPX41) for production; Germany/Finland for EU deployments. ISO 27001 + BSI C5 audited.
Reference docsE2B
Sandbox execution
Firecracker-class per-tenant microVMs. Region (EU or US) selected per tenant policy. Snapshots keep cold-start under a second.
Reference docsCloudflare
Public edge + DNS
TLS 1.3 at the edge, DDoS protection, WAF rules, public DNS. No data plane cost on the tenant budget — you stay on the Hetzner + E2B bills.
Reference docsSee /security for the full isolation, residency, and compliance posture against this substrate.
2. Architecture overview
The on-prem install runs the same five planes as pooled SaaS — Experience, Collaboration, Control, Agent Runtime, Observability. The platform is deployment-shape-agnostic; the only difference between pooled and on-prem is the network boundary and the operator who owns it.
┌─ Public edge · Cloudflare
│ TLS 1.3 · DNS · WAF
├─ Control plane · Hetzner CPX41 (boxes + DNS + object storage)
│ Postgres · Drizzle migrations · audit · metering · vault
├─ Agent runtime · E2B Firecracker microVMs
│ Per-tenant sandbox · snapshot/resume · persistent volume
├─ Collaboration plane · realtime CRDT + presence
│ Yjs + y-sweet · server-ordered room log
└─ Observability · OpenTelemetry GenAI + SLO gates3. Account setup
- Hetzner account. Provision a CPX41 box in your preferred region (Germany, Finland, or US West). docs.hetzner.com has the dashboard walkthrough.
- E2B account. Sign in at e2b.dev with GitHub. Generate a per-region API key and a sandbox template ID for the Armalo starter. Bind both keys to the workspace vault.
- Cloudflare account. Add the zone you intend to serve. The deploy script uses the Cloudflare API token to mint wildcard certificates and pin DNS records automatically.
4. The three deploy scripts
Production deploys run through three deterministic scripts in scripts/. Each script builds a fresh timestamped image tag (no mutable :latest), restarts the target container, and verifies the boot posture before returning.
scripts/deploy-hetzner-server.sh
Deploys the @armalo/server image — realtime composition root, HTTP API, room credential broker, audit trail. Resolves secrets from the Hetzner vault, builds a fresh timestamped tag, restarts the container, verifies /healthz before returning.
scripts/deploy-hetzner-web.sh
Deploys the @armalo/web image — Next.js marketing + workspace UI. Shares the server.env file (same box, different port), builds the bundle locally on the box, restarts armalo-app-web, verifies the home route before returning.
scripts/deploy-hetzner-e2b-reaper.sh
Deploys the E2B sandbox reaper — terminates orphaned or budget-exhausted microVMs. Required for the per-tenant cost control story; without it, stale sandboxes keep running and the bill keeps growing.
# Canonical deploy command sequence
corepack pnpm install --frozen-lockfile
corepack pnpm type-check
corepack pnpm test
corepack pnpm guards
bash scripts/deploy-hetzner-server.sh
bash scripts/deploy-hetzner-web.sh
bash scripts/deploy-hetzner-e2b-reaper.sh5. Helm chart for the on-prem install
For fully airgap-capable cluster installs, use the chart at infra/helm/:
values.yaml
Default installation. Hetzner + E2B + Cloudflare. Public DNS, ACME certificates, default tenant policy EU.
values-airgap.yaml
Airgap-capable: external dependencies pinned to local registries; telemetry fully offline; chart distribution as a tarball.
templates/
Kubernetes manifests for control plane, agent runtime, observability, ingress, secrets, and policy engine.
helm upgrade --install armalo ./infra/helm \
-f infra/helm/values.yaml \
--set global.region=eu-fsn1 \
--set global.airgap=false \
--namespace armalo --create-namespace6. Operations runbook
Backups
Hetzner object-storage buckets are snapshot daily with a 30-day rolling window. The control-plane Postgres is backed up every 6 hours with PITR for the last 7 days. Restore runs through scripts/workspace-doctor.mjs against any historical snapshot.
Disaster recovery
The DR posture is one region failover + one region restoration. The first deploy script (deploy-hetzner-server.sh) is also the recovery script — point the secrets resolver at the new region, run the script, verify /healthz. Active tenants continue from the replicated audit trail (replayable from the room log + CRDT states).
Monitoring
Fleet health surfaces in the compass admin dashboard. Each capsule (control plane, runtime, observability, integrations) has its own liveness + readiness probe. SLO gates block every deploy that drops a probe for more than 60 seconds.
Secret rotation
Room tokens rotate every 24 hours. Webhook secrets rotate every 90 days. KMS-managed keys rotate every 90 days. Long-lived cloud credentials are never stored in an agent or a committed file.
Need help with the install?
Our solutions team delivers the install for enterprise and agency deployments — same engine, your boundary. Reach out via /contact to scope.