How I Replaced a $50/Month E-Commerce Stack With a Single 7MB Binary
Six months ago, Nebkiso ran on Saleor. It was the "right" choice: a modern headless e-commerce platform with GraphQL, Postgres, Redis, Celery, Docker, Kubernetes, and Fly.io. It cost $50/month and had twelve services to keep running.
Today, Nebkiso runs on a single Rust binary. It's 7 megabytes, talks to one SQLite file, and lives on a $5/month VPS. The deploy takes three seconds.
Here's why I did it, what I learned, and why you might want to do the same.
The Saleor Stack: Everything Everywhere All at Once
I picked Saleor because it looked like the future. Headless commerce! GraphQL! Django under the hood! It had a shiny admin panel and a React storefront. What's not to love?
Here's what was actually running in production:
- Saleor Core -- the Python/GraphQL backend
- Saleor Dashboard -- the React admin panel
- Saleor Storefront -- the Next.js customer-facing site
- PostgreSQL -- the database
- Redis -- caching and task queues
- Celery -- background workers for async tasks
- Docker Compose -- to orchestrate all of it locally
- Kubernetes -- to orchestrate it in production
- Fly.io -- to host the Kubernetes cluster
Twelve services. For a store selling 25 handmade fragrances.
The problems started immediately. Every deploy meant coordinating migrations across three services. A GraphQL mutation to update a product description required tracing through six layers of abstraction. When something broke -- and things broke often -- I'd spend twenty minutes just figuring out which service was down.
The final straw was a Redis outage. Redis went down, Celery stopped picking up tasks, webhook processing froze, and orders stopped syncing. I fixed it in two hours. Two hours of debugging a cache layer for a store that gets maybe ten visitors a day.
I was maintaining infrastructure for a traffic volume that a Raspberry Pi could handle. The complexity-to-revenue ratio was completely inverted.
The Rewrite: What I Actually Needed
I sat down and listed what Nebkiso actually does:
- Show products with descriptions and prices
- Let people add things to a cart
- Accept payments via Stripe
- Show me pending orders in a simple admin panel
- That's it. That's the whole product.
I didn't need GraphQL. I didn't need Redis. I didn't need twelve services. I needed a web server, a database, and Stripe integration. Everything else was noise.
Here's what the new stack looks like:
| Concern | Before | After |
|---|---|---|
| Backend | Python/Django/Saleor | Rust/Axum |
| API | GraphQL | Server-rendered HTML + a few POST endpoints |
| Database | PostgreSQL | SQLite |
| Cache | Redis | Nothing. SQLite is fast. |
| Background jobs | Celery | Synchronous webhook handlers |
| Frontend | Next.js/React | Askama templates + HTMX |
| Styling | CSS-in-JS | Compiled Tailwind |
| Orchestration | Docker + Kubernetes | systemd |
| Hosting | Fly.io | $5 Vultr VPS |
| Binary size | ~800MB (all containers) | 7MB (one binary) |
| Monthly cost | $50+ | $5 |
| Services | 12 | 3 (nginx, binary, fail2ban) |
Why Rust + SQLite + HTMX?
Rust gives you a single static binary. No runtime, no virtual environment, no dependency hell. You build it, you copy it to the server, you run it. The compiler catches mistakes that Python would let silently ship to production.
SQLite is the correct database for this scale. It's zero-configuration, zero-maintenance, and faster than any network-attached database for single-server workloads. With WAL mode enabled, reads never block writes. Backups are literally cp nebkiso.db.
HTMX lets you build interactive UIs without writing JavaScript. Click "Add to Cart" → server returns updated HTML fragment → HTMX swaps it in. No state management, no API layer, no client-side routing. The server is the source of truth, always.
The combination is brutally efficient. Page loads are instant because the server is rendering templates and shipping HTML directly. There's no hydration, no bundle splitting, no "time to interactive" because the page is interactive the moment it arrives.
The Admin Panel: Security Through Simplicity
The most controversial choice: no authentication system. The admin panel lives behind an SSH tunnel.
ssh -L 3000:localhost:3000 tiny-box
# browse to http://localhost:3000/secret-admin-path
This means zero auth code to maintain. No password resets, no session management, no JWT tokens, no OAuth. The security boundary is SSH -- which is battle-tested, audited by thousands of organizations, and already running on every server.
The admin path is a random string (the kind you'd find in a UUID), so even if someone could reach the server, they'd need to guess a 32-character hex path. Combined with nginx returning 404 for the admin path on the public internet, it's effectively invisible.
What I Lost (and Don't Miss)
GraphQL playground: I used it twice. Both times to debug problems GraphQL itself created.
The React storefront: It was beautiful. It also loaded 2MB of JavaScript to render a product grid. My new site loads in under 100ms with 20KB of CSS.
Multi-tenant architecture: Saleor supports multiple stores, channels, warehouses, and fulfillment centers. I have one store. I'm the fulfillment center.
The dashboard: Saleor's admin panel has approximately 400 knobs. I needed eight: products, variants, orders, status, labels, BOM, import, guestbook. My admin panel has exactly those eight things.
What I Gained
Deploy in three seconds: ./deploy/deploy.sh tiny-box builds the binary, rsyncs it to the server, runs migrations, and reloads nginx. If something breaks, I revert the binary in the time it takes to type mv.
Understand everything: The entire codebase is ~5,000 lines of Rust across 70 files. I can hold the whole system in my head. When something breaks, I know exactly where to look because the feature-based directory structure maps directly to URLs.
Zero ops overhead: No database upgrades, no cache invalidation, no container registry, no Kubernetes YAML drift. The server has been up for 121 days. I've spent exactly zero hours on infrastructure in that time.
It's fun again: I spend my time writing fragrance descriptions and tweaking the atelier, not debugging Celery workers or waiting for Docker builds.
When You Should Do This
If you're building a small-to-medium web app and you're reaching for the "modern" stack, ask yourself:
- Do you need horizontal scaling, or are you just copying what big companies do?
- Could a single server handle your traffic for the next two years?
- Are you spending more time on infrastructure than on your actual product?
If you answered yes to any of these, try the boring stack. Rust, SQLite, HTMX, and a $5 VPS. It's not glamorous. It won't get you a conference talk. But it will ship, and it will keep shipping, and you'll sleep better knowing there's exactly one thing that can break instead of twelve.
The best stack is the one you can fix at 2 AM with an ssh command.