v3.0 upgrade:upgrading from v2.x? Nobody can sign in until you run pnpm db:migrate-better-auth.Read the migration guide
PropertyPro
DocsDocumentation
⌘ K
Documentation·Setup & installation

Setup & Installation

Everything you need to install, configure, and deploy PropertyPro — from your first pnpm install to a custom domain on production.

Action required

Upgrading from v2.x? Run the Better Auth migration before anyone tries to log in

v3.0 moves authentication from NextAuth to Better Auth, which needs a one-time database migration. Until it runs, nobody can sign in — every API call returns 503 and the sign-in page rejects even a correct administrator password with “Invalid email or password”. Passwords carry over untouched and no records are rewritten; everyone is simply signed out once. Back up first, then run it. Fresh installs can ignore this.

before starting v3.0
bash
pnpm db:migrate-better-auth --dry-run   # preview, writes nothing
pnpm db:migrate-better-auth             # required
Read the full upgrade guide

Chapter 01 · Overview

Welcome to PropertyPro

PropertyPro is a complete property management application built with Next.js 16, React 19, and MongoDB. Version 3.0 adds a public marketing website with online rental applications. This documentation covers everything from your first install to deploying in production.

What's in the package

Full source code, database schema, seed data, a PWA-ready dashboard, and a public marketing site with a rental application checkout.

License

One regular license per end product. Lifetime updates within the same major version.

Stack

Next.js 16, React 19 + Compiler, TypeScript, Tailwind CSS v4, MongoDB, Better Auth, Stripe.

Support

6 months of buyer support included. Renewable from your CodeCanyon account.

Upgrading from v2.x? Nobody can log in until the migration runs

v3.0 replaces NextAuth with Better Auth and needs a one-time database migration — pnpm db:migrate-better-auth. Until it runs, every API call returns 503 and sign-in refuses even a correct admin password with "Invalid email or password". See Upgrading to v3.0 before you do anything else.

Looking for end-user docs?

If you're configuring tenants, leases, and day-to-day workflows, head to the User Manual instead.

Chapter 02 · Upgrading to v3.0

v2.x → v3.0: run the auth migration

Fresh install? Skip this section entirely. Upgrading from any 2.x version? v3.0 replaces NextAuth with Better Auth and needs a one-time database migration. Until it has run nobody can sign in — not even an administrator with the right password — and the app refuses to serve requests.

  1. 1

    Back up first

    Settings → Backups → Create backup, or run mongodump. This is the entire rollback plan — do not skip it.

    bash
    mongodump --uri="$MONGODB_URI" --archive=pre-v3-upgrade.archive --gzip
  2. 2

    Copy the v3.0 files and install

    Preserve .env.local and your local ./uploads directory.

    bash
    pnpm install
  3. 3

    Leave your environment alone

    No change is required. v3.0 reads BETTER_AUTH_SECRET first and falls back to AUTH_SECRET, then NEXTAUTH_SECRET. If you switch to the new names, copy the same value across — a new secret signs everyone out.

  4. 4

    Run the migration

    Additive and safe to re-run: it copies password hashes rather than moving them, and skips anything already migrated. If it is interrupted, just run it again.

    bash
    pnpm db:migrate-better-auth --dry-run   # preview
    pnpm db:migrate-better-auth             # apply
  5. 5

    Rebuild and verify

    Sign in with an existing account and its existing password, check that Settings → Security lists your session, and confirm terminating a session signs that device out.

    bash
    pnpm build
    pnpm start
  6. 6

    Run the optional migrations

    Not required to boot, but each fixes something. db:migrate-email-index lets you re-create a tenant whose account was deleted; db:backfill-coordinates puts pre-3.0 properties on the new maps.

    bash
    pnpm db:migrate-email-index
    pnpm db:backfill-coordinates
The one command that matters
bash
# 1. Preview — writes nothing
pnpm db:migrate-better-auth --dry-run

# 2. Run it
pnpm db:migrate-better-auth

Until the migration runs, every API call returns 503

"This deployment is running database schema v0 but the application requires v1." That is deliberate — v3.0 refuses to serve an un-migrated database rather than answering correct passwords with "invalid credentials". Sign-in is not covered by the gate, so it reports "Invalid email or password" instead; same cause, same fix.

What the upgrade does and does not touch

Passwords are unchanged — nobody has to reset anything. Users, properties, leases, payments, and tenants are untouched. Everyone is signed out once, because pre-3.0 logins were JWT-based and have no server-side record to convert.

Customized files: one import moved

v3.0 changed 74 files mechanically. If you edited any of them, change next-auth/react to @/lib/auth-client — useSession, signIn, and signOut keep the same signatures. The old next-auth package is still installed so your build doesn't break, but it is scheduled for removal in v3.1.

Contacting support about an upgrade?

Include the output of pnpm db:migrate-better-auth --dry-run. It identifies the state of your database immediately. The bundled UPGRADE.md has the full reference, including rollback.

Chapter 03 · Quickstart

Get running in 5 minutes

If you're already comfortable with Node, pnpm, and MongoDB, this is the express path. Each step is expanded later in the docs.

  1. 1

    Install dependencies

    From the unzipped folder, install all packages with pnpm.

    bash
    cd propertypro
    pnpm install
  2. 2

    Copy env file

    Duplicate the example env, then set MONGODB_URI and generate an auth secret. Everything else has a working default.

    bash
    cp .env.example .env.local
    openssl rand -base64 32   # paste as BETTER_AUTH_SECRET
  3. 3

    Load sample data (optional)

    Seeds demo properties, units, and tenants so the dashboard isn't empty. It creates no login.

    bash
    pnpm db:seed
  4. 4

    Start the dev server

    Runs on http://localhost:3000. Leave it running for the next step.

    bash
    pnpm dev
  5. 5

    Create your first administrator

    Add SETUP_SECRET to .env.local, restart, then call the one-time bootstrap endpoint. Remove the secret afterwards.

    bash
    curl -X POST http://localhost:3000/api/setup/create-admin \
      -H "Content-Type: application/json" \
      -H "x-setup-secret: $SETUP_SECRET" \
      -d '{"email":"you@example.com","password":"<strong password>","firstName":"Your","lastName":"Name"}'

No default login ships with PropertyPro

As of 3.0 there is no built-in admin account and no hardcoded password. The bootstrap endpoint above is the only way to create your first administrator, and it refuses to run once one exists.

Chapter 04 · Requirements

System & runtime requirements

PropertyPro runs anywhere Node.js does. These versions are tested in CI — older versions may work but aren't supported.

Node.js 20.19.28+

Enforced by the package engines field. Use 20 LTS or 22 — Node 18 is not supported due to React Compiler dependencies.

pnpm 9+

Required. Install with corepack enable && corepack prepare pnpm@latest --activate.

MongoDB 6+

MongoDB Atlas is recommended. Self-hosted MongoDB works when MONGODB_URI points to your server.

File storage

Local disk by default (./uploads). Switch UPLOAD_STORAGE_PROVIDER to cloud for Cloudflare R2 — recommended on serverless hosts, where local disk does not persist.

Hosting suggestion

For most buyers, Vercel + MongoDB Atlas is the fastest path. The full deploy guide covers VPS and Docker too.

Chapter 05 · Download & Setup

Get the source running locally

Download from CodeCanyon, unzip, and install. PropertyPro ships as a ready-to-run Next.js project — no build steps required to start dev.

  1. 1

    Download from CodeCanyon

    Sign in to CodeCanyon → Downloads → PropertyPro. Choose 'All files & documentation'.

  2. 2

    Unzip the package

    Extract the archive to a permanent location. The main folder is /propertypro.

    bash
    unzip propertypro-v3.0.0.zip -d ~/projects/
    cd ~/projects/propertypro
  3. 3

    Install dependencies

    We use pnpm for fast, deterministic installs. Run this once after every download or update.

    bash
    pnpm install
  4. 4

    Verify the install

    A quick smoke test confirms everything resolved correctly.

    bash
    npx tsc --noEmit
    pnpm lint

Don't use npm or yarn

The lockfile is pnpm-only. Mixing package managers will corrupt resolutions — stick to pnpm.

Chapter 06 · Environment Variables

Configure .env.local

All secrets live in .env.local. The included .env.example documents every key — only the core block is needed to boot. Payment gateways, SMTP, and maps are normally configured in the admin panel and stored in the database; the matching env values are used only as a fallback when the admin field is empty.

.env.local
bash
# ---- Required: core --------------------------------------------------
# MongoDB Atlas
MONGODB_URI=mongodb+srv://username:password@cluster0.kbnje.mongodb.net/propertypro
# Self-hosted alternative
# MONGODB_URI=mongodb://mongo:password@your_ip_address:27017/propertypro
# MONGODB_DB=propertypro

# Public base URL, no trailing slash
BETTER_AUTH_URL=http://localhost:3000
# Generate with: openssl rand -base64 32
BETTER_AUTH_SECRET=replace-with-a-long-random-string

# Upgrading from v2.x? The old names are still read as a fallback
# (BETTER_AUTH_SECRET -> AUTH_SECRET -> NEXTAUTH_SECRET). Keep the SAME
# value — changing the secret signs everyone out.
# NEXTAUTH_SECRET=

# ---- First admin (delete after use) ----------------------------------
# Unlocks POST /api/setup/create-admin. Generate: openssl rand -hex 32
# SETUP_SECRET=

# ---- Application -----------------------------------------------------
APP_NAME=PropertyPro
APP_URL=http://localhost:3000
NEXT_PUBLIC_APP_URL=http://localhost:3000
SUPPORT_EMAIL=support@example.com

# ---- File storage ----------------------------------------------------
# "local" (default, ./uploads) or "cloud" (Cloudflare R2)
UPLOAD_STORAGE_PROVIDER=local
# R2_ACCOUNT_ID=
# R2_ACCESS_KEY_ID=
# R2_SECRET_ACCESS_KEY=
# R2_BUCKET_NAME=
# R2_PUBLIC_URL=https://files.example.com
# NEXT_PUBLIC_R2_PUBLIC_URL=https://files.example.com

# ---- Maps (normally set in Settings -> Maps) -------------------------
# "leaflet" (OpenStreetMap, free, no key — default), "google", "disabled"
# MAPS_PROVIDER=leaflet
# Browser key: Maps JavaScript API + Places API, restrict by HTTP referrer
# NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=
# Server key for db:backfill-coordinates: Geocoding API, restrict by IP
# GOOGLE_GEOCODING_API_KEY=

# ---- Stripe (fallback for the admin panel) ---------------------------
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...

# ---- Email / SMTP (fallback for the admin panel) ---------------------
# EMAIL_SERVER_HOST=smtp.gmail.com
# EMAIL_SERVER_PORT=587
# EMAIL_SERVER_USER=you@example.com
# EMAIL_SERVER_PASSWORD=your-app-password
# EMAIL_FROM=PropertyPro <noreply@example.com>
ENABLE_EMAIL_NOTIFICATIONS=true

# ---- Web Push (VAPID) ------------------------------------------------
# npx web-push generate-vapid-keys
# NEXT_PUBLIC_VAPID_PUBLIC_KEY=
# VAPID_PRIVATE_KEY=
# VAPID_SUBJECT=mailto:support@example.com

# ---- Encryption ------------------------------------------------------
# Required before storing SSNs. 32+ chars, keep stable across deploys.
# DATA_ENCRYPTION_KEY=
KeyRequiredDescription
MONGODB_URIRequiredMongoDB Atlas or self-hosted connection string. In development it falls back to mongodb://localhost:27017/PropertyPro when omitted.
MONGODB_DBOptionalDatabase name override, only used to build the localhost URI when MONGODB_URI is unset.
BETTER_AUTH_SECRETRequiredSigns sessions and tokens. Generate with openssl rand -base64 32. Falls back to AUTH_SECRET then NEXTAUTH_SECRET, so v2.x deployments keep working untouched.
BETTER_AUTH_URLRequiredPublic base URL with no trailing slash, used for callbacks and links in email. Falls back to NEXTAUTH_URL then NEXT_PUBLIC_APP_URL.
SETUP_SECRETOptionalUnlocks the one-time POST /api/setup/create-admin bootstrap. The endpoint is disabled while unset and refuses to run once any administrator exists. Remove it after creating your first admin.
UPLOAD_STORAGE_PROVIDEROptionallocal (default, writes to ./uploads) or cloud (Cloudflare R2). Serverless hosts need cloud — their filesystem does not persist between requests.
R2_*OptionalAccount ID, access key, secret, bucket, and public URL. Required only when UPLOAD_STORAGE_PROVIDER=cloud.
MAPS_PROVIDEROptionalleaflet (OpenStreetMap, the default, needs no key), google, or disabled. Only a fallback — once a settings document exists, Settings → Maps wins.
NEXT_PUBLIC_GOOGLE_MAPS_API_KEYOptionalBrowser key for the Google provider, with Maps JavaScript API and Places API enabled. It is served to the browser and cannot be kept secret, so restrict it by HTTP referrer and set a quota cap.
GOOGLE_GEOCODING_API_KEYOptionalSeparate server-side key with the Geocoding API enabled, used only by db:backfill-coordinates. Restrict it by IP — a referrer-restricted key is rejected.
STRIPE_SECRET_KEY / STRIPE_WEBHOOK_SECRETOptionalFallback for the admin payment settings. The webhook secret belongs to the specific endpoint, not your account.
EMAIL_SERVER_*OptionalOutbound SMTP for invites, receipts, reminders, and the new inquiry and application emails. SMTP_* names are accepted as aliases. Fallback for the admin email settings.
NEXT_PUBLIC_DEMO_MODEOptionalShowcase deployments only. Renders the one-click demo login panel, and each row appears only if its NEXT_PUBLIC_DEMO_*_PASSWORD is also set. Never enable on a deployment holding real data.
VAPID_*OptionalRequired only for web push notifications. Generate with npx web-push generate-vapid-keys.
DATA_ENCRYPTION_KEYOptionalRequired before storing SSNs or other encrypted tenant identity data. At least 32 characters, and it must stay stable across deployments.

Upgrading from v2.x? Keep your existing secret

BETTER_AUTH_SECRET falls back to AUTH_SECRET and then NEXTAUTH_SECRET, so no env change is required. If you do switch to the new names, copy the same value across — generating a new secret signs every user out.

Generate a secret fast

Run openssl rand -base64 32 for BETTER_AUTH_SECRET, and openssl rand -hex 32 for SETUP_SECRET.

Chapter 07 · Database Setup

Connect MongoDB & seed data

PropertyPro stores application data in MongoDB. Create a MongoDB Atlas cluster or self-hosted database, then point MONGODB_URI at it before starting the app.

  1. 1

    Create a MongoDB database

    Create a MongoDB Atlas cluster and database named propertypro, or prepare a self-hosted MongoDB instance.

    bash
    # MongoDB Atlas example
    MONGODB_URI=mongodb+srv://username:password@cluster0.kbnje.mongodb.net/propertypro
  2. 2

    Allow network access

    In MongoDB Atlas, add your local IP address for development and your hosting provider's outbound IPs for production.

    txt
    Atlas → Network Access → Add IP Address
  3. 3

    Seed demo data (optional)

    Loads demo properties, units, and tenants so the dashboard isn't empty. It creates no login — use the setup endpoint for that.

    bash
    pnpm db:seed
  4. 4

    Run migrations when upgrading

    Fresh installs need none of these. Upgrading from v2.x requires the Better Auth migration — the app returns 503 on every request until it has run. Preview any of them with --dry-run.

    bash
    pnpm db:migrate-better-auth --dry-run   # preview, writes nothing
    pnpm db:migrate-better-auth             # required, v2.x -> v3.0
    pnpm db:migrate-email-index             # allows re-creating deleted tenants
    pnpm db:backfill-coordinates            # puts pre-3.0 properties on the map
  5. 5

    Inspect with MongoDB Compass

    Use MongoDB Compass or the Atlas Data Explorer to browse collections and verify seeded records.

    txt
    mongodb+srv://username:password@cluster0.kbnje.mongodb.net/propertypro

Production: skip the seed

Never run pnpm db:seed against a live database — it inserts demo records intended for evaluation. Migrations are safe to re-run; they skip anything already done.

db:backfill-coordinates is a dry run by default

It lists what it would geocode, makes no API calls, and writes nothing. To apply, re-run with GEOCODE_APPLY=1 GEOCODE_CONFIRM=BACKFILL_COORDINATES and a GOOGLE_GEOCODING_API_KEY set — the script has no OpenStreetMap path, so it needs a Google key even on Leaflet installs.

Chapter 08 · Running Locally

Start the dev server

Two scripts cover 99% of dev work: dev and build. The PWA service worker is disabled in dev to keep iteration fast.

  1. 1

    Start in development

    Hot-reload, fast refresh, and source maps. Defaults to port 3000.

    bash
    pnpm dev
  2. 2

    Run a production build

    Compiles, treeshakes, and runs Next.js production server. Use this to validate before deploying.

    bash
    pnpm build
    pnpm start
  3. 3

    Visit the public site and the dashboard

    The site root is now the public marketing homepage, not a redirect to sign-in. The dashboard lives behind /auth/signin.

    txt
    Public site:  http://localhost:3000
    Properties:   http://localhost:3000/properties
    Dashboard:    http://localhost:3000/dashboard

No account yet?

PropertyPro ships without any login. Create your first administrator with the SETUP_SECRET bootstrap covered in Quickstart and Admin & Roles.

Turn the public site off

If you only want the management dashboard, open Dashboard → Public Site and switch the master toggle off. The root URL then behaves as it did in v2.x — sign-in for guests, dashboard for signed-in users.

Chapter 09 · Cloudflare R2

Configure file storage with Cloudflare R2

PropertyPro uses Cloudflare R2 for property images, tenant documents, and other uploaded files. Configure one private write path through R2 API credentials and one public read URL for displaying saved files.

  1. 1

    Create an R2 bucket

    In Cloudflare, open Storage & databases -> R2 and create a bucket for PropertyPro uploads. Use a lowercase bucket name with numbers and hyphens only, then place that exact bucket name in R2_BUCKET_NAME.

    bash
    R2_BUCKET_NAME=your-r2-bucket-name
  2. 2

    Create a scoped R2 API token

    From the R2 overview, open Manage API Tokens, create an API token with Object Read & Write permission, and scope it to the PropertyPro bucket. Copy the Access Key ID, Secret Access Key, and account ID before leaving the confirmation screen.

    bash
    R2_ACCOUNT_ID=your-r2-account-id
    R2_ACCESS_KEY_ID=your-r2-access-key-id
    R2_SECRET_ACCESS_KEY=your-r2-secret-access-key
    R2_BUCKET_NAME=your-r2-bucket-name
  3. 3

    Enable public file delivery

    For production, connect a custom domain such as assets.yourdomain.com to the bucket. For local testing only, you may enable the Cloudflare-managed r2.dev public development URL. Set both public URL variables to the exact origin with no trailing slash.

    bash
    # Production custom domain
    R2_PUBLIC_URL=https://your-custom-domain.com
    NEXT_PUBLIC_R2_PUBLIC_URL=https://your-custom-domain.com
    
    # Local or staging with R2 public development URL
    # R2_PUBLIC_URL=https://pub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.r2.dev
    # NEXT_PUBLIC_R2_PUBLIC_URL=https://pub-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.r2.dev
  4. 4

    Allow the image host in Next.js

    The bundled next.config.ts already allows r2.dev and r2.cloudflarestorage.com image hosts. If you use a custom R2 domain and render uploaded images through next/image, add that hostname to images.remotePatterns, then rebuild.

    next.config.ts
    ts
    images: {
      remotePatterns: [
        {
          protocol: "https",
          hostname: "assets.yourdomain.com",
          port: "",
          pathname: "/**",
          search: "",
        },
      ],
    }
  5. 5

    Verify uploads

    Restart the app after changing environment variables, upload a property image or tenant document, then open the saved file URL. If the upload succeeds but previews fail, check the public bucket URL and image remote pattern first.

R2 S3 endpoint format
txt
https://<ACCOUNT_ID>.r2.cloudflarestorage.com

Keep write credentials server-only

Never expose R2_ACCESS_KEY_ID or R2_SECRET_ACCESS_KEY in NEXT_PUBLIC_* variables. Only NEXT_PUBLIC_R2_PUBLIC_URL should be readable by the browser.

Use custom domains in production

Cloudflare's r2.dev public development URLs are intended for non-production traffic. A custom domain gives you normal Cloudflare cache, access control, WAF, and bot-management options.

Chapter 10 · Stripe Payments

Accept rent payments online

PropertyPro uses Stripe Checkout for one-time invoices and Stripe Customer Portal for tenant self-service. Both work with test keys out of the box.

  1. 1

    Get your API keys

    Stripe Dashboard → Developers → API keys. Copy the secret and publishable keys into .env.local.

  2. 2

    Create a webhook endpoint

    Point Stripe at /api/webhooks/stripe on your deployed URL. Subscribe to checkout.session.completed and invoice.payment_succeeded.

    bash
    # Webhook URL
    https://yourdomain.com/api/webhooks/stripe
  3. 3

    Test webhooks locally

    Use the Stripe CLI to forward events to your dev server while developing.

    bash
    stripe listen --forward-to localhost:3000/api/webhooks/stripe
  4. 4

    Switch to live mode

    When ready, swap test keys (sk_test_*, pk_test_*) for live keys (sk_live_*, pk_live_*) and re-create the webhook in live mode.

Chapter 11 · Email / SMTP

Wire up transactional email

Lease reminders, payment receipts, password resets, and tenant invites all go through one SMTP transport. We've tested Resend, SendGrid, Postmark, and standard SMTP servers.

Resend (recommended)

Easiest for new buyers. Free tier covers most small portfolios. EMAIL_SERVER_HOST=smtp.resend.com, EMAIL_SERVER_USER=resend.

SendGrid

Higher limits and better deliverability for large portfolios. EMAIL_SERVER_HOST=smtp.sendgrid.net.

Postmark

Best for transactional-only email. EMAIL_SERVER_HOST=smtp.postmarkapp.com.

Gmail / generic SMTP

Works for testing but rate-limited. Not recommended for production.

Verify your sender domain

Always set up SPF, DKIM, and DMARC for EMAIL_FROM's domain — without it, emails land in spam.

Chapter 12 · Push Notifications

Enable web push

Push notifications use the Web Push API with VAPID keys. Tenants and admins get instant alerts for payments, requests, and chats.

  1. 1

    Generate VAPID keys

    Run the bundled generator. Copy both keys into .env.local.

    bash
    pnpm gen:vapid
  2. 2

    Set NEXT_PUBLIC_VAPID_PUBLIC_KEY

    The client uses this public key to subscribe browsers to the push service.

  3. 3

    Set VAPID_PRIVATE_KEY

    The server uses this to sign push payloads. Never expose it to the browser.

  4. 4

    Test from the dashboard

    Sign in, allow notifications when prompted, then go to Settings → Notifications → Send test.

Chapter 13 · Branding & Theme

Make it yours

All brand assets live under /public and the color palette is defined as Tailwind CSS variables. No code changes required for a basic rebrand.

  1. 1

    Replace the logo

    Drop your SVG logo at /public/logo.svg. The dashboard reads from this path automatically.

  2. 2

    Update the favicon & PWA icons

    Replace /public/favicon.ico and the icons under /public/icons/ (sizes 192, 256, 384, 512).

  3. 3

    Tweak brand colors

    Edit the --brand-* tokens in app/globals.css. Tailwind picks them up across the app.

    app/globals.css
    css
    :root {
      --brand-50:  oklch(0.97 0.02 230);
      --brand-500: oklch(0.62 0.18 230);
      --brand-700: oklch(0.45 0.18 230);
    }
  4. 4

    Update site metadata

    Edit app/layout.tsx to change the default title, description, and OG image.

Chapter 14 · Multi-language

Add or edit languages

PropertyPro ships with English, Arabic, French, Spanish, and Bengali. Locales live as JSON files under /messages and are loaded with next-intl.

  1. 1

    Edit existing strings

    Open /messages/<locale>.json and edit values directly. Changes hot-reload in dev.

  2. 2

    Add a new language

    Copy /messages/en.json to /messages/<your-locale>.json and translate values. Then register it in lib/i18n.ts.

    lib/i18n.ts
    ts
    export const locales = ["en", "ar", "fr", "es", "bn", "de"] as const;
    export const defaultLocale = "en";
  3. 3

    RTL support

    Arabic is enabled by default and switches the layout direction automatically. Add other RTL locales in lib/i18n.ts → rtlLocales.

Chapter 15 · Deploy to Vercel

One-click deploy (recommended)

Vercel is the fastest path to production. The whole flow takes under 10 minutes once your environment variables are ready.

  1. 1

    Push to a GitHub repo

    Create a private GitHub repo and push the source. Vercel will pull from it.

    bash
    git init
    git add .
    git commit -m "Initial PropertyPro setup"
    git remote add origin git@github.com:you/propertypro.git
    git push -u origin main
  2. 2

    Import the repo into Vercel

    vercel.com → New Project → Import. Vercel auto-detects Next.js — no build config needed.

  3. 3

    Add environment variables

    Project Settings → Environment Variables. Paste every key from your .env.local. Set APP_URL, NEXTAUTH_URL, and AUTH_URL to your Vercel URL.

  4. 4

    Connect MongoDB Atlas

    Add Vercel's outbound access to MongoDB Atlas Network Access, then set MONGODB_URI in Vercel before deploying.

    bash
    MONGODB_URI=mongodb+srv://username:password@cluster0.kbnje.mongodb.net/propertypro
  5. 5

    Add your custom domain

    Project Settings → Domains. SSL is provisioned automatically.

Chapter 16 · Deploy to VPS / Docker

Self-host on your own server

If you'd rather run on a VPS, PropertyPro ships with a production-ready Dockerfile and a sample docker-compose.yml that can run MongoDB alongside the app.

  1. 1

    Build & start the stack

    From the project root, build the images and run them in the background.

    bash
    docker compose up -d --build
  2. 2

    Seed demo data inside the container

    Load demo records only when you are setting up a test or evaluation instance.

    bash
    docker compose exec app pnpm db:seed
  3. 3

    Front with nginx + SSL

    Use nginx (or Caddy) to terminate TLS and proxy to localhost:3000. Caddy will provision Let's Encrypt automatically.

    Caddyfile
    nginx
    propertypro.yourdomain.com {
      reverse_proxy localhost:3000
    }
docker-compose.yml
yaml
services:
  app:
    build: .
    ports: ["3000:3000"]
    env_file: .env.local
    environment:
      MONGODB_URI: mongodb://propertypro:change-me@mongo:27017/propertypro?authSource=admin
    depends_on: [mongo]
  mongo:
    image: mongo:7
    restart: unless-stopped
    environment:
      MONGO_INITDB_ROOT_USERNAME: propertypro
      MONGO_INITDB_ROOT_PASSWORD: change-me
    volumes:
      - mongodata:/data/db
    ports: ["27017:27017"]

volumes:
  mongodata:

Use a managed database

Self-hosting MongoDB works but you'll own backups, replication, and upgrades. MongoDB Atlas offloads that work.

Chapter 17 · Custom Domain & SSL

Point your domain at PropertyPro

Once your app is live, route a custom domain to it. Both Vercel and Caddy provision SSL certificates for free.

  1. 1

    Add an A or CNAME record

    For Vercel, add a CNAME pointing to cname.vercel-dns.com. For your VPS, point an A record at the server IP.

  2. 2

    Verify in Vercel / Caddy

    Vercel auto-verifies once DNS propagates. Caddy reissues the certificate on the next request.

  3. 3

    Update application URLs

    Switch APP_URL, NEXTAUTH_URL, and AUTH_URL to https://yourdomain.com and redeploy. These are used in emails and auth callbacks.

Chapter 18 · Admin & Roles

Set up your team

PropertyPro ships with no accounts at all. Create the first administrator through the one-time bootstrap endpoint, then invite your team with scoped permissions.

  1. 1

    Create the first administrator

    Add SETUP_SECRET to your environment and restart, then POST once to the bootstrap endpoint. It is disabled while the secret is unset and refuses to run once any administrator exists, so it cannot be replayed.

    bash
    openssl rand -hex 32   # use as SETUP_SECRET
    
    curl -X POST https://your-domain.com/api/setup/create-admin \
      -H "Content-Type: application/json" \
      -H "x-setup-secret: $SETUP_SECRET" \
      -d '{"email":"you@example.com","password":"<strong password>","firstName":"Your","lastName":"Name"}'
  2. 2

    Remove the setup secret

    Delete SETUP_SECRET from your environment and redeploy. Its job is done.

  3. 3

    Invite your team

    Settings → Team → Invite. Each invitee gets an email with a one-time setup link and chooses their own password — minimum 8 characters as of 3.0.

  4. 4

    Assign roles

    Built-in roles are Admin, Manager, and Tenant. Manager and Tenant are now editable; Admin stays locked because it is the only guaranteed holder of role_management. Create custom roles under Settings → Roles.

  5. 5

    Grant delete rights explicitly

    New in 3.0: lease_delete, tenant_delete, payment_delete, document_delete, property_delete, and user_delete are separate permissions and are never implied by the matching edit grant. The built-in Manager ships without them, so managers cannot delete until you grant them. Bulk deletes also need bulk_operations.

  6. 6

    Audit log

    Every sensitive action is recorded, including public-site edits. Review under Settings → Activity.

Never use seed:demo to create a real admin

That script exists to populate public showcase deployments at well-known email addresses. It refuses to run under NODE_ENV=production unless ALLOW_PRODUCTION_SEED is set, and it will not run at all without demo passwords in the environment.

Reserved role names

Custom roles can no longer be named after a built-in role or its aliases (super_admin, landlord, owner, property_manager, and similar). Before 3.0 such a role was created but silently granted the full built-in permission set — check any custom roles created on an older version.

Don't share admin accounts

Shared logins break audit trails. Always invite a new user — even for short-term contractors.

Chapter 19 · Updates & Backups

Stay current and recoverable

PropertyPro ships minor updates monthly. Your data survives every release. v3.0 is the one upgrade that needs a database migration — the bundled UPGRADE.md covers it in full, including rollback.

  1. 1

    Back up first

    Settings → Backups → Create backup, or run mongodump. On the v3.0 upgrade this is the entire rollback plan.

  2. 2

    Download the latest build

    From your CodeCanyon downloads. Compare the version against the Changelog to plan the update.

  3. 3

    Diff and merge

    Use git or your favorite diff tool to merge the new files, preserving .env.local and your local ./uploads directory.

  4. 4

    Install and rebuild

    Install dependencies and run a production build after merging each release.

    bash
    pnpm install
    pnpm build
  5. 5

    v2.x → v3.0 only: run the auth migration

    v3.0 replaces NextAuth with Better Auth. Passwords, properties, leases, payments, and tenants are untouched, but every user is signed out once because the old JWT logins have no server-side record to convert. The migration is additive and safe to re-run.

    bash
    pnpm db:migrate-better-auth --dry-run   # preview
    pnpm db:migrate-better-auth
  6. 6

    v2.x → v3.0 only: verify

    Sign in with an existing account and its existing password, confirm Settings → Security lists your session, and check that terminating a session signs that device out.

  7. 7

    Schedule database backups

    Use MongoDB Atlas automated backups or set up nightly mongodump on a VPS.

    bash
    # Cron: nightly backup at 02:00
    0 2 * * * mongodump --uri="$MONGODB_URI" --archive=/backups/propertypro-$(date +\%F).archive --gzip

Customized files and the v3.0 upgrade

v3.0 changed 74 files mechanically. If you edited any of them, your copy is preserved and then out of step with the rest of the app. The usual fix is one import: next-auth/react becomes @/lib/auth-client, with useSession, signIn, and signOut keeping the same signatures. The old next-auth package is still installed so customized files don't break your build, but it is scheduled for removal in v3.1.

Rolling back v3.0

The migration copies password hashes rather than moving them, so restoring the v2.3 files works as long as nobody has changed their password since upgrading. Anyone who has must reset, or you restore the backup.

Chapter 20 · Maps & Geocoding

Put your properties on the map

New in 3.0. Maps are a pluggable provider chosen in the app, not in a build step — the database setting wins over the environment, so switching provider needs no redeploy.

  1. 1

    Pick a provider

    Dashboard → Settings → Maps offers OpenStreetMap, Google Maps, or no maps. The same form is embedded in Public Site → Settings; both write the same setting.

  2. 2

    OpenStreetMap — the default, no setup

    Works on a fresh install with no API key, no Google Cloud account, and no credit card. Address search uses Nominatim, debounced and limited to queries of 3 characters or more to respect the OSM usage policy.

  3. 3

    Google Maps — optional

    Reveals an API key field. Enable Maps JavaScript API and Places API on the key, and restrict it by HTTP referrer. Google Maps requires a Cloud billing account.

    bash
    # Fallback only — Settings -> Maps takes precedence
    MAPS_PROVIDER=google
    NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your-browser-key
  4. 4

    Geocode your existing properties

    Properties created before 3.0 have no coordinates and stay off the maps until you backfill them. Dry run by default: it lists what it would do, makes no API calls, and writes nothing.

    bash
    pnpm db:backfill-coordinates
    
    # Apply for real
    GEOCODE_APPLY=1 GEOCODE_CONFIRM=BACKFILL_COORDINATES \
      GOOGLE_GEOCODING_API_KEY=your-server-key \
      pnpm db:backfill-coordinates

The browser key is public — restrict it

A Google Maps browser key is served to every visitor by design and cannot be kept secret. Restrict it by HTTP referrer to your own domains and set a daily quota cap in the Cloud console, or an unrestricted key can be lifted and billed against your account.

The backfill needs a Google key either way

It geocodes through Google only — there is no Nominatim path — so it needs a separate server-side, IP-restricted key with the Geocoding API enabled, even on installs running the free OpenStreetMap provider. It skips properties that already have coordinates, so an interrupted run can simply be re-run.

Heavy traffic? Move off the public OSM servers

The Leaflet default points at OSM's shared tile and Nominatim infrastructure, which their usage policy says is unsuitable for production traffic. A busy install should switch to its own or a commercial tile and geocoding host.

Chapter 21 · Public Site

Control the public marketing website

New in 3.0. PropertyPro serves a public home page, properties browser, property and unit detail pages, and a contact page from the same install — all of it switchable and editable from Dashboard → Public Site.

  1. 1

    Switch pages on or off

    A master toggle plus per-page switches for Home, Properties, Property detail, and Contact. A page is live only when both its own switch and the master switch are on, and toggles save the moment you flip them.

  2. 2

    Edit the copy

    Five editors cover Brand & navigation, Home, Properties, Contact, and FAQ — wordmark, logo, header and footer links, hero headline and background, feature cards, how-it-works steps, FAQ categories, contact methods, and per-page SEO and social share metadata.

  3. 3

    Accept rental applications

    With Property detail on, visitors can apply for a unit through the checkout: lease terms, occupants, income, and screening consent, filed as a rental application. No payment is taken. Switch that page off and both checkout URLs redirect instead of accepting applications.

  4. 4

    Grant access to the module

    The new public_site_management permission controls it. Built-in Admin has it, and existing custom admin roles holding system_settings keep access without any change.

  5. 5

    Reset to defaults

    Public Site → Module settings restores any single section, or the whole public site, to the copy the app shipped with, behind a confirmation dialog.

Switching a page off never deletes anything

Signed-out visitors are redirected to /auth/signin and signed-in users to /dashboard — both targets editable, and both must be paths inside the app. Your stored copy comes back untouched when you switch the page on again.

Changes go live immediately

The public routes render per request rather than from a build-time prerender, and every save clears the public route cache, so shared header and footer edits land on every page at once.

Image fields are paths, not uploads

The hero background, feature card, and how-it-works images are path entry with a live preview. Put the file under /public first, then reference it — the editor warns when the path does not load.

Chapter 22 · Troubleshooting

Fix common issues

Most install problems fall into the same handful of buckets. Walk through these before opening a support ticket.

Every request returns 503 "database schema v0 but the application requires v1"

The v3.0 auth migration has not run, or did not finish. Run pnpm db:migrate-better-auth. This message is deliberate — v3.0 refuses to serve an un-migrated database rather than answering correct passwords with "invalid credentials".

"Invalid email or password" for a password you know is right

Same cause as the 503 — sign-in is not covered by the schema gate. Run pnpm db:migrate-better-auth. If the migration has run, check that BETTER_AUTH_SECRET still holds your old NEXTAUTH_SECRET value; changing it signs everyone out.

Build fails on next-auth/react

A file you customised still imports the old auth client. Change it to @/lib/auth-client — useSession, signIn, and signOut keep the same signatures and return shapes.

Properties don't appear on the map

Properties created before 3.0 have no coordinates. Run pnpm db:backfill-coordinates with GEOCODE_APPLY=1 and a GOOGLE_GEOCODING_API_KEY. New properties get coordinates from the address autocomplete on the property form.

Managers can no longer delete leases or tenants

Expected in 3.0 — deleting now needs lease_delete, tenant_delete, payment_delete, document_delete, or user_delete, which no longer ride along with the edit grant. Edit the built-in Manager role or create a custom role to grant them.

MONGODB_URI connection refused

Check the username, password, database name, and Atlas Network Access allowlist. For self-hosted MongoDB, confirm the authSource value.

Inquiry and application emails never arrive

Everything email-shaped depends on SMTP under Settings → Email. Without it inquiries and applications are still stored and still badge the sidebar, but acknowledgements and staff alerts are not sent and dashboard replies are filed with an "Email failed" badge.

Stripe webhook signature invalid

Make sure STRIPE_WEBHOOK_SECRET matches the secret of the specific endpoint, not your account secret.

Emails landing in spam

Set up SPF, DKIM, and DMARC for your sender domain. Resend & SendGrid have step-by-step UIs for this.

Push notifications not arriving

iOS Safari requires the PWA to be installed via Add to Home Screen first. Web push is unavailable in Incognito mode.

pnpm install fails

Delete node_modules and pnpm-lock.yaml, run corepack enable, then pnpm install fresh.

File uploads silently fail

Check R2_* variables, NEXT_PUBLIC_R2_PUBLIC_URL, upload limits, and whether your Cloudflare R2 bucket allows public reads from the configured URL.

Updates·Last updated May 2, 2026
↑ Back to top