Configure
Configuration
Outcome: you know what every line in .env.example does and which ones you must set.
Mailnib reads its settings from environment variables. On cPanel they live in a .env file next to app.js (copy .env.example to start). On Render and Vercel you enter them in the dashboard. Restart the app after changing any of them.
Required means the app will not work correctly without it in production. Optional means the default is fine for most sites.
Core
| Variable | Required | What it does |
|---|---|---|
DATABASE_URL | Required | The PostgreSQL connection string, postgresql://USER:PASSWORD@HOST:5432/DATABASE. Percent-encode special characters in the password. Add ?sslmode=require for most hosted databases. |
APP_KEY | Required | A random secret of at least 32 characters. The web installer generates it. Change it only by rotating it, see Security checks and rotating APP_KEY; the example value in .env.example is refused as too weak. |
PUBLIC_APP_URL | Required | The address your customers use, with https://, such as https://mail.example.com. Confirmation links, export links and exported image addresses are built from it. Publishing to email platforms needs it to be a public https:// address. |
APP_KEY_PREVIOUS | Optional | Only while rotating APP_KEY: the old key. Values sealed with either key are readable until pnpm app-key:rotate has run. Remove it afterwards. |
TRUSTED_PROXY_HOPS | Optional | How many proxies in front of the app append to X-Forwarded-For, for request limits. Default 1; use 2 behind a CDN. |
RATE_LIMITING | Optional | off disables every request limit. For automated tests only. Default on. |
CRON_SECRET | Optional | A long random secret that lets your scheduler call POST /api/jobs/dunning to send failed-payment reminders. Without it the endpoint answers "not found". See Payment providers. |
MIGRATE_ON_START | Optional | true applies pending database migrations every time the app starts. Set it to true on cPanel and Render. Default false. |
REGISTRATION_DISABLED | Optional | true closes the public sign-up page; existing customers still sign in, and you can still create tenants from the console. Default false. |
MARKETING_SITE_ENABLED | Optional | false turns the public storefront off: / opens the app and the pricing, templates and contact pages answer "not found". Legal pages stay online. Default true. See Your public site and legal pages. |
Storage
Uploaded images and exported files are kept on the local disk unless you configure a bucket.
| Variable | Required | What it does |
|---|---|---|
STORAGE_DRIVER | Optional | local (default) or s3. Required as s3 on Vercel. |
STORAGE_ROOT | Optional | The folder for local storage, relative to the app or absolute. Default storage. Render sets /var/data. The installer's token and progress files also live here. |
STORAGE_S3_BUCKET | With S3 | The bucket name. When set, uploads go to the bucket. |
STORAGE_S3_REGION | With S3 | The bucket's region, such as eu-west-1. |
STORAGE_S3_ENDPOINT | Optional | Leave empty for Amazon S3. Set it for Cloudflare R2, Backblaze B2, MinIO and other S3-compatible services. |
AWS_ACCESS_KEY_ID | With S3 | Access key with read and write access to the bucket. |
AWS_SECRET_ACCESS_KEY | With S3 | Its secret. |
Transactional mail
These are the fallbacks for Email (SMTP) settings. Anything you save in the operator console wins over them, except the password.
| Variable | Required | What it does |
|---|---|---|
MAIL_TRANSPORT | Optional | Leave empty and the platform chooses: SMTP in production once an SMTP host is set (here or in the console), the log otherwise. smtp forces SMTP. log writes every message to the server log and delivers nothing, even when SMTP settings are saved. |
SMTP_HOST | With SMTP | Your provider's SMTP host, such as smtp.postmarkapp.com. |
SMTP_PORT | Optional | Default 587. Use 465 for implicit TLS. |
SMTP_ENCRYPTION | Optional | starttls (default, port 587), tls (port 465) or none. |
SMTP_USERNAME | With SMTP | Usually your full address or an API user. |
SMTP_PASSWORD | With SMTP | When set, it wins over a password saved in the console. Setting it here keeps the password out of the database, which we recommend. |
MAIL_FROM_NAME | Optional | The sender name customers see. Default Mailnib. |
MAIL_FROM_ADDRESS | With SMTP | The sender address. It must be on a domain your provider allows. |
MAIL_REPLY_TO | Optional | Where replies go. |
Template sample photos
| Variable | Required | What it does |
|---|---|---|
SAMPLE_IMAGES_URL | Optional | Defaults to https://assets.keyngdev.online, the author's sample-photo host, when unset or empty. Set a different HTTP(S) base URL to use your own host, or off to show drawn placeholders. The host contains sample photos and thumbnails/<template-id>.png. Photos are hosted remotely and are not part of the download. Unreachable photos and thumbnails fall back to the bundled artwork. |
Generate hosted thumbnails
Run pnpm templates:hosted-thumbnails --out /absolute/path/outside-the-repository after building the packages. It renders every built-in template with the configured sample photos. Upload the resulting PNG files to the thumbnails/ folder on the same host. Keep these files outside the repository and the downloadable release: they contain hosted photos. The command rejects output inside the repository, including paths through symlinks, and stops if a sample photo cannot be downloaded.
Payment providers
All optional. Each provider needs every one of its variables; see Payment providers.
| Variable | What it does |
|---|---|
STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET | Stripe secret key (sk_…) and the signing secret of your webhook endpoint (whsec_…). |
PAYPAL_CLIENT_ID, PAYPAL_SECRET, PAYPAL_WEBHOOK_ID | PayPal REST app credentials and the ID of the webhook you create for it. |
RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET, RAZORPAY_WEBHOOK_SECRET | Razorpay API keys and webhook secret. |
PAYSTACK_SECRET_KEY | Paystack secret key; it also verifies webhooks. |
STRIPE_API_URL, PAYPAL_API_URL, RAZORPAY_API_URL, PAYSTACK_API_URL | Override the provider's API address. Leave them out of .env entirely (an empty value is not the same as unset); they exist for testing and for PayPal's sandbox. |
Web installer
| Variable | Required | What it does |
|---|---|---|
INSTALL_STATE_DIR | Optional | Where the installer keeps its token, progress and the "installed" marker. Defaults to STORAGE_ROOT. It must survive restarts and must not be inside a public folder. |
The installer also honours INSTALL_ENV_FILE, the file it appends APP_KEY to (default: .env in the app folder). You rarely need it.
First operator from the command line
Used only by pnpm db:seed, which Vercel's build runs. The web installer ignores them.
| Variable | What it does |
|---|---|
OPERATOR_EMAIL | The operator's sign-in address. Default operator@example.com. |
OPERATOR_NAME | The operator's display name. |
OPERATOR_PASSWORD | At least 12 characters. When empty, a random password is printed once. It is only used when the operator is first created. |
Demo mode
Never turn these on for a real installation. See Demo mode.
| Variable | What it does |
|---|---|
DEMO_MODE | true turns the installation into a public demo with one-click sign-in and guarded settings. Default false. |
DEMO_RESET_TOKEN | Secret for the reset endpoint. Without it, the endpoint answers 404. |
DEMO_RESET_INTERVAL_MINUTES | Only used to tell visitors when the next reset is due. Default 60. |
DEMO_PURCHASE_URL | Your item page. When set, the demo's landing page shows a Buy a licence button. |
Set by the host
You normally leave these alone; Node.js or your host provides them.
| Variable | What it does |
|---|---|
NODE_ENV | production on a live site. |
PORT | The port the app listens on. cPanel and Render set it. Default 3000. |
HOSTNAME, HOST | The address the server binds to. Default 0.0.0.0. |
NEXT_RUNTIME | Set by the framework. Do not change it. |
VERCEL | Set by Vercel. Its presence makes S3 storage mandatory. |
Development only
| Variable | What it does |
|---|---|
SCREENSHOT_MODE | Fixture mode used when capturing screenshots. Keep 0. |
NEXT_DIST_DIR | A second build folder used by the test tools. Leave empty. |
Storage settings reference
Local files are stored under STORAGE_ROOT. To use Amazon S3, Cloudflare R2, Backblaze B2 or another compatible service, set the S3 environment variables listed above and restart. Storage settings are read-only in the console; its connection test checks the configured adapter. Storage is counted per workspace as files arrive. After restoring a database and files together, check the usage totals.
Scheduled work
Set CRON_SECRET and schedule a daily GET or POST to /api/jobs/dunning with Authorization: Bearer <CRON_SECRET>. Vercel uses the schedule in apps/web/vercel.json; on other hosts, configure the scheduler provided by your host. Keep the secret out of public commands and logs. Ordinary deferred work still runs inline without a scheduler.