Install
Install on Vercel
Outcome: Mailnib running on Vercel, with your operator account created during the build.
Vercel runs the app as serverless functions, so its file system does not keep anything between requests. Two things follow from that, and this page is built around them:
- Uploads must go to S3-compatible storage. The app refuses to start on Vercel without a bucket.
- The web installer is not used. Your operator account is created by
pnpm db:seed, whichvercel.jsonruns during every build.
Before you start
- A PostgreSQL database reachable from the internet, such as Neon, Supabase or Vercel's own Postgres. Copy its pooled connection string.
- An S3-compatible bucket: Amazon S3, Cloudflare R2, Backblaze B2 or similar, with an access key that can read and write it.
1. Put the source in a repository and import it
- On your computer, open a terminal inside the
sourcefolder of your download (the one that containspackage.json), thengit init,git add .,git commit -m "Mailnib"and push it to a new private repository on GitHub, GitLab or Bitbucket. Vercel builds from source, soruntime/is not used here. - In Vercel, click Add New… → Project and import that repository.
- Set Root Directory to
apps/weband leave Include files outside the root directory switched on. Leave the framework and build settings as detected:apps/web/vercel.jsonsets the install command and the build command, which runspnpm db:migrate, thenpnpm db:seed, thenpnpm buildfrom the repository root.
2. Set the environment variables
Before the first deploy, open Settings → Environment Variables and add:
| Variable | Value |
|---|---|
DATABASE_URL | Your provider's pooled connection string. |
PUBLIC_APP_URL | https://your-project.vercel.app or your own domain. |
APP_KEY | A random secret of at least 32 characters. On a Mac or Linux terminal: openssl rand -base64 48 |
OPERATOR_EMAIL | The email address you will sign in to the operator console with. |
OPERATOR_PASSWORD | A unique password of at least 12 characters. |
STORAGE_DRIVER | s3 |
STORAGE_S3_BUCKET | Your bucket name. |
STORAGE_S3_REGION | The bucket's region, such as eu-west-1, or auto for R2. |
STORAGE_S3_ENDPOINT | Leave empty for Amazon S3; set it for R2, B2 or MinIO. |
AWS_ACCESS_KEY_ID | The access key for the bucket. |
AWS_SECRET_ACCESS_KEY | Its secret. |
The database must accept connections from Vercel's build machines, because migrations and the seed run during the build.
3. Deploy and check
Click Deploy. When it finishes, open https://your-address/api/health. You should see:
{"ok":true,"db":"connected","migrations":"up to date"}
Then go to https://your-address/admin/login and sign in with OPERATOR_EMAIL and OPERATOR_PASSWORD. The seed runs on every build but never changes an operator that already exists, so changing OPERATOR_PASSWORD later does not change your password.
Good to know
- Visiting
/installshows a "not found" page. That is expected: the seed recorded the installation. - Rate limits are kept per function instance, so they are looser than on a single server.
- For outgoing mail, see Email (SMTP) settings.
- Failed-payment reminders run from Vercel Cron, already set up in
apps/web/vercel.json(daily, the Hobby plan limit). SetCRON_SECRETin the environment variables or the job is refused. See Payment providers.
PostgreSQL role privileges
Use a dedicated login role that owns this application's tables and functions. It needs CONNECT and CREATE on the database, USAGE and CREATE on the application schema (public), and permission to create the drizzle migration schema. Migrations need to create and alter tables, indexes, policies, triggers and functions; normal operation reads and writes those tables and executes the shipped functions. Use the same role for the installer, migrations and DATABASE_URL.
NOSUPERUSER NOBYPASSRLS is supported and recommended. Neither superuser nor BYPASSRLS nor permission to create roles is required. Tenant tables retain FORCE ROW LEVEL SECURITY even for their owner. Verified webhooks, the authenticated operator console and the secret-authenticated dunning scheduler discover workspaces through fixed, read-only functions, then enter a workspace scope for tenant data and writes. Never share the application's database credentials with tenants or browser code. Test setup creates roles to verify isolation; those privileges are needed only by the test administrator.
On managed PostgreSQL, use the database/schema owner supplied by the provider, or have the provider grant the schema creation privileges above. On cPanel, ask the host to grant these privileges if the control-panel database user cannot create schemas or functions. No elevated RLS-bypass role is needed.