Self-host
Environment variables
Required configuration for a self-hosted vantezzen/pay instance.
Web app variables
| Variable | Example | Notes |
|---|---|---|
DATABASE_URL | postgres://... | Postgres connection string. |
PAY_PROVIDER_SERVICE_URL | http://provider-service:3001 | Internal URL the web app uses for provider operations. |
PAY_PROVIDER_SERVICE_SECRET | base64... | Shared secret between the web app and provider service. |
BETTER_AUTH_SECRET | base64... | Random auth secret. |
BETTER_AUTH_URL | https://pay.example.com | Public dashboard/auth URL. |
NEXT_PUBLIC_APP_URL | https://pay.example.com | Public URL used for webhooks, checkout returns, and registry links. |
PAY_ALLOW_SIGNUPS | true or false | Allow new dashboard accounts. |
RESEND_API_KEY | re_... | Resend API key for auth emails and wallet recovery. |
RESEND_FROM | vantezzen/pay <[email protected]> | Verified sender used for transactional emails. |
GITHUB_CLIENT_ID | Iv1... | Optional GitHub OAuth app client id for dashboard sign-in. |
GITHUB_CLIENT_SECRET | ... | Optional GitHub OAuth app client secret. |
PAY_BILLING_MODE | self_hosted | Keep this as self_hosted for unbilled self-hosted deployments. |
The hosted billing variables (PAY_HOSTED_PAY_SECRET_KEY,
NEXT_PUBLIC_HOSTED_PAY_URL, NEXT_PUBLIC_HOSTED_PAY_KEY,
PAY_ALL_ACCESS_EMAILS, and PAY_ALL_ACCESS_USER_IDS) are only for the
official hosted pay.vantezzen.io deployment. Do not set them for normal
self-hosting.
Provider service variables
Put these in services/provider/.env or the provider service deployment:
| Variable | Example | Notes |
|---|---|---|
DATABASE_URL | postgres://... | Postgres connection string. Can use a narrower DB role. |
PAY_PROVIDER_SERVICE_SECRET | base64... | Same shared secret as the web app. |
PAY_PROVIDER_SERVICE_PORT | 3001 | Local port. |
PAY_SECRETS_PROVIDER | env or vault | Defaults to env. |
PAY_ENCRYPTION_KEY | base64... | Required for PAY_SECRETS_PROVIDER=env; must decode to exactly 32 bytes. |
VAULT_ADDR | https://vault.example.com | Required for PAY_SECRETS_PROVIDER=vault. |
VAULT_TOKEN | hvs... | Vault token for Transit encrypt/decrypt. |
VAULT_TRANSIT_MOUNT | transit | Vault Transit mount path. |
VAULT_TRANSIT_KEY | pay-provider-secrets | Vault Transit key name. |
Generate secrets
Generate secrets locally:
openssl rand -base64 32
openssl rand -base64 32
openssl rand -base64 32Use one value for BETTER_AUTH_SECRET, one for PAY_PROVIDER_SERVICE_SECRET,
and one for PAY_ENCRYPTION_KEY if you use the default env secrets provider.
Local example
DATABASE_URL="postgres://pay:pay@localhost:5432/pay"
PAY_PROVIDER_SERVICE_URL="http://localhost:3001"
PAY_PROVIDER_SERVICE_SECRET="..."
BETTER_AUTH_SECRET="..."
BETTER_AUTH_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
PAY_ALLOW_SIGNUPS="true"
RESEND_API_KEY="re_..."
RESEND_FROM="vantezzen/pay <[email protected]>"
PAY_BILLING_MODE="self_hosted"DATABASE_URL="postgres://pay:pay@localhost:5432/pay"
PAY_PROVIDER_SERVICE_SECRET="..."
PAY_PROVIDER_SERVICE_PORT="3001"
PAY_SECRETS_PROVIDER="env"
PAY_ENCRYPTION_KEY="..."Production tips
- Keep
BETTER_AUTH_URLandNEXT_PUBLIC_APP_URLon the same public origin unless you have a specific reason not to. - Rotate provider API keys from the provider dashboard, then update vantezzen/pay.
- Put
PAY_ENCRYPTION_KEY,PAY_SECRETS_PROVIDER, andVAULT_*on the provider service, not the public web app. - Do not rotate
PAY_ENCRYPTION_KEYcasually; it encrypts stored provider secrets. - Set
PAY_ALLOW_SIGNUPS=falseafter your first admin user exists. - Set up Resend before enabling real users. Email verification, password reset, email changes, account deletion confirmation, and wallet recovery all depend on transactional email.
- For GitHub login, set the OAuth app callback URL to
https://pay.example.com/api/auth/callback/github.