Deploying Dazzle to Heroku¶
Prerequisites¶
- Heroku CLI installed
- Git repository with your Dazzle project
dazzle.tomlat the repository root
Quick Start¶
# Create Heroku app
heroku create my-dazzle-app
# Add PostgreSQL and Redis
heroku addons:create heroku-postgresql:essential-0
heroku addons:create heroku-redis:mini
# Set required env vars
heroku config:set DAZZLE_ENV=production
heroku config:set DAZZLE_SECRET_KEY=$(python -c "import secrets; print(secrets.token_urlsafe(32))")
# Deploy
git push heroku main
DATABASE_URL and REDIS_URL are set automatically by the add-ons.
Procfile¶
Your repository should contain a Procfile at the root:
web: uvicorn dazzle_back.runtime.app_factory:create_app_factory --factory --host 0.0.0.0 --port $PORT --workers ${WEB_CONCURRENCY:-4}
This uses uvicorn's multi-worker mode. Each worker gets its own process and connection pool.
Environment Variables¶
| Variable | Required | Default | Description |
|---|---|---|---|
DATABASE_URL |
Yes | (auto) | PostgreSQL URL (set by Heroku Postgres add-on) |
REDIS_URL |
Yes | (auto) | Redis URL (set by Heroku Redis add-on) |
DAZZLE_ENV |
Yes | development |
Set to production |
DAZZLE_SECRET_KEY |
Yes | - | Secret key for sessions and tokens |
DAZZLE_DB_POOL_MIN |
No | 2 |
Minimum connection pool size |
DAZZLE_DB_POOL_MAX |
No | 10 |
Maximum connection pool size |
WEB_CONCURRENCY |
No | 4 |
Number of uvicorn worker processes |
Connection Pool Tuning¶
Match DAZZLE_DB_POOL_MAX to your Heroku Postgres plan's connection limit. The full per-process budget is ~14 connections (pool ceiling + ~3-4 event-framework + ~1-2 startup-migration transients), so the formula has a hidden multiplier:
| Plan | Max Connections | Recommended DAZZLE_DB_POOL_MAX (with 4 workers) |
Total footprint |
|---|---|---|---|
| Essential-0 | 20 | 2 |
(2+4)*4 = 24 — over; use WEB_CONCURRENCY=1 |
| Essential-1 | 40 | 5 |
(5+4)*4 = 36 |
| Standard-0 | 120 | 15 |
(15+4)*4 = 76 |
| Standard-2+ | 400+ | 25 |
(25+4)*4 = 116 |
Formula: (DAZZLE_DB_POOL_MAX + 4) * WEB_CONCURRENCY < plan_max_connections - 5
The +4 accounts for non-pool framework connections; the -5 leaves headroom for heroku pg:psql debug sessions.
# Conservative defaults for Essential-1 (single dyno, 4 workers):
heroku config:set DAZZLE_DB_POOL_MAX=5 WEB_CONCURRENCY=4
# Or simplify the footprint with a single worker per dyno + horizontal scaling:
heroku config:set DAZZLE_DB_POOL_MAX=10 WEB_CONCURRENCY=1
heroku ps:scale web=4
See docs/reference/databases.md → Connection Pool for diagnostic commands and full symptom → fix table.
Scaling¶
Start with a single standard dyno:
Before scaling horizontally, try vertical scaling:
Horizontal scaling works out of the box — each dyno runs independent workers with their own connection pools. The advisory lock on migrations ensures only one worker runs schema changes.
Asset Bundling¶
DAZZLE_ENV=production flips the framework's CSS/JS loading mode from individual files (live-reload friendly, dev-only) to a single bundled file (/static/dist/dazzle.min.{js,css}). The bundle is precomputed at framework release time and ships in the dazzle-dsl wheel — no Node toolchain required at slug build time.
Configure via [ui] assets in dazzle.toml:
[ui]
# "auto" = bundle when DAZZLE_ENV=production, individual in dev (default)
# "always" = bundle in every environment (perf testing / staging)
# "never" = individual scripts always (live-reload during prod debugging)
assets = "auto"
CLI overrides for one-off testing:
dazzle serve --bundle # force bundled regardless of manifest
dazzle serve --no-bundle # force individual regardless of manifest
Smoke-test bundled mode locally before deploying so you catch any project-side custom JS/CSS that doesn't survive bundling:
DAZZLE_ENV=production dazzle serve --local
# Then load the app in a browser, open DevTools → Network, verify
# /static/dist/dazzle.min.js and /static/dist/dazzle.min.css both
# return 200 with the framework banner at the top.
On Heroku the bundle ships in the wheel; no slug-build step is required. Once you've set DAZZLE_ENV=production and deployed v0.61.141 or later, every page automatically loads the bundle. If you ever need to revert to individual scripts (e.g. to reproduce a dev-mode bug), set assets = "never" in dazzle.toml or pass --no-bundle to dazzle serve.
Trade-offs¶
| Mode | Pros | Cons |
|---|---|---|
| Bundled | One CSS + one JS request; smaller wire transfer; faster first paint on cold connections | Source maps less granular; live-reload no longer per-file |
| Individual | Per-file source maps; live-reload reloads only what changed | ~24 separate requests; slower cold first paint on real RTT |
The default auto mode picks bundled in production and individual in dev — appropriate for most projects. Override only if you've measured a specific reason.
File Storage¶
Heroku's filesystem is ephemeral. For file uploads, configure S3:
heroku config:set DAZZLE_FILE_STORAGE=s3
heroku config:set AWS_ACCESS_KEY_ID=...
heroku config:set AWS_SECRET_ACCESS_KEY=...
heroku config:set AWS_S3_BUCKET=my-dazzle-uploads
Framework Version Pinning¶
Pin the framework version in dazzle.toml to prevent unexpected changes:
The server refuses to start if the installed version doesn't match.
Backups¶
Heroku Postgres provides automatic daily backups. For manual backups:
# Heroku's built-in backup
heroku pg:backups:capture
# Dazzle's backup (includes uploads and metadata)
heroku run dazzle backup create
heroku run dazzle backup create --output /tmp/backup.tar.gz
Custom Domains and SSL¶
heroku domains:add app.example.com
# SSL is automatic with ACM (Automated Certificate Management)
heroku certs:auto:enable
Monitoring¶
# Live logs
heroku logs --tail
# Metrics dashboard
heroku open --app my-dazzle-app
# Navigate to "Metrics" tab in Heroku Dashboard
Troubleshooting¶
Port already in use: Heroku assigns $PORT dynamically. Never hardcode the port.
Connection pool exhaustion: Check heroku pg:info for connection count. Reduce DAZZLE_DB_POOL_MAX or scale down workers.
Migration conflicts: The advisory lock prevents concurrent migrations across workers. If a migration hangs, check heroku pg:locks.
Memory issues: Monitor with heroku logs --tail | grep "Memory quota". Reduce WEB_CONCURRENCY if needed.