This is the abbreviated version. The complete reference, including deployment topologies and backup strategy, is docs/self-hosting.md in the repository.
What you need
- Node.js 20 or later and npm.
- PostgreSQL 14 or later. Managed (Neon, Supabase, RDS) or your own — the app speaks standard Postgres.
- Somewhere to run it. Your own machine, a cloud server, or Vercel. Nothing here is Vercel-specific except the pre-declared cron schedule.
- A domain with HTTPS. Not optional. This application handles NRIC numbers and salary data.
1. Clone and install
git clone https://github.com/on-the-ground-ai/otg-hrms.git
cd otg-hrms
npm install2. Configure
cp .env.example .env.localTwo variables are mandatory. DATABASE_URL is your Postgres connection string. AUTH_SECRET signs session tokens — generate a fresh one, do not reuse a secret from anywhere else:
openssl rand -base64 32Generate NRIC_ENCRYPTION_KEY the same way. Without it the NRIC field is unavailable, which is the safe failure mode — the app refuses to store an identifier it cannot encrypt.
3. Create the schema
npm run db:migrateThis runs every migration in order and then the reference-data seeds: default leave types, and Singapore public holidays for 2026 and 2027. It is idempotent — running it twice is safe, and it is how you apply upgrades later.
4. Start it
npm run dev # development
npm run build # production
npm start5. Create your company
Go to /signup. This is the only unauthenticated write path in the application besides password reset, and it is rate-limited per IP. Creating a company gives you:
- The company record.
- Your account, as super admin — the highest role.
- A default set of leave types you can rename, deactivate or add to.
6. Add people
Use Admin → Onboarding rather than the plain create form: the wizard also sets up approvers and sends the welcome email. Each new joiner gets a one-time activation link and sets their own password. You never send anyone a password.
7. Before you invite the company
- 1Check the holiday calendar under Admin → Settings. Wrong holidays mean wrong leave deductions, and it is the mistake people notice.
- 2Check the leave entitlements for two or three employees with different join dates against what you actually promised them.
- 3Send yourself a leave request and approve it from the email link, to confirm outbound email and the approval tokens work end to end.
- 4Upload a receipt on a test claim, to confirm file storage is configured.
- 5Take a database backup and restore it somewhere else. An untested backup is a hypothesis, not a backup.
Optional pieces
Everything below is off by default and the system runs without it. Email is the one you will actually want.
| Feature | Needs | Without it |
|---|---|---|
| Outbound email | AUTH_RESEND_KEY, EMAIL_FROM | Mail queues and fails; people check the portal instead of their inbox |
| File uploads to Vercel Blob | BLOB_READ_WRITE_TOKEN | Files are written to local disk instead — the default |
| Receipt OCR | ANTHROPIC_API_KEY | Claim fields are typed by hand |
| Scheduled jobs | CRON_SECRET + a scheduler | No birthday alerts, probation reminders, holiday warnings or retention purge |