Skip to content

Case study

Menumill: a multi-tenant SaaS platform I co-founded, built and ran for 16 years.

I co-founded Menumill with Tom Beck and launched it in 2009 as a content management platform that hosted and managed hundreds of websites for everyone from single restaurants to multi-location chains. I designed the architecture and the data model, wrote most of the code, and ran it for 16 years alongside full-time engineering work. Its anchor client, Zippy's, ran the menu boards and digital signage in every restaurant on Menumill for 15 years, 25 locations at its peak. When I retired the platform in 2024, every remaining client kept a working site.

Menumill
designed, built and run
16 yrs
of websites on the platform
100s
in-store screens across 25 Zippy's restaurants
250+
with the anchor client
15 yrs

What was Menumill?

Menumill was a content management platform for hosting and managing websites. Our tagline was "simple website management": if you could write an email, you could manage your website. Clients started with restaurants and grew to include property managers, builders, photographers, resorts, a music festival and professional athletes. Designers controlled every line of HTML, CSS and JavaScript through Liquid templates, which we had used since early 2008, and one theme could serve all of an agency's client sites.

Behind that simplicity, one master site could feed a whole network of child sites, and managing the network felt the same as managing a single site. One client stayed for nearly 16 years, from January 2010 to October 2025.

Sites and screens that ran on Menumill

How did Menumill run more than 250 in-store screens?

Zippy's started in 2009 with a few screens in a handful of restaurants. Every screen showed a web page served by Menumill, and the screens did two jobs:

  • Menu boards: the menu itself, built live from the records for each menu item and its variations, with their sizes, prices and nutrition facts. When a price changed in the database, every board that showed that item changed with it.
  • Digital signage: promotions, counter and dining room specials, and seasonal campaigns, rotating through playlists and coming on and off on schedule.
A row of Zippy's digital menu boards above the counter, showing the breakfast and plate lunch menus.
Zippy's menu boards above the counter, each one a web page served by Menumill.

Zippy's kept adding to it. Promotions came after the menus, then more screens, then more restaurants, until every Zippy's restaurant ran its menu boards and digital signage on Menumill: 25 locations at its peak, with 10 to 12 screens each. If the platform went down, their menus went dark during service, so it was built to stay up:

  • Schedules set by the teams that own them. Operations set the dayparts once, so breakfast, lunch and evening menus switched at the same time in every restaurant. Marketing scheduled promotions weeks ahead, with a start date and an end date. Schedules could repeat daily, weekly, monthly or yearly.
  • Nothing to do in the restaurants. Once a schedule was set, every screen changed on its own.
  • Changes in moments. One counter per site cleared its page cache and told its screens to refresh over WebSockets.
  • Screens that ride out outages. Each screen's page shipped with an offline manifest listing every image, script and stylesheet it needed. If the network dropped, the menu stayed on screen.

Menumill ran Zippy's in-store screens for 15 years, until they retired them in 2024.

How do you share data across locations without losing control?

We designed master and child sites with Zippy's for exactly this. It was a big part of why every Zippy's restaurant ended up on Menumill. The structure was part of the data model: a site could be the master for any number of child sites, and each child inherited the master's content types, menus, promotions, schedules and templates.

  • Inherited by default. The head office changed a menu or a promotion in one place. A nightly sync, or a push on demand to one restaurant or all of them, carried the change everywhere. Opening a new restaurant meant creating a new child site, populated from the master in the background.
  • One menu, local prices. Every restaurant shared the same templates and the same menu records. Each site's own settings told it which market's prices to show when a page rendered, so one shared menu showed the right prices in every restaurant.
  • Clear ownership. Records that came from the master were reset at every sync. A restaurant could clone a menu item into its own copy, and sync never touched anything a restaurant created for itself. A manager could change that day's specials, and the next morning every restaurant started from the head office's menu.
  • Incremental sync. From 2011 the sync copied only what had changed since each restaurant's last sync.

The same questions come up whenever organizations share records: who owns a record, who can change it, and what happens when they disagree.

How do you let every customer shape its own data?

In 2010 I replaced the fixed content types (pages, menus, events and the rest) with types each site defined for itself:

  • fields of 13 types, such as text, money, dates and times, dropdowns, images and links to other records
  • relationships that can be queried from both sides
  • ordered collections, such as a menu that holds sections that hold menu items

A new type needed no database change, and the rewrite cut the application code by about a quarter.

That flexibility let one platform serve very different businesses. A property manager ran a site for each of its buildings. A builder showed its portfolio.

Each Zippy's restaurant was two businesses under one roof: a fast food counter ordering from menu boards, and a sit-down dining room with its own menu and daily specials. So one menu item could carry a different price for each size, for the counter and the dining room, and for each market. The data model handled it: every menu item had a variation for each size and service format, and each variation carried its own prices by market, its own nutrition facts and the SKU from the chain's point-of-sale data. A menu-wide price change arrived as one file. It was matched by SKU and applied in bulk, with a report of anything that didn't match.

The same platform ran a 25‑restaurant chain's menu boards, a property manager's rental listings and a professional skier's website.

How do you retire a platform responsibly?

When Zippy's retired its screens in 2024, I wound the platform down one client at a time:

  1. I crawled each remaining site from the sitemap the platform generated, so pages outside the navigation came across too.
  2. I versioned each site in Git and redeployed it as static HTML.
  3. I moved each site over by DNS.

The hosted platform was switched off in November 2024 after a final database snapshot. Every remaining client kept a working site.

What carried forward?

  • Templates and permissions at Fluxx. When I joined Fluxx, its templates were hard-coded. I introduced Liquid there, along with the user roles and permissions patterns I had built for Menumill.
  • Multi-tenancy and data modeling. Menumill kept tenants apart in its application code. Today I enforce tenant isolation in the database itself, so every screen, API and AI query follows the same rules.
  • Testing. I built the 2010 rewrite with test-driven development from its first weeks. Today every feature also starts as a written spec, and its use cases become automated tests before the code is written.

See how I build platforms today.

Building a platform or a product?

Let's talk it through. A free 30-minute call to see how 30 years of building and running platforms can help yours.

Book a 30-minute call