Enterprise-level ordering for the small guy.
GrubStack is a subscription SaaS suite for food trucks and small restaurants: online ordering, a self-checkout kiosk, a kitchen terminal and a management dashboard. Every restaurant runs on its own dedicated deployment, and I designed, built and operate all of it.
- RoleFounder & sole engineer
- TimelineSep 2022 – Present
- StatusPreparing for launch
Visit grubstack.app ↗Built for the small guy.
Food trucks and small restaurants usually have to choose between software they can afford and software that's actually modern. I wanted the small guy to have both: affordable order processing built on current technology, with the kind of features normally reserved for businesses that can pay enterprise prices.
GrubStack started three and a half years ago as a prototype, a way to keep building real software while I was looking for work. It never stopped growing. Today it's a full SaaS suite of five apps, with payments and the infrastructure to give every restaurant its own deployment.
Five apps, one platform.
REST API
Python / FlaskPowers every app in the suite, with a payment layer that processes orders through Square or Stripe.
Dashboard
ReactWhere restaurants configure payment integrations, deploy their apps, and manage their subscription.
Ordering app
Next.jsCustomer-facing online ordering, server-side rendered for speed and search visibility.
Terminal app
Next.jsShows cooks incoming orders in the kitchen, refreshed every minute.
Kiosk app
Next.jsSelf-checkout from a tablet or kiosk screen for food trucks and small restaurants.
From sign-up to live.
Subscribing, connecting payments and deploying a restaurant's apps all happen through the platform, so getting a new restaurant live is a guided flow rather than a manual setup.
Sign up & subscribe
The restaurant creates an account on grubstack.app and picks a subscription.
Connect payments
They link their own Stripe or Square account through OAuth, so customer payments go straight to them.
Dedicated deployment
Their apps deploy into their own space in the Kubernetes cluster, each on its own subdomain:
- core-slug.grubstack.app
- api-slug.grubstack.app
- web-slug.grubstack.app
- terminal-slug.grubstack.app
Build menus & go live
Once they add items and menus, their ordering app opens to the public and customers can start ordering.
The life of an order.
Once a restaurant is live, an order moves through the suite from the customer's screen to the kitchen and back. The customer can see where their order is at every step.
Customer orders
On the web ordering app, a status indicator stays on screen while the order processes.
API takes payment
The API charges the card through the restaurant's connected Stripe or Square account.
Kitchen picks it up
The order lands in the core dashboard or the terminal app, depending on the restaurant's setup.
Status updates
The cook or operator moves the order through each stage as it's prepared.
Completion email
When the order's ready, the customer receives a completion email.
One deployment per restaurant.
Most SaaS products put hundreds of customers on one shared copy of the app. GrubStack is dedicated-tenant: each restaurant gets its own set of apps in its own space in the Kubernetes cluster. Each restaurant's apps are isolated from everyone else's, and each deployment can be upgraded on its own.
Releases run through Jenkins, which builds images into a private Docker registry and redeploys each customer's apps with Helm. When I need more capacity, I add nodes to the cluster.
Watches the cluster from outside, so an outage can't take down its own alerts.
Square or Stripe, one codebase.
Restaurants already have a payment provider they trust, so GrubStack works with two of the most widely used. Each restaurant connects its own Stripe or Square account through OAuth, and customer payments go directly to them.
Behind that is an abstract payment class: the rest of the API asks it to charge an order, and a Square or Stripe implementation does the work. Adding another provider would mean writing a new implementation rather than reworking the ordering flow.
Taming database changes.
Early on, I maintained the database structure by hand. That worked for one deployment, but it doesn't scale to one per restaurant: every manual change is a chance for two databases to drift apart.
I moved to Flyway, so every schema change is now a versioned migration tracked alongside the code. Each deployment applies the same migrations in the same order, and I always know exactly which version a database is on.
Keeping it running.
Uptime Kuma runs on a separate external server and keeps watch over the cluster, so if the cluster has a problem, the monitoring isn't affected by it. I keep the server stack patched, write and maintain the software documentation, and render the public website and ordering app on the server for speed and search visibility.
Card readers, then launch.
I'm currently integrating Stripe and Square card readers so restaurants can take in-person payments through GrubStack too. Once that's finished, I'm ready to start selling it. The goal hasn't changed: be the most affordable option, while offering enterprise-level features to businesses that couldn't otherwise afford them.
What I'd do differently.
I'd plan it like a team project from day one. Because GrubStack was my own project, built in my free time with no deadlines, I tracked the work in my head. A Kanban board or Jira with a real backlog would have made priorities clearer and the path from prototype to product shorter.
Have a role or project in mind?
I'm open to full-time roles, contract work, or just a conversation about what you're building.