Demo Shard Operations Guide
January 8, 2026 · View on GitHub
This guide explains how to run the dedicated demo shard, provision new demo tenants, and keep those tenants up to date. Read it together with multi_tenancy_guide.md when you need implementation details.
1. Goals & Benefits
| Goal | Why it matters |
|---|---|
| Isolate marketing demos | Demo data never touches production tenants. |
| Provide curated experience | Each demo subdomain boots from a known seed pack. |
| Support automatic resets | Visitors always see a clean, predictable state. |
| Protect infrastructure | Destructive controller actions are blocked in demo mode. |
2. Architecture Overview
- Dedicated database connection –
config/database.ymldefines ademo_shardfor every environment. Its migrations live indb/demo_shard_migrate/. - Website metadata –
pwb_websitesnow tracksdemo_mode,demo_seed_pack,demo_reset_interval, anddemo_last_reset_atso we can flag demo tenants and schedule resets. - Shard-aware requests –
PwbTenant::ApplicationRecordconnects to:default,:shard_1, and:demo.Pwb::ApplicationControllerwraps every request inActiveRecord::Base.connected_to(shard: current_website.database_shard)so all Active Record classes hit the same shard. - Demo helpers – The
Pwb::DemoWebsiteconcern (included inPwb::Website) adds handy scopes (.demos,.on_demo_shard), thedemo?predicate, interval parsing, and thereset_demo_data!workflow that clears tenant data and reapplies seed packs. - Middleware & UI –
DemoShardMiddlewareforce-routes known demo subdomains to the demo shard,_demo_banner.html.erbsurfaces a visitor-facing notice, and theDemoRestrictionscontroller concern blocks destructive actions whencurrent_website.demo?evaluates true.
3. Preparing the Databases
Run these commands whenever you bootstrap a new environment (local, staging, production) so every shard exists and is migrated:
# Create databases
bin/rails db:create
bin/rails db:create:tenant_shard_1
bin/rails db:create:demo_shard
# Apply migrations
bin/rails db:migrate
bin/rails db:migrate:tenant_shard_1
bin/rails db:migrate:demo_shard
or run everything at once:
bin/rails db:shards:prepare
Tip:
bin/rails db:preparewill run the primary migrations automatically, but you still need the explicit:tenant_shard_1and:demo_shardtasks unless you use the helper above.
4. Provisioning Demo Tenants
- Configure reserved subdomains in
config/initializers/demo_subdomains.rbso each marketing URL maps to a seed pack (e.g.,demo-spain→spain_luxury). - Run the provisioning rake task:
This task loops overbin/rails demo:provisionDEMO_SUBDOMAINS, callsPwb::DemoProvisioner.provision, and ensures each website lives on the demo shard withdemo_modeenabled. - Seed packs – Provisioner delegates to
Pwb::SeedPackso you can reuse any existing pack. New packs can be added underdb/seeds/packs/with no changes to the provisioning service.
5. Resetting Demo Data
- Manual reset:
This iterates overbin/rails demo:resetPwb::Website.demos.on_demo_shardand callsreset_demo_data!. - Automatic reset:
DemoResetJobchecks each demo site, comparesdemo_last_reset_atagainstdemo_reset_interval, and resets stale tenants.config/schedule.rbschedules the job daily at 03:00. If you deploy to a scheduler (Heroku, Render, etc.) make sure thewhenever-generated cron or the platform-equivalent job runsrails runner "DemoResetJob.perform_later"every night. - Custom intervals: Update
demo_reset_intervalper website (e.g.,'12 hours','2 days'). The concern parses strings, integers (seconds), orActiveSupport::Durationobjects.
6. UX Safeguards
_demo_banner.html.erbappears automatically at the top of every public theme whencurrent_website.demo?is true, explaining that data resets regularly and linking to the signup page.DemoRestrictionsautomatically redirects destructive controller actions (destroy,delete_account,export, etc.) when a visitor is on a demo site.- The initializer-driven whitelist ensures only the subdomains you specify are affected, so regular tenants never see the banner or restrictions.
7. Local Testing Tips
- Create a website in the console, set
demo_mode: true,shard_name: 'default'(so it stays on your local DB), and assign ademo_seed_pack. - Run
Pwb::SeedPack.find(pack).apply!(website: demo_site)once, or calldemo_site.reset_demo_data!to exercise the full workflow. StubbingPwb::SeedPackis also acceptable in automated tests. - The new automated specs (
spec/models/concerns/pwb/demo_website_spec.rbandspec/services/pwb/demo_provisioner_spec.rb) show how to stubPwb::SeedPack, how to assertdemo_last_reset_atupdates, and how to keep tests fast without touching the actual demo shard.
8. Dokku Deployment
When deploying to Dokku, use the postgres:link command with aliases to set up shard database URLs:
# Create the shard databases
dokku postgres:create pwb-demo-shard
dokku postgres:create pwb-shard-1 # Optional: for tenant_shard_1
# Link with aliases that match config/database.yml expectations
# The --alias flag sets the environment variable name prefix
dokku postgres:link pwb-demo-shard your-app --alias PWB_DEMO_SHARD_DATABASE
dokku postgres:link pwb-shard-1 your-app --alias PWB_TENANT_SHARD_1_DATABASE
# This automatically sets:
# - PWB_DEMO_SHARD_DATABASE_URL=postgres://...
# - PWB_TENANT_SHARD_1_DATABASE_URL=postgres://...
Verify the links:
dokku postgres:app-links your-app
# Should show: pwb-demo-shard, pwb-shard-1
dokku config:show your-app | grep SHARD
# Should show the DATABASE_URL environment variables
Run migrations on shards after deploy:
dokku run your-app bin/rails db:migrate:demo_shard
dokku run your-app bin/rails db:migrate:tenant_shard_1
Troubleshooting:
- If you see "No connection pool" errors, ensure the shard database is linked
- Check that
PWB_DEMO_SHARD_DATABASE_URLis set (not justDATABASE_URL) - The app conditionally loads shards based on env vars - see
config/database.yml
9. Operations Checklist
- Ensure
PWB_DEMO_SHARD_DATABASE_URL(or equivalent) is set in every deploy environment. - Run the
db:create/db:migratecommands for the demo shard after each release with migrations. - Keep
DEMO_SUBDOMAINSup to date as marketing adds or removes demo URLs. - Confirm the nightly scheduler triggers
DemoResetJob(check logs for[DemoReset] Reset ...). - Smoke-test demo subdomains after provisioning to verify the banner, seed data, and restrictions appear.