Architecture Decisions
ADR-0008: Self-hosted Convex on k3s
Documents the decision to self-host Convex backends on the k3s cluster
ADR-0008: Self-hosted Convex on k3s
Status
Accepted
Date: 2026-03-05
Context
Multiple apps in the monorepo use Convex as their backend platform (CalNexus, Lexilink, Planex, Nexus LMS, Archus, BGS-Service, Elbe-Akustik). Convex provides real-time sync, serverless functions, and a document database that significantly accelerates development.
However, running 7 Convex deployments on Convex Cloud raised concerns:
- Cost: Convex Cloud pricing scales per-deployment. With 7 apps (and growing), costs become unpredictable and significant
- Data locality: EU data residency requirements for client projects (BGS-Service, Elbe-Akustik) are not guaranteed on Convex Cloud
- Vendor lock-in: Infrastructure-level dependency on a single SaaS provider for all backend functionality
- Control: Limited ability to tune performance, manage backups, or customize the deployment topology
Key constraints:
- Must maintain the same developer experience (Convex CLI, dashboard, real-time sync)
- Each app needs its own isolated Convex instance
- Must integrate with existing 1Password secret management
- Must work within the single-node k3s cluster
Decision
Self-host Convex on the k3s cluster with the following architecture:
Per-App Isolation
- Each app gets its own Convex StatefulSet in the
convexnamespace - StatefulSets use
OnDeleteupdate strategy for safe, manual upgrades - Persistent volumes ensure data survives pod restarts
Networking
- Each Convex backend is exposed via Traefik ingress at
https://convex-{app}.hansenexus.dev - Lexilink production uses a custom domain:
https://api.lexilink.app
Secret Management
- Admin keys stored in 1Password (
convex-{app}-{env}naming convention) - 1Password Operator syncs admin keys to Kubernetes Secrets
- CI deploys Convex functions using admin keys from GitHub Actions secrets
Dashboard Access
- Convex dashboards protected via Traefik BasicAuth middleware
- Dashboard URLs follow the same
convex-{app}.hansenexus.devpattern
Multi-Environment (Lexilink)
- Production:
convexnamespace - Staging:
hn-stagingnamespace (separate StatefulSet) - Preview:
hn-previewnamespace (scales to 0 on PR close, PVC preserved)
Consequences
Positive
- Cost is predictable and fixed (server cost only, regardless of number of apps)
- Full EU data residency on Hetzner servers in Germany
- Complete control over upgrades, backups, and performance tuning
- No per-deployment pricing allows adding new apps freely
- Convex developer experience (CLI, real-time sync, dashboard) is preserved
Negative
- Self-managed upgrades require manual intervention (
OnDeletestrategy means deleting pods to trigger updates) - Backup strategy must be implemented and maintained independently
- Monitoring Convex health is an additional operational responsibility
- StatefulSet management adds complexity compared to managed cloud
- Single-node cluster means no HA for Convex instances
Neutral
- Convex functions are deployed via the same
bunx convex deploycommand regardless of hosting - The
CONVEX_SELF_HOSTED_URLandCONVEX_SELF_HOSTED_ADMIN_KEYenvironment variables replace Convex Cloud’s authentication - Dashboard provides the same functionality as Convex Cloud’s dashboard
Alternatives Considered
Alternative 1: Convex Cloud
- Description: Use Convex’s managed cloud hosting for all deployments
- Pros: Zero operational overhead, automatic scaling, managed backups, official support
- Cons: Per-deployment pricing scales poorly with 7+ apps, no EU data residency guarantee, vendor lock-in on infrastructure
- Why not chosen: Cost becomes significant at scale; data locality requirements for client projects
Alternative 2: PlanetScale + Supabase
- Description: Replace Convex with a traditional database (PlanetScale for MySQL) plus Supabase for real-time
- Pros: Mature, widely adopted, strong ecosystem
- Cons: Completely different paradigm from Convex, loss of real-time sync out of the box, massive migration effort, two services to manage instead of one
- Why not chosen: Would require rewriting all backend logic across 7 apps; Convex’s real-time document model is integral to the architecture
References
- Convex Self-Hosting Documentation
- Related: ADR-0002 (Convex as Backend Platform)
- Related: ADR-0007 (1Password Secret Management)
Notes
- Convex version upgrades should be tested on a non-critical app (e.g., Planex) before rolling out to all apps
- The
OnDeleteupdate strategy means pods must be manually deleted to pick up new images after a StatefulSet update - PVCs are preserved when preview environments scale to 0, allowing data to persist across PR lifecycles
- Elbe-Akustik’s Convex instance runs on hn-hub rather than k3s due to client-specific requirements