I've been quietly building server applications a certain way for almost a decade. Last month I finally gave the pattern a name.
I call it the Celenite Stack. The "C" is intentional — it's for C++. The rest is a deliberate swap of the first letter of Selenite, a foundation crystal named after Selene, the moon goddess. Cute? A little. But the name encodes the philosophy: the C++ core is the foundation, not an add-on.
Eight production applications use this pattern today. They protect 30+ domains. They power a 14,700-station internet radio directory. They serve real users at real load on $600/month worth of bare-metal. They do all this without a single Kubernetes operator, without a queue broker, without a sidecar of any kind.
Here's what the pattern is, why I chose it, and where it falls short.
What everyone else does
The default 2026 stack for "build a web application with an admin UI" looks like this:
- Node.js or Python or Go for the API layer
- React for the admin UI
- nginx in front for TLS
- A separate message broker (Redis, RabbitMQ, Kafka)
- A separate background worker process
- A separate metrics sidecar
- A separate log shipper
- A separate auth service
- Containers for each one
- An ingress controller in front of those
- A service mesh between them
- A CI pipeline that's longer than the code
The container image weighs 500 MB to 1 GB. Cold start is six seconds. The Helm chart has 14 sub-charts. To deploy a one-line change, six things have to roll. The on-call rotation needs three engineers because no one understands the full surface area.
I'm not strawmanning. This is the median enterprise PHP/Node/Python application I've worked on for fifteen years.
There is a reason this stack exists. Microservices solve real organizational problems — they let independent teams ship independently. But most software isn't built by independent teams. Most software is built by one person, or three people who sit in the same office, or a team of eight that meets every morning. For those teams — for the team I am, which is one person — the microservice tax is enormous and the benefit is zero.
I went the other way.
The Celenite Stack
One compiled C++17 daemon owns the entire application server. It does HTTP, it does TLS, it does authentication, it does business logic, it does the hot path — the part that actually has to be fast. Sitting next to it on a Unix domain socket is a dedicated PHP-FPM pool that handles HTML rendering and the admin dashboard. That's it. That's the whole stack.
┌─────────────────────────────────────────────────┐
│ Compiled C++17 daemon — one binary, one │
│ systemd unit, one config file. │
│ │
│ ├─ HTTPS server (OpenSSL, no nginx) │
│ ├─ Hardware-aware thread pool │
│ ├─ Business logic + hot-path compute │
│ ├─ Authentication + session management │
│ ├─ REST API endpoints │
│ └─ FastCGI → PHP-FPM (Unix socket) │
└─────────────────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────────────────┐
│ PHP-FPM pool, dedicated to this app. │
│ Renders the admin UI. Hot-reloadable. │
│ Consumes the API. Has no business logic. │
└─────────────────────────────────────────────────┘
The C++ daemon terminates TLS itself. There is no nginx in front. The frontend is interchangeable — today it's PHP, tomorrow it could be React or a mobile app or a CLI consuming the same REST API. The C++ core doesn't know or care.
Each application gets its own daemon, its own FPM pool, its own Unix socket, its own systemd unit, its own config file. The total deployable footprint per app is a single compiled binary plus a few PHP files plus a YAML config. The container image is 60–80 MB. Cold start is under a second.
Three apps that use it
I want to keep this concrete because the pattern is easy to describe and hard to evaluate without examples.
Mcaster1YPMan — directory synchronization daemon
Mcaster1YPMan is a directory synchronization daemon. Every hour it pulls a 5 MB XML file from Xiph.org listing every public internet radio station on the network, parses it with a SAX parser, crawls all ~14,700 stations concurrently on a thread pool sized to the host's CPU, verifies each stream is actually playable, sanitizes the metadata, and writes everything into temp tables in MariaDB. When all of that succeeds — and only when it succeeds — the daemon does a single atomic RENAME TABLE swap from temp to live. Zero downtime. The public yp.casterclub.com directory never serves a partial result. If the upstream YP feed is broken that hour, the swap aborts and the previous good data stays live.
All of that pipeline lives inside one process. The atomic swap is the entire point — it cannot be safely split across services without losing the guarantee. The admin UI for monitoring sync runs, browsing the 30-day archive, and triggering manual re-crawls is a few hundred lines of PHP rendering API responses.
A microservices-shaped version of this would have an XML parser service, a crawler service, a verifier service, a writer service, a message bus to coordinate them, and a saga pattern to handle partial failure. The atomic table swap would be impossible. It would also have ten times the operational surface area and worse reliability.
Mcaster1BackDraft — Web Application Firewall
Mcaster1BackDraft is a Web Application Firewall and nginx log analyzer protecting roughly thirty production domains. The same C++ daemon listens on three ports: one receives nginx-proxied traffic, inspects every request against nine threat-scoring rules, logs to MariaDB, and forwards via FastCGI to PHP-FPM. Another port serves the admin dashboard. A third exposes a REST API. Three jobs, one process, one audit log. The dashboard has a real-time threat timeline and a Chart.js heat map. The hot path runs at line rate because it's C++. The dashboard is PHP because PHP is good at HTML.
Mcaster1StackSmith — DevSecOps platform
Mcaster1StackSmith is the most evolved example. It's a DevSecOps platform for Docker, Podman, and Kubernetes management — think Docker Desktop plus Portainer plus Rancher in one binary. Same Celenite core: C++ daemon owns HTTPS, the Docker Engine API, the Kubernetes multi-cluster client, the AES-256-GCM credential vault, and the HMAC-chained audit log. A PHP-FPM frontend renders the admin web UI. A Qt6 desktop application talks to the same REST API the web UI uses. Three frontends, one core. The Qt client isn't second-class because the daemon doesn't know which client is calling.
Yes, it runs great on Kubernetes (in fact, it's ideal for it)
The most common misread of this pattern is "you're against microservices." I'm not. The Celenite Stack is itself a microservice pattern — just one that lives inside the right boundary instead of being fragmented across artificial ones.
Every Celenite app is a single compiled binary plus a dedicated PHP-FPM pool plus a YAML config. That is exactly what a Kubernetes deployment wants:
- Sub-100 MB container images. Faster pulls during rollouts. Faster cold starts during scale-up. Smaller registry footprint. Smaller attack surface.
- TLS terminated by the application. No need for cert-manager + ingress-nginx + a service mesh to get encrypted traffic between pods. The daemon already speaks HTTPS, so in-cluster mTLS is a config flag, not a sidecar.
- Liveness, readiness, and metrics endpoints built in. Standard HTTP probes work out of the box. No sidecar required.
- Stateless by default. The daemon doesn't write to local disk for application state. Configuration comes from a mounted ConfigMap or environment variables. Replicate the pod, scale horizontally, autoscale to zero — all of it works.
- Trivial Helm chart. One Deployment, one Service, one ConfigMap, one Secret. Maybe a HorizontalPodAutoscaler if the workload warrants it. Compare to the typical microservice's 14 sub-charts.
Mcaster1StackSmith is the prime example — same app, two deployment shapes. Today it runs as a standalone systemd service on bare metal at stacksmith.mcaster1.com — one C++ daemon, one dedicated PHP-FPM pool, one config file, one host. The exact same binary, with a different config, runs as a single Kubernetes pod inside the clusters it's managing: the C++ daemon and PHP-FPM as sibling containers in one pod, sharing a Unix domain socket via an emptyDir volume, with the fleet agents that report telemetry from each node as a DaemonSet. Source code untouched between the two modes. The Helm chart is four manifests. The systemd unit is twenty lines. Pick the deployment shape that fits the environment; the application doesn't care which one you chose.
This is the point. A Celenite app is happy as a 50 MB Linux service on a tiny VM, as a Docker container on a developer laptop, as a Kubernetes Deployment in production, or as a single-pod tenant inside a customer's K8s cluster. Mcaster1BackDraft and Mcaster1StreamProxy run in multiple of those shapes simultaneously today. The pattern permits all of them because the daemon is a real, complete server — not a half-service that needs orchestration glue to actually do its job.
Celenite Stack is anti-microservice-soup. It is not anti-microservice.
A microservice with the right boundary, in the right tool, with the right deployment model, is a fine thing. The mistake is treating "microservice" as the goal instead of as one possible shape of the answer. Celenite Stack lets the shape match the problem — standalone binary on a VM, systemd service on bare metal, container on Docker, or pod on Kubernetes. Same code, your call.
Why it works
I'll be direct about what this pattern actually buys you.
Performance where it matters, simplicity where it doesn't. The WAF inspects at line rate. The DSP encoder runs lock-free SPSC ring buffers with sub-millisecond audio latency. The streaming proxy handles 5,000 concurrent listeners on a single Go binary. Those are jobs C++ and Go do well. Meanwhile the admin UI — the part most engineers waste months on — is just PHP rendering HTML. It hot-reloads. It's debuggable in a browser. It's hireable.
Containers are tiny. A typical Celenite container image is 60 to 80 megabytes. The largest one is 100 MB. Compare to a typical Node.js application image at 400–800 MB, or a Spring Boot image at 600 MB to a gigabyte. This matters in real ways: faster pulls, faster cold starts, smaller registry footprint, smaller attack surface.
Process isolation per app, not per concern. Each application has its own daemon, its own FPM pool, its own socket, its own systemd unit, its own database user. When one app crashes, the others don't notice. But within a single app, all the related work runs in one process where shared state is cheap and observable. You get fault isolation without the orchestration tax.
TLS at the application. There is no nginx hop. The C++ daemon owns the socket from accept() to response. Wildcard certs live on disk and are loaded once on startup. Per-listener TLS termination is built into the daemon, not bolted on by a sidecar. This makes per-host MTLS and client-certificate auth straightforward instead of operationally exotic.
Hardware-aware threading. Thread pools are sized to the CPU and available RAM, declared in YAML, applied at startup. No autoscaling layer. The application knows the machine it's running on and adapts. This sounds quaint until you realize how much CPU and memory budget the average cloud-native stack burns running components that only exist to orchestrate other components.
No sidecars. No service mesh. No agent. Logs go to journald. Metrics are exposed by the daemon itself on a /metrics endpoint. Health checks are an HTTP endpoint. The daemon already speaks HTTPS, so there's nothing to bolt on. If the daemon needs to talk to another service, it does so directly. The deployment is a binary and a config file.
What it costs
I'm not going to pretend this pattern is free.
The bus factor is real. I am, currently, the only person who knows the C++ core deeply. Hiring is harder than for a Node.js shop. Onboarding takes longer. If I were building a 30-person product team I would think very carefully about this. For a one-person engineering org, it is the right trade.
Two-runtime debugging. Bugs can cross the C++/PHP boundary. I once spent half a day chasing a thread-pool deadlock that only manifested when the PHP layer made a curl request back into the C++ daemon during a request the daemon was already serving. C++ tooling and PHP tooling don't share traces. The fix is good logging and discipline. The cost is real.
No hot reload on the C++ side. A code change means a recompile and a restart. Mitigated by pushing iteration into the PHP layer wherever the design allows. But if you're cowboy-coding in the daemon, every cycle is a build.
C++ memory discipline is non-optional. Lock-free ring buffers. ASAN/UBSAN clean compiles. QPointer guards. Bounds checks in places you'd never bother in a managed language. The compiler will not save you from a use-after-free. The reward is performance and a small footprint. The price is care.
The hiring conversation is harder. "We're a C++ and PHP shop" gets you a smaller candidate pool than "we're a Python and React shop." But the candidates you do get are excellent, because C++ self-selects for engineers who care about the machine.
When you should not use this pattern
Plenty of cases. I'll name them.
If you're building a CRUD application with no hot path — no real-time anything, no high concurrency, no DSP, no protocol implementation — the Celenite Stack gives you nothing a Rails or Django app couldn't. Use the framework you already know.
If you're an organization where multiple teams need to ship independently against a shared platform, microservices are organizational scaffolding, not technical architecture. Use them. Just understand that's the actual trade.
If you're building something that has to scale to thousands of nodes horizontally and the workload is shardable, you'll outgrow a single-binary model. Spotify and Stripe shouldn't run on Celenite Stack. Your fifteen-user internal tool probably should.
If you can't hire C++ engineers and don't want to be one, the pattern doesn't fit. There's no shame in that. Build with what your team can ship.
What I want you to take away
The industry told us for a decade that the only way to build modern software is to fragment it across services and orchestrate the fragments with another layer of software. I rebuilt my entire ecosystem on the opposite assumption — that a compiled core with a scripted frontend, with no sidecars and no service mesh, is a perfectly modern way to ship — and I haven't missed anything I gave up.
The Celenite Stack runs my Web Application Firewall, my streaming media server, my YP directory daemon, my multi-codec broadcast encoder, my container management platform, my chat server, and my email infrastructure. Eight production applications. ~650,000 lines of code. One operator. $600/month of hosting.
It's not the only way to build software. It's a way. It's mine. I'm putting a name on it because I think the rest of you deserve to know that the choice exists, that the trade-offs are explicit, and that it works in production at non-trivial scale.
If you build with this pattern — or want to — find me on LinkedIn or at davestj@gmail.com. I'm always interested to hear about other people choosing the boring, durable path.
Stop theorizing. Start building.