What is your developer-dependent CMS actually costing you? Calculate your costs and get a full report

Agility CMS

CBI Health + Agility Concierge: From a 7-Hour Publish Cycle to Live in Seconds

CBI Health brought website development in-house and inherited a static Nuxt.js build that had grown to 25 minutes, with a publish cycle measured in hours. Agility Concierge paired the Agility platform with hands-on engineering: we helped plan the move in-house, moved the site to server rendering on Vercel, then tuned the 2020-era framework for modern caching alongside CBI's developer. Content now goes live in seconds, builds take about a minute, and the site's CMS API bandwidth is down 98.9%.

Health CareLegacy SystemModern Website and SEOCBI Health ↗
The CBI Health homepage, served from Vercel's edge cache
CBI Health

At a glance

CustomerCBI Health, which describes itself as Canada's largest community healthcare provider. Bilingual (English and French) website of about 2,500 pages, on Agility since 2020.
Previous stackNuxt.js 2, fully statically generated, hosted on cloud storage behind a CDN, deployed by a manually approved pipeline.
Stack todayThe same Nuxt.js 2 code, server-rendered on Vercel, with Agility, edge caching and cache purges triggered by publishing.
The problemContent took a median of about 7 hours to reach the site. Builds took 25 minutes or more. Development was moving from an agency to a one-person in-house team.
What Agility didThe Agility platform plus Agility Concierge, led by Agility CTO Joel Varty and coordinated by Agility President Jina Zohori. We helped plan the move to in-house development, moved the hosting, rebuilt the caching, measured everything, and did the work in CBI's own repository alongside its developer.
ResultsContent live in seconds. Builds in about 70 seconds. 98.9% less CMS API bandwidth. Server renders 58% faster. No rewrite.

Sound familiar?

If you recognize your own site in any of these, this story is for you.

  • Your editors publish, then wait for a rebuild. We moved CBI to server rendering, with cache purges driven by each publish. Content is now live on the next page request.
  • Your builds get longer every year. Pages now render on demand instead of all at once at build time. Builds dropped from 25+ minutes to about 70 seconds.
  • You're taking development in-house from an agency. Agility Concierge helped plan the handover, then worked in CBI's own repository alongside its developer and left documentation the team owns.
  • Your CMS API usage or costs jumped after a hosting change. A shared data cache, compression and fixes for cache-splitting URLs cut CBI's API bandwidth by 98.9%.
  • Your ad campaigns land on slow, uncached pages. Clicks with tracking parameters are now served from the cached page at the edge.
  • You've been told that modernizing means a rewrite. CBI kept its 2020 Nuxt.js codebase. Everything around it changed.

For marketing and digital leaders

This part covers what changed for the people who run the site day to day. Developers can skip ahead to the technical story further down.

2020: a static site that made sense

An agency partner built the site in 2020 with Nuxt.js and Agility. Every page was generated ahead of time as plain HTML and served from a CDN. For a site of that size at the time, it was a sound choice: fast, cheap to host, and a full build took only a few minutes. The trade-off looked small. Content reached the live site only after a rebuild.

From "a few minutes" to a 7-hour wait

Every page added makes a static build longer, and the site kept growing: more locations, more services, a second language, a blog, careers. By 2026:

  • The production build took 23 to 25 minutes just to generate the pages, and about 40 minutes for the test environment.
  • Uploading the result and clearing the CDN added another 20 to 45 minutes.
  • Each release waited for a manual approval. From April to June 2026, the median time from a change to the live site was about 7 hours.

For editors it was worse. Publishing in the CMS put nothing on the website. A typo fix, a clinic's holiday hours and an urgent notice all waited for the same daily, manually run build. If it failed, they waited another day. The pipeline also depended on a single self-hosted build machine.

Bringing development in-house, with Agility Concierge

CBI wanted to own its website development rather than rely on an agency. Agility helped coordinate that move. Joel Varty, Agility's CTO, worked with CBI to understand where its technology needed to go and helped plan the handover from the agency that had built and maintained the site for five years to a dedicated in-house developer.

The developer shipped new templates, forms and campaign pages from the start, but every change still went through the same slow pipeline. CBI didn't need a new website. It needed the one it had to stop slowing the team down, and a one-person team needed more than a support ticket could give it. So Agility Concierge stayed with them for the next step.

What changed for the business

  • Editors are back in control. A publish in Agility is live on the next page request. Redirects, which the marketing team manages in the CMS, go live in about a minute, with no developer involved.
  • Less risk. The old and new sites ran side by side, with a written test plan, until CBI chose to switch. If the site's servers ever fail, cached pages keep serving for up to seven days.
  • Search in both languages. Every English and French page now ships its title, description and social tags in the page itself, where search engines read them. Ad clicks land on cached, fast pages.
  • A leaner footprint. The site's CMS bandwidth is on track for about 105 GB a month, down from 2.3 to 2.5 TB in July and August.
  • The investment is protected. No rewrite. CBI's developer works in the same code and templates as before.

How it unfolded

  1. 2020: an agency partner launches the site as a static build.
  2. 2025: CBI brings development in-house, with Agility helping coordinate the handover from the agency.
  3. Spring 2026: builds take 25+ minutes, and a change takes a median of about 7 hours to go live.
  4. May 2026: Agility builds both options, an optimized static build and server rendering on Vercel, as working versions CBI can compare. Redirects move to the edge.
  5. June 17, 2026: the site switches to Vercel, with the old setup kept as a fallback.
  6. August 2026: Agility Concierge spots a roughly fourfold jump in CMS API bandwidth in Agility's own telemetry and brings CBI a plan.
  7. September 29, 2026: we freeze a baseline of the numbers, then build and verify the caching changes on preview sites.
  8. October 7, 2026: CBI's developer merges the caching release.
  9. October 8 to 11, 2026: first full days of results, measured in production.

What didn't go to plan, and how we fixed it

  • In testing, French pages rendered with the English header and footer. A small routing fix now sets the language before the page renders.
  • A language cookie on every response stopped the CDN from caching anything. We removed it; the URL already says which language a page is in.
  • Server rendering roughly quadrupled the site's CMS API bandwidth. Fixing that became the second phase of the project.
  • Publishing the homepage had never cleared the live homepage from the CDN, so it could stay out of date for up to two days. The caching release fixed it.
Seconds
From CMS publish to live site, down from a median of ~7 hours
~70s
Production build time, down from 25+ minutes
98.9%
Less CMS API bandwidth (same weekdays, first days after release)
92%
Fewer CMS API calls per day
58%
Faster average server render

I talked with members of the team at CBI and heard their frustrations with their processes. That wasn't acceptable to me. I'm so happy that we were able to work together to solve it with them.

Joel Varty

CTO, Agility

CBI Health — agility-diagram-cbi-api-bandwidth.svg
CBI Health — agility-diagram-cbi-publish-to-live.svg
CBI Health — agility-diagram-cbi-build-time.svg
CBI Health — agility-diagram-cbi-request-path.svg
CBI Health — agility-diagram-cbi-cache-holes.svg
CBI Health — cbi-health-screenshot-homepage-edge-cache.png
CBI Health — cbi-health-screenshot-ad-click-cached.png
CBI Health — cbi-health-screenshot-french-seo.png
CBI Health — cbi-health-screenshot-blog-cached-lists.png
CBI Health — agility-diagram-cbi-tech-plus-concierge.svg
CBI Health — agility-diagram-cbi-concierge.svg
CBI Health — agility-diagram-cbi-timeline.svg

For developers and architects

This is the engineering story: what we changed, why, and the problems you are likely to hit doing the same thing on an older framework.

Before and after

LayerBeforeAfter
FrameworkNuxt.js 2 (Vue)Nuxt.js 2, unchanged
RenderingFull static generate, ~2,500 pages per environmentSSR in a single Vercel Function running Nuxt's own server
Hosting and CDNCloud storage plus CDN, purge-all on every deployVercel CDN, per-page purge by cache tag
Publish to liveNext build plus manual approval (~7 h median)Agility webhook to tag invalidation (seconds)
Build23–25 min generate step (about 40 min for UAT)About 70 s
RedirectsCDN configurationEdge middleware generated from Agility, with loop detection
CMS data at runtimeUncompressed, no reuse between requestsVercel Runtime Cache, gzip, 5-minute TTL, purged on publish

Request path: visitor, edge cache, render, shared data cache, Agility, with publish purging both caches

Step one: server rendering on Vercel

We built both options as working branches: an optimized static build, and server-side rendering on Vercel. SSR won, because builds stop scaling with page count and content stops waiting for a build.

Nuxt 2 has no first-class adapter for SSR on Vercel, so we wrote a small serverless renderer that runs Nuxt's production server (loadNuxt('start')) inside one Vercel Function behind a catch-all rewrite. Every response is tagged for purging and carries separate browser and CDN cache policies:

// api/index.js (simplified)
res.setHeader('Vercel-Cache-Tag', `path:${path},site`)
res.setHeader('Cache-Control', 'public, max-age=0, must-revalidate')
res.setHeader('CDN-Cache-Control',
  'max-age=86400, stale-while-revalidate=86400, stale-if-error=604800')

The browser always revalidates, so a purge is visible right away. The CDN keeps a page fresh for a day, serves it stale while it refreshes for another day, and serves the last good copy for up to a week if the function errors. An Agility webhook resolves each publish to its path and calls invalidateByTag. Shared content (navigation, footer, settings) invalidates the site tag, which refreshes pages lazily as they are visited. The 1,100+ redirects are pulled from Agility at build time into edge middleware, which detects and drops redirect loops. A redirect publish triggers a deploy hook, so redirect changes go live in about a minute.

Step two: caching a 2020 framework for 2026

After the cutover, each cache miss rendered a page that made about 19 CMS API calls and pulled about 2 MB of uncompressed JSON, mostly the full sitemap for both languages. Nothing was shared between requests or server instances. We changed five things:

  • Compression. Server-side calls now request gzip. In production, the largest response (the English sitemap) shrank 84%, from 601 KB to 99 KB per request.
  • A shared data cache. Sitemaps, pages and lists are served from Vercel's Runtime Cache, shared by every instance, with a 5-minute TTL on the live API and no cache on the preview API. In testing, a warm render made zero API calls. In production, each shared list is now refreshed about once every five minutes, and the cache answers about 300,000 lookups a day.
  • Purge order. On publish, the webhook clears the data cache before the HTML, so a background refresh can never re-cache stale data for a day:
// api/revalidate.js (simplified)
await getCache({ namespace: 'agility' }).expireTag('agility') // 1. CMS data
await sleep(350)                                              // 2. let it propagate
await invalidateByTag(`path:${path}`)                         // 3. then the HTML
  • Canonical cache for tracking parameters. Vercel's cache key includes the full query string, so every ?gclid= or ?utm_source= click was a miss. Edge middleware strips about 20 known tracking parameters, fetches the canonical URL from the CDN and returns it under the original URL, so analytics still sees the parameters. Non-GET requests get a 405 without rendering.
  • A same-origin proxy for browser calls. Client-side list fetches now go through a CDN-cached /api/agility proxy at the link depth the components actually use. The main blog-list call went from about 986 KB to about 20 KB, both gzipped, and blog-list traffic as a whole fell from 3.7 GB a day to 0.04 GB.

The whole cache can be switched off with one environment variable (AGILITY_CACHE_TTL=0). Before release, we checked that every page in the test set rendered identical HTML with the cache on and off.

Gotchas worth knowing

  • One Set-Cookie disables CDN caching. nuxt-i18n's browser-language cookie made every page a miss. We turned off detectBrowserLanguage; the URL decides the locale.
  • The CDN stores one copy per exact Accept-Encoding string. Chrome and Safari send different strings, so a hit for one is a miss for the other. Don't trust a curl HIT as proof that browsers hit.
  • The file tracer can't see Nuxt 2's runtime requires. nuxt build --standalone plus a never-executed block of static require()s fixed the cascade of "Cannot find module" errors.
  • Purge the path the page is actually served on. Agility's homepage path is /home, but it is served at /, so both need purging.

The results

  • Content goes live in seconds, on the next page request after publishing, down from a median of about 7 hours.
  • Production builds take about 70 seconds, down from a 23–25 minute generate step.
  • 98.9% less CMS API bandwidth: about 79 GB a day down to 0.88 GB, comparing the same weekdays. Across API and media together, bandwidth fell 96%, to a run rate of about 105 GB a month.
  • 92% fewer CMS API calls: about 300,000 a day down to about 23,000.
  • 58% faster server renders: 883 ms to 370 ms on average, and 1.14 s to 0.46 s at the 75th percentile.
  • More resilient: cached pages keep serving for up to seven days if the origin fails.

How we measured

  • API calls and render times: Vercel production metrics for the site, September 22 to October 6, 2026, compared with October 8 to 11, 2026.
  • Bandwidth: Agility's own API logs, comparing October 8 to 10 with the same weekdays (September 24 to 26) in a baseline week that was frozen with a SHA-256 manifest before the release, so it couldn't change afterwards.
  • Builds: the median of the last 100 production deploys (August 31 to October 9, 2026). Old pipeline timings come from its CI history, April to June 2026.
  • Payload sizes: per-request sizes from Agility's logs, cross-checked against a repeatable test harness that stays in CBI's repository.
  • These are the first days after release. We'll update them with a full week of data.

Technology plus Concierge

The platform made the solution possible: Agility's webhooks, APIs and image CDN, running on Vercel's edge. Agility Concierge made it happen, working hands-on alongside CBI instead of through a ticket queue.

The people behind it

  • Joel Varty, CTO, Agility. Coordinated the engagement from the start: learning CBI's technology needs as they evolved, helping plan the move to in-house development, and leading the technical work on the move to Vercel and the caching release.
  • Jina Zohori, President, Agility. Oversees customer success and experience at Agility. Quarterbacked this project and set the timing of every step, from the cutover to the caching release, around CBI's own schedule.

What Concierge meant for CBI

  • We came to them. Agility's own monitoring spotted the rise in API traffic, and we brought CBI the analysis and a plan before they had to go looking.
  • Options, not opinions. We built both candidate architectures as working branches, so CBI could compare real results before choosing.
  • Their repository, their pace. Every change went into CBI's own repository as a pull request with a preview deployment. We timed each merge with CBI's developer, who kept shipping features throughout.
  • A safety net at every step. Old and new stacks ran side by side with a written test plan until CBI was ready to switch. The caching changes ship with an off switch.
  • Proof, not promises. A frozen baseline and a test harness mean every number here can be checked independently.
  • We flagged what we found. Redirect loops, redirects hiding live pages, a leftover sync job from the old pipeline, oversized icons: we raised each one, fixed what was ours to fix, and asked CBI about the rest instead of guessing.
  • Documentation they own. Architecture, caching, operations and a handoff guide live in CBI's repository, so its team can run and change the site on its own.

Lessons for teams on a legacy framework

  • Static generation has a scaling curve. A build that takes minutes at launch can take most of an hour a few years later. If editors are waiting on builds, the architecture is costing you more than the hosting is.
  • You don't have to rewrite to modernize. With a thin adapter layer, an older framework can run on modern serverless hosting and edge caching.
  • Moving to SSR changes your traffic profile. Cache your CMS data on the server, compress it, and hunt down anything that splits your CDN cache.
  • Choose a platform that comes with people. A small in-house team can run a big site when its vendor will work in its code, measure the result and leave the team able to run it.