Step 25 in Hetzner docs with full Google Cloud/Wallet Console setup, service account configuration, local testing, and architecture diagrams. Loyalty module env vars added to environment.md and .env.example. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
17 KiB
Environment Variables Reference
All configuration for the Orion platform is managed through environment variables, loaded
via Pydantic Settings from an .env file or the process environment. This page provides a
complete reference for every variable recognised by app/core/config.py.
Variables are read at startup and exposed through the settings singleton. In most cases
the defaults are tuned for local development; production deployments must override the
security-sensitive values listed in the Production Checklist at the
bottom of this page.
Core / Project
Metadata used in the OpenAPI schema and health endpoints.
| Variable | Description | Default | Required |
|---|---|---|---|
PROJECT_NAME |
Display name shown in API docs and health responses | Orion - Multi-Store Marketplace Platform |
No |
VERSION |
Semantic version reported by the platform | 2.2.0 |
No |
Database
!!! danger "Production requirement"
You must set DATABASE_URL to a real PostgreSQL connection string in every
non-development environment. The default value contains a placeholder password and
should never be used in production.
| Variable | Description | Default | Required |
|---|---|---|---|
DATABASE_URL |
PostgreSQL connection string (postgresql://user:pass@host:port/db) |
postgresql://orion_user:secure_password@localhost:5432/orion_db |
Yes |
Admin Initialisation
Used by init_production.py and the database seeder to create the initial platform
administrator account.
!!! warning "Change the default password"
The default ADMIN_PASSWORD is admin123. The production validation check will emit a
warning if this value is left unchanged.
| Variable | Description | Default | Required |
|---|---|---|---|
ADMIN_EMAIL |
Email address for the initial admin account | admin@orion.lu |
No |
ADMIN_USERNAME |
Username for the initial admin account | admin |
No |
ADMIN_PASSWORD |
Password for the initial admin account | admin123 |
No (but must change in production) |
ADMIN_FIRST_NAME |
First name of the admin user | Platform |
No |
ADMIN_LAST_NAME |
Last name of the admin user | Administrator |
No |
JWT Authentication
Controls JSON Web Token generation and expiry.
!!! danger "Production requirement"
JWT_SECRET_KEY must be replaced with a strong random value. Generate one with:
```bash
openssl rand -hex 32
```
| Variable | Description | Default | Required |
|---|---|---|---|
JWT_SECRET_KEY |
Secret used to sign and verify JWTs | change-this-in-production |
Yes |
JWT_EXPIRE_HOURS |
Hours component of the token lifetime | 24 |
No |
JWT_EXPIRE_MINUTES |
Minutes component of the token lifetime | 30 |
No |
API Server
Settings passed to Uvicorn when the application starts.
| Variable | Description | Default | Required |
|---|---|---|---|
API_HOST |
Bind address for the API server | 0.0.0.0 |
No |
API_PORT |
Port the API server listens on | 8000 |
No |
DEBUG |
Enable debug mode (extra logging, auto-reload) | True |
No (set False in production) |
Documentation
| Variable | Description | Default | Required |
|---|---|---|---|
DOCUMENTATION_URL |
URL where the MkDocs site is served | http://localhost:8001 |
No |
Security / Middleware
!!! warning "Restrict allowed hosts"
The default ALLOWED_HOSTS value of ["*"] accepts requests with any Host header.
In production, restrict this to your actual domain names.
| Variable | Description | Default | Required |
|---|---|---|---|
ALLOWED_HOSTS |
JSON list of permitted Host header values |
["*"] |
No (but restrict in production) |
RATE_LIMIT_ENABLED |
Enable request rate limiting | True |
No |
RATE_LIMIT_REQUESTS |
Maximum number of requests per window | 100 |
No |
RATE_LIMIT_WINDOW |
Rate limit window duration in seconds | 3600 |
No |
Logging
| Variable | Description | Default | Required |
|---|---|---|---|
LOG_LEVEL |
Python log level (DEBUG, INFO, WARNING, ERROR, CRITICAL) |
INFO |
No |
LOG_FILE |
Path to a log file; None means stdout only |
None |
No |
Platform Domain
Controls the base domain for store subdomains and custom-domain features.
| Variable | Description | Default | Required |
|---|---|---|---|
PLATFORM_DOMAIN |
Root domain under which store subdomains are created | wizard.lu |
No |
ALLOW_CUSTOM_DOMAINS |
Allow stores to use their own domain names | True |
No |
REQUIRE_DOMAIN_VERIFICATION |
Require DNS verification before activating a custom domain | True |
No |
SSL_PROVIDER |
SSL certificate provider (letsencrypt, cloudflare, manual) |
letsencrypt |
No |
AUTO_PROVISION_SSL |
Automatically provision SSL certificates for custom domains | False |
No |
DNS_VERIFICATION_PREFIX |
TXT record prefix used for domain ownership verification | _orion-verify |
No |
DNS_VERIFICATION_TTL |
TTL in seconds for DNS verification records | 3600 |
No |
Platform Limits
Guard-rails for multi-tenant resource usage.
| Variable | Description | Default | Required |
|---|---|---|---|
MAX_STORES_PER_USER |
Maximum number of stores a single user can own | 5 |
No |
MAX_TEAM_MEMBERS_PER_STORE |
Maximum team members allowed per store | 50 |
No |
INVITATION_EXPIRY_DAYS |
Days before a team invitation link expires | 7 |
No |
Stripe Billing
!!! info "Required for payments" All three Stripe keys must be set to enable subscription billing and payment processing. Obtain them from the Stripe Dashboard.
| Variable | Description | Default | Required |
|---|---|---|---|
STRIPE_SECRET_KEY |
Stripe secret API key | "" (empty) |
Yes (for payments) |
STRIPE_PUBLISHABLE_KEY |
Stripe publishable API key | "" (empty) |
Yes (for payments) |
STRIPE_WEBHOOK_SECRET |
Stripe webhook signing secret | "" (empty) |
Yes (for payments) |
STRIPE_TRIAL_DAYS |
Length of the free trial period in days | 30 |
No |
Email Configuration
Orion supports multiple email providers. Set EMAIL_PROVIDER to choose one, then
configure the matching provider-specific variables below.
| Variable | Description | Default | Required |
|---|---|---|---|
EMAIL_PROVIDER |
Email transport backend (smtp, sendgrid, mailgun, ses) |
smtp |
No |
EMAIL_FROM_ADDRESS |
Sender address for outgoing emails | noreply@orion.lu |
No |
EMAIL_FROM_NAME |
Sender display name | Orion |
No |
EMAIL_REPLY_TO |
Optional reply-to address | "" (empty) |
No |
EMAIL_ENABLED |
Master switch to enable/disable all outgoing email | True |
No |
EMAIL_DEBUG |
Log emails to console instead of sending (development only) | False |
No |
SMTP Settings
Used when EMAIL_PROVIDER=smtp.
| Variable | Description | Default | Required |
|---|---|---|---|
SMTP_HOST |
SMTP server hostname | localhost |
No |
SMTP_PORT |
SMTP server port | 587 |
No |
SMTP_USER |
SMTP authentication username | "" (empty) |
No |
SMTP_PASSWORD |
SMTP authentication password | "" (empty) |
No |
SMTP_USE_TLS |
Use STARTTLS (port 587) | True |
No |
SMTP_USE_SSL |
Use implicit SSL (port 465) | False |
No |
SendGrid Settings
Used when EMAIL_PROVIDER=sendgrid.
| Variable | Description | Default | Required |
|---|---|---|---|
SENDGRID_API_KEY |
SendGrid API key | "" (empty) |
Yes (if using SendGrid) |
Mailgun Settings
Used when EMAIL_PROVIDER=mailgun.
| Variable | Description | Default | Required |
|---|---|---|---|
MAILGUN_API_KEY |
Mailgun API key | "" (empty) |
Yes (if using Mailgun) |
MAILGUN_DOMAIN |
Mailgun sending domain | "" (empty) |
Yes (if using Mailgun) |
Amazon SES Settings
Used when EMAIL_PROVIDER=ses.
| Variable | Description | Default | Required |
|---|---|---|---|
AWS_ACCESS_KEY_ID |
AWS access key for SES | "" (empty) |
Yes (if using SES) |
AWS_SECRET_ACCESS_KEY |
AWS secret key for SES | "" (empty) |
Yes (if using SES) |
AWS_REGION |
AWS region for the SES endpoint | eu-west-1 |
No |
Storefront Defaults
Default locale and currency applied to new storefronts. Individual stores can override
these through the admin interface or the AdminSetting database table.
| Variable | Description | Default | Required |
|---|---|---|---|
DEFAULT_STOREFRONT_LOCALE |
Locale code for currency and number formatting | fr-LU |
No |
DEFAULT_CURRENCY |
ISO 4217 currency code | EUR |
No |
Seed Data
Controls the volume of demo data generated by the database seeder.
| Variable | Description | Default | Required |
|---|---|---|---|
SEED_DEMO_STORES |
Number of demo stores to create | 3 |
No |
SEED_CUSTOMERS_PER_STORE |
Number of demo customers per store | 15 |
No |
SEED_PRODUCTS_PER_STORE |
Number of demo products per store | 20 |
No |
SEED_ORDERS_PER_STORE |
Number of demo orders per store | 10 |
No |
Celery / Redis
Background task processing. When USE_CELERY is False, tasks fall back to FastAPI's
built-in BackgroundTasks.
!!! tip "Enable Celery in production"
Set USE_CELERY=True and ensure a Redis instance is reachable at REDIS_URL for
reliable background task processing.
| Variable | Description | Default | Required |
|---|---|---|---|
REDIS_URL |
Redis connection string used as Celery broker and result backend | redis://localhost:6379/0 |
No |
USE_CELERY |
Use Celery for background tasks instead of FastAPI BackgroundTasks | False |
No (set True in production) |
FLOWER_URL |
URL of the Flower monitoring dashboard | http://localhost:5555 |
No |
FLOWER_PASSWORD |
Password for Flower authentication | changeme |
No (but change in production) |
Sentry
Error tracking and performance monitoring via Sentry.
| Variable | Description | Default | Required |
|---|---|---|---|
SENTRY_DSN |
Sentry Data Source Name; None disables Sentry |
None |
No |
SENTRY_ENVIRONMENT |
Environment tag sent with events (development, staging, production) |
development |
No |
SENTRY_TRACES_SAMPLE_RATE |
Fraction of transactions sampled for performance monitoring (0.0--1.0) | 0.1 |
No |
Monitoring
Prometheus metrics and Grafana dashboard integration.
| Variable | Description | Default | Required |
|---|---|---|---|
ENABLE_METRICS |
Expose a /metrics endpoint for Prometheus scraping |
False |
No (set True in production) |
GRAFANA_URL |
URL of the Grafana instance | https://grafana.wizard.lu |
No |
GRAFANA_ADMIN_USER |
Grafana admin username | admin |
No |
GRAFANA_ADMIN_PASSWORD |
Grafana admin password | "" (empty) |
No |
Cloudflare R2 Storage
Object storage for media uploads. When STORAGE_BACKEND is local, files are stored on
the server filesystem.
| Variable | Description | Default | Required |
|---|---|---|---|
STORAGE_BACKEND |
Storage backend to use (local or r2) |
local |
No |
R2_ACCOUNT_ID |
Cloudflare account ID | None |
Yes (if using R2) |
R2_ACCESS_KEY_ID |
R2 API access key | None |
Yes (if using R2) |
R2_SECRET_ACCESS_KEY |
R2 API secret key | None |
Yes (if using R2) |
R2_BUCKET_NAME |
R2 bucket name | orion-media |
No |
R2_PUBLIC_URL |
Custom public URL for media access (e.g. https://media.yoursite.com) |
None |
No |
Loyalty Module
Configuration for the loyalty module (stamp/points programs, wallet integration).
All variables use the LOYALTY_ prefix and are managed by app/modules/loyalty/config.py.
Anti-Fraud Defaults
| Variable | Description | Default | Required |
|---|---|---|---|
LOYALTY_DEFAULT_COOLDOWN_MINUTES |
Minimum minutes between stamps for the same card | 15 |
No |
LOYALTY_MAX_DAILY_STAMPS |
Maximum stamps per card per day | 5 |
No |
LOYALTY_PIN_MAX_FAILED_ATTEMPTS |
Failed PIN attempts before lockout | 5 |
No |
LOYALTY_PIN_LOCKOUT_MINUTES |
Duration of PIN lockout in minutes | 30 |
No |
Points
| Variable | Description | Default | Required |
|---|---|---|---|
LOYALTY_DEFAULT_POINTS_PER_EURO |
Points earned per euro spent | 10 |
No |
Google Wallet
!!! info "Required for Google Wallet passes" Both variables must be set for loyalty cards to appear in Google Wallet. See Hetzner Step 25 for setup guide.
| Variable | Description | Default | Required |
|---|---|---|---|
LOYALTY_GOOGLE_ISSUER_ID |
Google Wallet Issuer ID (numeric string from Pay & Wallet Console) | None |
Yes (for Google Wallet) |
LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON |
Path to the Google service account JSON key file | None |
Yes (for Google Wallet) |
Apple Wallet
!!! info "Required for Apple Wallet passes"
All five variables must be set for .pkpass generation.
Requires an Apple Developer account ($99/year).
| Variable | Description | Default | Required |
|---|---|---|---|
LOYALTY_APPLE_PASS_TYPE_ID |
Pass type identifier (e.g., pass.com.example.loyalty) |
None |
Yes (for Apple Wallet) |
LOYALTY_APPLE_TEAM_ID |
Apple Developer Team ID | None |
Yes (for Apple Wallet) |
LOYALTY_APPLE_WWDR_CERT_PATH |
Path to Apple WWDR intermediate certificate | None |
Yes (for Apple Wallet) |
LOYALTY_APPLE_SIGNER_CERT_PATH |
Path to pass signing certificate (.pem) |
None |
Yes (for Apple Wallet) |
LOYALTY_APPLE_SIGNER_KEY_PATH |
Path to pass signing private key (.pem) |
None |
Yes (for Apple Wallet) |
QR Code
| Variable | Description | Default | Required |
|---|---|---|---|
LOYALTY_QR_CODE_SIZE |
QR code image size in pixels | 300 |
No |
Cloudflare CDN / Proxy
| Variable | Description | Default | Required |
|---|---|---|---|
CLOUDFLARE_ENABLED |
Set to True when the application sits behind Cloudflare proxy (adjusts trusted-proxy headers) |
False |
No (set True when proxied) |
Production Checklist
Before deploying to production, ensure the following variables are set correctly. Items marked critical will trigger a startup warning if left at their default values.
!!! danger "Critical -- must change"
- [x] DATABASE_URL -- point to a production PostgreSQL instance
- [x] JWT_SECRET_KEY -- generate with openssl rand -hex 32
- [x] ADMIN_PASSWORD -- choose a strong, unique password
- [x] DEBUG -- set to False
- [x] ALLOWED_HOSTS -- restrict to your domain(s)
!!! warning "Strongly recommended"
- [x] USE_CELERY -- set to True with a production Redis instance
- [x] FLOWER_PASSWORD -- change from the default changeme
- [x] ENABLE_METRICS -- set to True for observability
- [x] SENTRY_DSN -- configure for error tracking
- [x] SENTRY_ENVIRONMENT -- set to production
- [x] STORAGE_BACKEND -- set to r2 for scalable media storage
- [x] CLOUDFLARE_ENABLED -- set to True if behind Cloudflare proxy
!!! info "Required for specific features"
- [x] Payments: STRIPE_SECRET_KEY, STRIPE_PUBLISHABLE_KEY, STRIPE_WEBHOOK_SECRET
- [x] Email (SendGrid — recommended): EMAIL_PROVIDER=sendgrid, SENDGRID_API_KEY — handles transactional + marketing in one account
- [x] Email (Mailgun): MAILGUN_API_KEY, MAILGUN_DOMAIN — transactional only, no marketing features
- [x] Email (SES): AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY — cheapest at scale
- [x] R2 Storage: R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY
- [x] Google Wallet: LOYALTY_GOOGLE_ISSUER_ID, LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON
- [ ] Apple Wallet: LOYALTY_APPLE_PASS_TYPE_ID, LOYALTY_APPLE_TEAM_ID, LOYALTY_APPLE_WWDR_CERT_PATH, LOYALTY_APPLE_SIGNER_CERT_PATH, LOYALTY_APPLE_SIGNER_KEY_PATH
Example .env file (production)
# Core
DATABASE_URL=postgresql://orion:STRONG_PASSWORD@db.internal:5432/orion
JWT_SECRET_KEY=a]3f...your-random-hex-here...9c2b
DEBUG=False
ALLOWED_HOSTS=["wizard.lu","*.wizard.lu"]
# Admin
ADMIN_PASSWORD=your-strong-admin-password
# Celery / Redis
REDIS_URL=redis://redis.internal:6379/0
USE_CELERY=True
FLOWER_PASSWORD=a-secure-flower-password
# Stripe
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
# Email (example: SendGrid)
EMAIL_PROVIDER=sendgrid
SENDGRID_API_KEY=SG....
# R2 Storage
STORAGE_BACKEND=r2
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_PUBLIC_URL=https://media.wizard.lu
# Monitoring
ENABLE_METRICS=True
SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
SENTRY_ENVIRONMENT=production
CLOUDFLARE_ENABLED=True
# Google Wallet (Loyalty)
LOYALTY_GOOGLE_ISSUER_ID=3388000000023089598
LOYALTY_GOOGLE_SERVICE_ACCOUNT_JSON=/app/google-wallet-sa.json