HomeSelf-host

Self-host rightnow-ai

rightnow-ai runs only on hardware you control. This guide covers both supported ways: Docker Compose on your own computer, and the same stack on a Linux VPS with Caddy for HTTPS.

The interactive walkthrough needs JavaScript. Every command is also written out below.

Before you begin

SectionCheck
DockerDocker Desktop (Windows, macOS) or Docker Engine with the Compose plugin (Linux)
gitTo clone the repository and pull upgrades
Secretsopenssl, or the generator in the walkthrough above
VPS only1 vCPU / 2 GB RAM to start, a domain with an A record, inbound ports 22, 80 and 443
Gmail, MicrosoftYour own Google OAuth client and Microsoft Entra app (guides linked below)

Run it on your laptop

bash
git clone https://github.com/AnalyticDog13/rightnow-ai.git
cd rightnow-ai
cp .env.example .env

In .env, set ENCRYPTION_KEY and BETTER_AUTH_SECRET (openssl rand -base64 32 each) and a SETUP_TOKEN of 16 or more characters. Leave MODE=local and APP_URL=http://localhost:3000.

bash
scripts/start.sh
# Windows (PowerShell): powershell -ExecutionPolicy Bypass -File scripts\start.ps1 · the first build takes a few minutes

Open http://localhost:3000/sign-up and create the owner account with your SETUP_TOKEN. Sign-up closes after the first owner; everyone else joins by invite. Always open the app at the exact host in APP_URL (localhost, not 127.0.0.1), or every form submission is rejected.

While the laptop sleeps

Nothing sends or syncs while the machine is asleep. On wake, a mailbox may send only after a sync in the last 5 minutes (SYNC_MAX_STALENESS_SEC=300), so replies, bounces and unsubscribes are seen first. Missed sends go out late inside their schedule window, or wait for the next window, never as a burst. A send that was in flight is never resent automatically: it is looked for in the Sent folder, and held if it isn't found twice, 15 minutes apart.

Tracking needs a public address

On localhost, recipients cannot reach your tracking or one-click unsubscribe links, so emails carry a mailto-only unsubscribe and open and click tracking is off. Run on a VPS (or behind a tunnel, for tests only) for tracking.

Dry-run mode

Set MAIL_PROVIDER_FAKE=1 in .env to replace Gmail, Microsoft and IMAP/SMTP with an in-process fake mailbox. You can connect accounts, launch a campaign and watch the whole pipeline run without contacting a provider. The app refuses this flag when MODE=vps.

Run it on a VPS

The same Compose file runs on the server; MODE=vps adds the caddy service, which gets a Let's Encrypt certificate automatically.

  1. A Linux VPS with Docker Engine, the Compose plugin and git (1 vCPU / 2 GB RAM is enough to start).
  2. Point an A record (and AAAA with IPv6) for your domain at the server and wait until it resolves.
  3. Allow inbound 22, 80 and 443 only. Postgres is not published and the web port is bound to 127.0.0.1.
bash
ufw allow 22,80,443/tcp && ufw enable
git clone https://github.com/AnalyticDog13/rightnow-ai.git && cd rightnow-ai
cp .env.example .env
.env
MODE=vps
DOMAIN=app.example.com
APP_URL=https://app.example.com
ENCRYPTION_KEY=<openssl rand -base64 32>
BETTER_AUTH_SECRET=<openssl rand -base64 32>
SETUP_TOKEN=<16+ random characters>
POSTGRES_PASSWORD=<openssl rand -hex 24>   # set before the first start

Then run scripts/start.sh, open https://app.example.com/sign-up and create the owner with your SETUP_TOKEN.

Private hosts. Webhook URLs, the AI base URL and the email-verifier URL must be public by default. ALLOW_PRIVATE_HTTP_HOSTS=1 lets them reach your private network (for example a local Ollama server). Cloud metadata and link-local addresses stay blocked whatever the setting.

Connect Gmail and Microsoft

Gmail and Google Workspace

  • Create a Google Cloud project and enable the Gmail API.
  • Configure the OAuth consent screen and set it to "In production": an app left in "Testing" gets refresh tokens that expire after 7 days.
  • Create a Web application OAuth client with the redirect URI ${APP_URL}/api/v1/oauth/google/callback.
  • Put GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in .env and restart.
  • In the app: Email Accounts, then Connect Google. Full guide: GOOGLE_SETUP.md.

Microsoft 365 and Outlook.com

  • Register an app in Microsoft Entra for organizational and personal accounts.
  • Add the Web redirect URI ${APP_URL}/api/v1/oauth/microsoft/callback.
  • Create a client secret and note its expiry date.
  • Add the delegated Graph permissions Mail.Send, Mail.ReadWrite, offline_access and User.Read.
  • Put MS_CLIENT_ID, MS_CLIENT_SECRET and MS_TENANT=common in .env. Full guide: MICROSOFT_SETUP.md.

Any other provider connects over IMAP/SMTP with a host, username and password (an app password where the provider requires one). Before the first real campaign from a provider, run its one-time live check: npx tsx scripts/live-verify.ts --account <id> --to <another address of yours>. Campaign sending from a provider stays off until it passes.

Sending limits per mailbox

The daily limit you set is never allowed above half of the provider's rolling 24-hour cap. Calendar-day counting is only safe against a rolling window with that margin. Larger values are saved at the maximum and the page tells you so.

Account typeProvider cap (default / highest you can set)Highest daily limitMinimum gap
Google Workspace1500 / 2000750none
Gmail (gmail.com)400 / 500200none
Microsoft 3655000 / 1000025003 s
Outlook.com, Hotmail, Live250 / 3001253 s
SMTP100 / 1000050none

New accounts start at 30 a day (or the maximum, if lower). Counts follow the account's own timezone.

Backups, restore and upgrades

TaskCommand
Startscripts/start.sh
Stop (data is kept)scripts/stop.sh
Backupscripts/backup.sh writes backups/rightnow-<timestamp>.dump
Restorescripts/restore.sh backups/rightnow-<timestamp>.dump (replaces current data; asks for yes)
Upgradescripts/upgrade.sh: backs up, then git pull, rebuild, migrations, restart

Windows uses the matching .ps1 scripts. Data lives in the Docker volume pgdata; the scripts never delete it.

Back up ENCRYPTION_KEY with your database. Your mailbox logins are encrypted with it; a backup restored without the same key cannot read them.

A restore pauses every active campaign. Messages sent after the dump was taken are recognised from the Sent folders and not sent again. Resume the campaigns only after every mailbox shows a sync completed since the restore.

On a VPS, back up nightly with cron and copy backups/ off the server, keeping the key somewhere separate:

crontab
0 3 * * * cd /home/you/rightnow-ai && mkdir -p backups && scripts/backup.sh >> backups/cron.log 2>&1 && find backups -name '*.dump' -mtime +14 -delete

Moving from a laptop to a VPS

  1. Stop the local stack and keep it stopped: two installs must never sync or send from one mailbox.
  2. On the laptop, run scripts/backup.sh.
  3. Copy the dump and your ENCRYPTION_KEY to the server out of band.
  4. Set up the VPS as above with the same ENCRYPTION_KEY; do not start it yet.
  5. Run scripts/restore.sh <dump> (it pauses active campaigns), then start the stack.
  6. Sign in, check /api/readyz and each mailbox's state, and download one attachment by hand.
  7. Add the new domain's redirect URIs in Google and Microsoft and reconnect any mailbox that needs attention.
  8. Switch DNS and tracking-domain CNAMEs only after these checks pass.
  9. Never restart the local install afterwards: it would send the same mail a second time.

Why not Vercel, Netlify or serverless?

rightnow-ai needs a PostgreSQL database and a background worker that runs all the time to sync mailboxes, warm up and send on schedule. The Compose stack has five services: postgres (data and the job queue), migrate (applies database migrations, then exits), web (the app and REST API), worker (sync, scheduling, sending, warm-up) and, with MODE=vps, caddy (HTTPS). Vercel and Netlify run short-lived functions and static files, so they cannot host the worker or the database. Only this documentation site is static.

View the code on GitHub