← ./work / HARBOUR.infra
HARBOUR.infra
Moving a 40-person studio from rented apps to its own platform
A design studio of 40 pays for 23 subscriptions, has accounts nobody remembers creating, and keeps client files in five places. HARBOUR is a study of bringing all of it home: one platform they own, run like a small cloud.
- Type
- Infrastructure · self-hosting
- Year
- 2026
- Role
- Platform design, migration, operations
- Status
- Concept study
Concept A study of how I’d approach this kind of problem, not a client project.
$ cat ./targets — design targets, not measured results
- 23 → 1
- subscriptions into one platform
- 99.9%
- availability target
- < 1 h
- full restore, rehearsed
- 1
- account to disable when someone leaves
Platform
A three-node Kubernetes cluster on rented bare metal in an EU data centre. The ingress controller handles TLS with automatic certificates and rate limits. Each service runs in its own namespace with network policies, so the calendar can only reach the systems it's meant to.
One login
Single sign-on over OIDC with MFA enforced. Staff and clients get one account each, and when someone leaves, disabling that one user closes every door at once.
Changes through Git
The whole platform is described as code in one repository. A pull request runs tests and scans the container images. Once it's merged, a GitOps controller makes the cluster match the repo, and rolling back means reverting a commit.
Data and recovery
Postgres runs as a primary with a streaming replica. Volumes are snapshotted every hour, encrypted and copied off-site. A full restore into an empty cluster is rehearsed every month, with a target of under an hour.
Watching it
Metrics and logs feed one dashboard. Alerts fire on SLO burn rate so a single slow request doesn't wake anyone, and they arrive as one plain sentence: what's wrong, who's affected and what has already been tried.
$ trace — how it works
- 01Staff & clients
- 02Ingress + SSO
- 03Website · files · calendar · apps
- 04Postgres HA
- 05Hourly snapshots
- 06Off-site copy
$ open ./result — what it would look like