Skip to main content

One CLI Command to a Full Remote Stack: What Moniepoint's Crossplane-Built Dev Environments Cost to Build Versus Buy

11 min readDora NodaDora Noda
Share
On this page

Moniepoint processes $17 billion a month across 10 million businesses and individuals, and its developers test changes against full ecosystems — not a single service, but the chat app and the identity service and both databases and Redis. When that stack outgrew the laptop, the fintech didn't buy seats on a cloud dev-environment platform. It built eshu workload run: one CLI command that turns Score-spec YAML files into a namespaced full stack on a remote Kubernetes cluster, with Vault-managed database credentials and a TTL reaper that deletes the namespace when the developer forgets. The August 5, 2026 engineering post by Sergio Lourenco walks through the whole design, including the two bugs that bit hardest.

Here is the cost thesis up front, with the receipts in the build-vs-buy section below: at a normalized buy price of roughly $15–65 per developer per month, a team of around 20 developers breaks even on the build in year two, and a 100-developer organization wins in year one. The variable that moves the answer is maintenance burden — half a day a month of platform attention versus two days moves the steady-state breakeven from ~17 seats to ~60. If you run fewer than a dozen developers, buy. If you run a fleet and a platform team already, the Moniepoint shape is cheaper and keeps full-stack dev data on infrastructure you control.

eshu end to end: Score YAML in, namespaced stack out

The developer experience is deliberately boring. After installing Moniepoint's custom CLI, the first eshu workload run generates a random workload name and ID — stored in a local config file — so two developers never clash on the shared remote cluster. The developer writes plain YAML files following the Score specification (the CNCF Sandbox workload spec: describe containers, ports, and resource dependencies once, translate to any platform), each with five parts: apiVersion, metadata, containers, service, and resources.

The post's worked example is a chat app: a frontend container, a backend API, a separate identity service, a dedicated Postgres plus Redis for chat, and its own database for identity — defined across two YAML files, with a special service resource letting the chat file reference the identity workload by name. The CLI merges the files into one, resolves file references like ${file:initscript.sql}, and validates the result against a schema client-side, so typos fail in milliseconds without a cluster round trip. Only then does the merged definition travel over HTTP to eshu's server inside the remote cluster.

The server side is where Crossplane enters. The server creates a namespace named after the workload ID (the post's example: fe65c057-45760157-careful-weasel), copies in the secrets the workloads need — notably the image-pull secret for Moniepoint's internal Artifactory — and then creates an XWorkload custom resource carrying the Score definition. That CR kicks off a Crossplane composition pipeline running a custom function built on function-sdk-go, which does four tasks per workload: parse the Score YAML out of the request, generate the Kubernetes resources plus an environment-variable map (including auto-generated database credentials), add the containers, and create the services plus an nginx proxy pod that makes everything reachable via port-forward.

Two design details carry most of the elegance. First, environment wiring is pure string interpolation: a container that needs the Postgres resource named pg just references resources.pg.host, resources.pg.username, and resources.pg.password, and the function substitutes the generated values. Second, every backing resource implements one shared interface behind a chain-of-responsibility flow, so adding a new resource type means implementing the interface, not touching the pipeline. That abstraction pays off immediately on initialization: Postgres takes a config map mounted at /docker-entrypoint-initdb.d/init.sql, while the Google Spanner emulator has no such hook — so the Spanner implementation launches a job that connects to the pod and runs the init script after start. The developer's YAML is identical either way.

Once the composition lands, the CLI polls workload status through the Crossplane spec.crossplane.resourceRefs label, and when everything reports Running it opens port-forwards through the nginx proxy — one randomized port per resource and service — to the developer's machine. No ingress, no DNS, no TLS ceremony: remote compute, local access.

Build versus buy, with the category error corrected

Before the numbers, the scoping correction that makes them honest. Moniepoint compares eshu against docker compose and Testcontainers — developer-run local stacks — not against preview-environment vendors. That is the right comparison, and this post keeps it.

Per-PR preview platforms (Bunnyshell's instance plans start at $49/month; an illustrative $50 per active full-stack environment per month is the usual modeling anchor) solve a different job: reviewer-facing URLs bound to the pull-request lifecycle, created on PR open and destroyed on merge. Eshu environments are developer-driven, live for hours or days, are reached over port-forward instead of HTTPS URLs, and die by TTL, not by merge event. Buying preview seats does not replace eshu workload run, and building eshu does not give you per-PR review links. Price the thing against its actual shelf: remote dev environments.

The buy shelf for remote dev, normalized to per-developer-month at a stated typical usage of ~160 workspace hours per month on a 2-core-equivalent with modest storage:

Buy optionPricing shapeNormalized ~$/dev/moFine print
Coder (self-hosted)From $35/user/mo platform fee + your compute~$50–65You still run the cluster; fee buys the control plane, templates, and IDE integration
GitHub CodespacesUsage, ~$0.09/core-hour past the free allowance~$13–30Light users near the bottom; heavy or always-on usage climbs fast
DaytonaUsage (~$200 credit ≈ first 1,200 hours in third-party modeling)~$27 modeledModeled, not invoiced — slot in your real quote

So the buy band is roughly $15–65 per developer per month, recurring forever, scaling linearly with headcount. A 20-developer team pays $3,600–15,600/year; a 100-developer org pays $18,000–78,000/year. Take $40/dev/mo ($480/dev/yr) as the midpoint for the breakeven below.

The build side, with every input stated so you can substitute your own: six platform-engineering weeks to build the CLI, cluster server, composition function, Vault wiring, and TTL reaper (240 hours × loaded $150/hr ≈ $36,000 one-time); half a day a month of maintenance (48 hours/year × $150 ≈ $7,200/year); and the remote dev cluster share, roughly two to three small nodes at ~$1,000/year. Year one ≈ $44,000; year two onward ≈ $8,200/year.

Against the $480/dev/yr buy midpoint, year-one breakeven sits near 90 seats — a Moniepoint-sized organization wins immediately — while the steady-state breakeven is ~17 seats, so a 20-developer team is ahead from year two and every hire after that widens the gap, because the fleet's marginal environment cost is near zero while the buy meter ticks per seat.

Now the two sensitivities that decide real cases. First, usage hours move the buy side: a team averaging 60 workspace hours a month (part-time contributors, Codespaces-style metering near free allowances) can sit at ~$15/dev/mo, pushing steady-state breakeven past 45 seats; a team running heavy always-on workspaces — or agentic coding loops that burn workspace hours unattended — lands at the top of the band, where the build wins at any headcount.

Second, maintenance burden moves the build side: if the composition function, Vault engine upgrades, and reaper tuning cost two days a month instead of half a day, steady-state build cost rises to ~$30,000/year and breakeven moves from ~17 seats to ~60. eshu's lesson is that this burden is controllable — the steady-state password fix and capped naming in the next section are exactly the kind of bugs that, left in, turn half a day into two — but budget it honestly: unmaintained platform code is how builds lose to buys.

The decision rule in one paragraph: under ~12 developers, buy — $40/dev/mo is $5,760/year against a $44,000 build, and you have no platform team to amortize anyway. Past ~20 developers with a platform engineer already on payroll, build the Moniepoint shape — the cash outlay is ~$1,000/year of cluster, the marginal environment is free, and full-stack dev data with realistic seeds never leaves infrastructure you control. Between 12 and 20, the tiebreakers are data sensitivity (regulated fintech data favors own-cluster, which is presumably why Moniepoint built) and workspace intensity (heavy hours favor the build).

The two lessons worth stealing

The post's most reusable section is its challenges, because both failure modes await anyone composing random or composed values through Crossplane — and the fixes are each one sentence.

Lesson one: the 60-second render cycle punishes randomness. Crossplane reprocesses the XWorkload roughly every 60 seconds and reflects any change into the cluster. Eshu's function generates random database passwords — so every render cycle produced new desired state, the credentials changed out from under the running pods, and the environment restarted itself every minute, forever. The fix is a steady-state rule: the function checks the existing state of the resources first and reuses the already-generated password when one is present, so the second render converges instead of churning. Generalize it into a composition-function law: a function that cannot reach a fixed point is a restart loop with extra steps — any generated value (passwords, tokens, randomized ports) must be read back from live state before it is regenerated.

Lesson two: composed names hit the 63-character wall. To let developers reuse simple port and container names without cross-team clashes, eshu composes uniqueness from workload context (workload ID + file + container + port + suffixes). That composition promptly exceeded Kubernetes' 63-character name limit. The fix: cap each part of the composed name at a fixed character length. It is the standard answer, but the failure mode is worth naming because it arrives late — everything works until the first developer with a long workload name and nested references discovers the API server rejecting their pods.

The two jobs that make it production-grade

A demo provisions; a product handles credentials and cleanup. Eshu does both as first-class design, not backlog items.

Credentials flow through HashiCorp Vault's database secrets engine: when the developer runs eshu workload endpoint credential, the CLI asks the control-plane server, which calls the Vault API to mint fresh temporary credentials (via postgresql-database-plugin for Postgres). Resources without a Vault engine still work through the same command — they just return the static credentials generated at provisioning time. One interface, two freshness levels, zero credentials in the Score YAML.

Cleanup is a dedicated reconciler server watching a TTL annotation on each workload: when a namespace outlives its target TTL, the reaper deletes the namespace and the Vault secrets. This is the job every ephemeral-environment demo skips and every production deployment rediscovers — without it, "ephemeral" is just a namespace with a long half-life. Moniepoint built the reaper as part of the system, not as quarter-two hardening.

Measured against the alternatives the post actually claims, the benefits land: simpler setup than compose for unfamiliar stacks (no Postgres version, no resource specs, no probes — sensible defaults), a unified interface across engines that don't natively match, remote execution for heavy apps, and shareable definitions — one workload file can import another, with a centralized catalog as the stated future. That last point is the quiet architectural win: Score YAML in git is already a catalog entry; the platform just hasn't built the index yet.

What to steal even without Crossplane

Three ideas port anywhere. First, Score as the developer-facing contract: developers declare containers, ports, and "I need a database" without target-platform syntax, and the platform translates — the same Score-plus-Crossplane pairing showed up as a KubeCon India 2026 demo, so Moniepoint is riding an emerging pattern, not inventing a snowflake. Second, the unified resource interface: hide init differences (config-map mount vs. post-start job) behind one shape so the tenth backing service costs as little as the second. Third, the TTL reaper as day-one scope: any system that provisions per-developer namespaces without a backstop sweeper is budgeting for a leak.

The through line is ownership economics. A regulated fintech running full-stack dev environments for a large engineering org found that the build — a CLI, one CRD, a four-task function, Vault, and a reaper — beats the per-seat meter past a couple dozen developers, keeps realistic dev data on its own cluster, and turns every environment definition into a shareable artifact. Your team size and maintenance honesty decide whether that math holds for you. Either way, the two lessons — steady-state composition functions, capped composed names — are free.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Remote dev stacks like eshu are cheapest on compute nobody meters: star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex