Sales Demo Environments: Curated Data Without Production Risk
GuideSeptember 29, 20269 min read

Sales Demo Environments: Curated Data Without Production Risk

The Short Version

A sales demo has two ways to go wrong that look like opposites but come from the same underlying problem: demoing against real production data, which risks a genuinely awkward moment if a prospect happens to see another customer's information, or demoing against a stale, hand-maintained dataset that hasn't been refreshed in months and visibly looks fake the moment a sharp prospect notices the same three customer names in every demo they've ever seen from this vendor. The fix is the same seeding discipline used for E2E testing, applied to a different goal: data curated specifically to look good and tell a coherent story, refreshed on a schedule, isolated entirely from anything real.

Why Production Access Is the Wrong Default for Demos

The appeal of demoing against production is obvious: the data is real, the numbers look genuinely impressive, and nobody has to maintain a separate dataset. The risk is equally real and considerably worse than it looks on paper. A sales engineer clicking through a live demo, live, in front of a prospect, is one wrong click away from surfacing another customer's actual name, email, or account details, which is not a hypothetical, it is a routine occurrence anywhere this pattern is common enough to have been normalized. Beyond the immediate embarrassment, it is very often a genuine data-handling or contractual violation, since most customer agreements do not anticipate their data being shown to unrelated third parties during a sales call.

Why a Stale Manual Dataset Is the Wrong Fix

The instinctive alternative, a manually built demo dataset created once, is safe from the production-exposure risk and creates a slower-moving problem instead: it goes stale. A prospect who has evaluated more than one vendor in this space has likely seen several demos with the exact same three fictional customer names and the exact same sample data, since nobody ever got around to refreshing it after the initial setup. A demo dataset that looks visibly unchanged for a year reads as low effort at best and as a sign the product itself is not actively maintained at worst, an impression a sales team can't afford during exactly the moment they're trying to make the opposite impression.

The Actual Pattern: A Dedicated, Long-Lived, Regularly Reset Environment

A sales demo environment is a type: primary environment (long-lived, not ephemeral), running continuously on a stable, branded URL, whose data is generated by the same kind of deterministic seed script used for E2E testing, just tuned for narrative quality instead of edge-case coverage:

YAML
1kind: Environment
2name: sales-demo
3type: primary
4
5components:
6  - kind: Database
7    name: postgres
8    dockerCompose:
9      environment:
10        POSTGRES_PASSWORD: SECRET['demo-db-pass']
11
12  - kind: GenericComponent
13    name: demo-seed
14    dependsOn: [postgres]
15    runnerImage: 'postgres:15-alpine'
16    deploy:
17      - 'psql -h postgres -U postgres -d app_demo -f /seed/demo-narrative.sql'
18
19  - kind: Application
20    name: web
21    hosts:
22      - hostname: 'demo.yourcompany.com'
23        servicePort: 3000
24        selfManagedDns: true

The seed script here is written deliberately differently from an E2E test's seed data. A test seed prioritizes edge-case coverage (the suspended user, the empty-state account); a demo seed prioritizes a coherent, impressive story: a handful of realistic-looking customer accounts with sensible names and logos, order histories that show growth trends worth pointing at, and dashboards that render meaningful charts rather than empty placeholder graphs. See Seeding Realistic Database Data for E2E and QA Testing for the underlying mechanics this reuses; the difference here is purely in what the seed content is optimized to demonstrate.

Resetting on a Schedule, Not on Request

The critical operational discipline that keeps a demo environment usable indefinitely is resetting it automatically, on a fixed schedule, rather than waiting for someone to notice it's gotten messy from a previous demo (a sales engineer deleted a record to show the delete flow, created a test account with an unprofessional name to check a feature, or simply accumulated clutter over several weeks of use).

YAML
1# Scheduled nightly reset
2on:
3  schedule:
4    - cron: '0 6 * * *'  # 6 AM UTC, before most demos of the day
5
6jobs:
7  reset-demo-data:
8    runs-on: ubuntu-latest
9    steps:
10      - run: |
11          bns environments update --id <SALES_DEMO_ENV_ID> \
12            --restart-component postgres --restart-component demo-seed

A nightly reset means every sales engineer, in every timezone, starts each working day with the identical, known-good demo state, regardless of what happened in yesterday's demos. This removes an entire category of "someone else's demo broke my demo" friction that a shared, unreset environment accumulates over time.

Isolating the Demo Environment From Everything Else

A demo environment should have no network path to production or to any other environment, for the same reason a fork-PR environment does: it is a system a sales engineer, and by extension anyone watching over their shoulder on a screen share, interacts with directly, and it should not be able to reach anything more sensitive than its own curated, disposable dataset even if something in the demo flow goes wrong.

YAML
1security:
2  access:
3    allowedIps:
4      - '0.0.0.0/0'  # Publicly reachable is often intentional for a demo URL

Unlike a fork-PR environment, a sales demo environment is frequently meant to be publicly reachable, since prospects sometimes revisit the demo link between calls or share it internally within their own organization. The isolation that matters here is not network reachability, it's making certain the demo environment's database, secrets, and any exported variables are entirely separate from production's, so a publicly reachable demo URL never becomes a path to anything with real stakes behind it.

A Stable, Branded URL

Unlike an ephemeral PR environment where the auto-generated subdomain is a feature (zero configuration, guaranteed uniqueness), a demo environment benefits from a stable, memorable, branded domain, since it gets shared in emails, typed from memory on a call, and revisited by the same prospect multiple times over a sales cycle:

YAML
1hosts:
2  - hostname: 'demo.yourcompany.com'
3    servicePort: 3000
4    selfManagedDns: true
5    k8s:
6      ingress:
7        tlsSecretName: ''
8        annotations:
9          cert-manager.io/cluster-issuer: letsencrypt-prod

See Custom Domains and Network Isolation for Staging Environments for the DNS delegation and certificate steps a custom domain like this actually requires, which is a small amount of one-time setup well worth it for an environment prospects are meant to remember and return to.

Saving Cost When Nobody's Demoing

A sales demo environment sitting fully provisioned around the clock, including nights and weekends when no demos are scheduled, spends money for no audience. Stopping the environment during predictable low-activity windows and starting it back up shortly before business hours, rather than running it continuously, cuts its cost without affecting availability during the hours it actually matters:

Bash
$ bns environments stop --id <SALES_DEMO_ENV_ID>   # Friday evening
$ bns environments start --id <SALES_DEMO_ENV_ID>  # Monday morning

See Stop and Start Environments On-Demand for the full mechanics of this pattern, including how it preserves state across a stop/start cycle so the demo doesn't need to re-seed every Monday, only on its regular nightly reset schedule.

Building the Demo Data Around an Actual Script

The most effective demo datasets are built backward from the specific narrative a sales engineer actually walks through, not generated generically and hoped to be useful. If the demo script shows a customer's growth over their first quarter, the seed data should contain an account with exactly that growth pattern already baked in, not a flat, unremarkable dataset the sales engineer has to explain around. Working from an actual outline of what gets shown, in what order, and what each screen is meant to demonstrate, then reverse-engineering the seed rows that make each of those screens look exactly right, produces a noticeably more polished result than seeding first and building the script around whatever came out.

SQL
1-- Demo account showing a clear growth trend across the exact
2-- quarter the sales script highlights
3INSERT INTO accounts (name, plan, created_at) VALUES
4  ('Meridian Logistics', 'growth', now() - interval '95 days');
5
6INSERT INTO orders (account_id, amount, created_at)
7SELECT
8  (SELECT id FROM accounts WHERE name = 'Meridian Logistics'),
9  (500 + (n * 40) + (random() * 100))::int,
10  now() - interval '95 days' + (n || ' days')::interval
11FROM generate_series(1, 90) AS n;

A generated series like this, with amounts that trend upward across the date range rather than being purely random, produces exactly the kind of chart a sales engineer wants to point at without needing to explain away an unconvincing flat line or an unexplained dip.

Handling Multiple Demo Scenarios

Different prospects care about different parts of the product, and a single fixed dataset optimized for one narrative can undersell a feature a different prospect specifically wants to see. Rather than maintaining entirely separate demo environments per scenario, which multiplies the maintenance and reset burden, the same variable-driven approach used for E2E test scenarios works here too: one environment, a small set of named seed variants a sales engineer selects before a call based on what that specific prospect cares about.

YAML
1environmentVariables:
2  DEMO_SCENARIO: growth-story
3
4components:
5  - kind: GenericComponent
6    name: demo-seed
7    dependsOn: [postgres]
8    deploy:
9      - 'psql -h postgres -U postgres -d app_demo -f /seed/{{ env.vars.DEMO_SCENARIO }}.sql'

A sales engineer preparing for a call focused on a specific feature triggers a reseed with the matching scenario shortly beforehand, rather than the team maintaining several fully separate demo environments that each need their own hosting, reset schedule, and URL to keep track of.

Keeping the Demo Application Version Current

A demo environment that gets left on an old deploy while the actual product keeps shipping features creates its own credibility problem: a prospect who later signs up and sees a materially different (better) product than the one demoed either feels misled or assumes the demo was inaccurate, neither of which is the impression a sales team wants to leave. Wiring the demo environment's deploy to track the same release cadence as production, rather than being manually redeployed whenever someone remembers to, closes this gap:

YAML
1# Redeploy the demo environment whenever a new production release ships
2on:
3  push:
4    tags:
5      - 'release-*'
6
7jobs:
8  redeploy-demo:
9    runs-on: ubuntu-latest
10    steps:
11      - run: |
12          bns environments update --id <SALES_DEMO_ENV_ID> --from-path bunnyshell.yaml

This keeps the demo environment's application version within one release of production at all times, without requiring anyone to remember a manual step, while the seed data itself stays on its own independent nightly reset schedule, since application version and demo data freshness are two separate concerns that happen to both need regular attention but don't need to move on the same schedule.

For the seed-data pattern this reuses with a different narrative goal, see Seeding Realistic Database Data for E2E and QA Testing. For the custom domain setup a demo URL benefits from, see Custom Domains and Network Isolation for Staging Environments.

Who Actually Owns This Environment

A demo environment tends to fall into an ownership gap in practice: engineering built it, but sales is the team actually using and depending on it day to day, and neither side may be checking on it regularly by default. Assigning explicit, named ownership, typically a sales engineering lead as the primary point of contact for "the demo looks broken" reports and a specific engineering contact responsible for the underlying template and infrastructure, closes this gap before a demo environment quietly degrades for weeks because each side assumed the other was watching it.

Checklist

  • Demo data is generated by a deterministic seed script, never a live copy of production
  • The seed script is reset on a fixed nightly (or similarly frequent) schedule, not only when someone notices it's stale
  • The demo environment has no network path to production or to any environment with real customer data
  • The demo runs on a stable, branded custom domain rather than a rotating auto-generated URL
  • The environment is stopped during predictable low-activity windows to control cost
✦✦

Give sales a demo that always looks good and never touches production.

14-day full-feature trial. No credit card required. Pay-as-you-go from $0.007/min per environment.

Frequently Asked Questions

Why not just demo against a copy of production?

A production copy carries real customer data, meaning a sales engineer is one wrong click away from a genuinely awkward moment showing a prospect another customer's information. It also cannot be safely reset or manipulated for the demo script (creating a specific scenario, deleting a record to show that flow) without risking real data.

Why not just use a fixed set of screenshots or a recorded video instead?

Because prospects increasingly expect (and specifically ask for) a live, clickable demo they can interact with, not a passive recording. A real environment lets a sales engineer follow whatever tangent the prospect's questions take, which a fixed recording cannot do.

How often should demo data actually be reset?

On a fixed schedule, nightly is common, rather than only when someone notices it looks messy. A nightly reset means every sales engineer starts each day's demos from the same known-good state, regardless of what the previous day's demos changed.

Should the demo environment run continuously or only when a demo is scheduled?

For a customer-facing branded URL that prospects might revisit between calls, continuously, though it can still be stopped overnight or on weekends to save cost, since a demo audience rarely needs 3 a.m. access. For an internal-only demo environment used for occasional training, on-demand start is more cost-effective.

Does the demo environment need the exact same infrastructure as production?

No, and it usually shouldn't. A demo audience cares about the application behaving correctly and looking polished, not about production-scale infrastructure, so a demo environment is typically sized much smaller than production, which also keeps its running cost low.