A comprehensive, AI-powered dental hospital management system built with Next.js 16, designed for dental clinics and multi-branch hospital chains in India. Includes GST-compliant billing, Razorpay/PhonePe/Paytm payments, patient portal, tele-dentistry, 16 AI skills, and more — completely free and open source.
Looking for dental practice management software in India? DentalERP is a free alternative to expensive proprietary dental software. Self-host it on your own server with full control over your patient data.
- Patient Management — Records, medical history, dental charting, document uploads
- Appointment Scheduling — Calendar view, slot management, reminders, no-show prediction
- Treatment Plans — Treatment tracking, procedure catalog, AI-assisted treatment advice
- Billing & Invoicing — GST-compliant invoicing, payment tracking, payment plans (EMI)
- Prescriptions — Digital prescriptions, medication database, print/PDF export
- Inventory Management — Stock tracking, low-stock alerts, AI-powered demand forecasting
- Lab Integration — Lab order management, status tracking, work coordination
- Staff Management — Roles & permissions, attendance, doctor schedules
- AI Skills (16 built-in) — Treatment advisor, smart scheduler, billing agent, patient intake, inventory forecaster, cashflow forecaster, patient segmentation, claim analyzer, consent generator, dynamic pricing, and more
- Patient Portal — Online booking, medical records access, digital intake forms
- Insurance & Claims — Insurance verification, claim submission, auto-adjudication
- CRM & Loyalty — Patient segmentation, loyalty points, referral tracking
- Communications — SMS/Email/WhatsApp messaging, campaign management, marketing automation
- Tele-Dentistry — Video consultations via Jitsi Meet integration
- Sterilization Tracking — Instrument management, sterilization logs, compliance reporting
- Dental Imaging — Interactive SVG dental arch viewer with condition mapping
- IoT Device Integration — Medical device data logging and monitoring
- Payment Gateways — Razorpay, PhonePe, Paytm integration (encrypted credentials)
- Reports & Analytics — Revenue, appointments, treatment stats, exportable to Excel
- Audit Logging — Full audit trail for compliance
- Multi-branch Support — Hospital-scoped data isolation via NextAuth
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript 5 |
| Database | MySQL 8 via Prisma 5 ORM |
| Auth | NextAuth v5 (beta) with credentials provider |
| UI | Tailwind CSS 3 + Radix UI + shadcn/ui |
| Charts | Recharts |
| Forms | React Hook Form + Zod validation |
| AI | OpenRouter (multi-model gateway) |
| Nodemailer (SMTP) | |
| Testing | Vitest + Playwright + Testing Library |
| CI/CD | GitHub Actions |
- Node.js 20 or later
- npm 10 or later
- Docker with Compose v2 — recommended, but optional
git clone https://github.com/abinauv/dental-erp.git
cd dental-erp
npm install
cp .env.example .env
# Start MySQL, Redis, MinIO and Mailpit
docker compose -f docker-compose.dev.yml up -d
# Set DATABASE_URL in .env to match the container:
# DATABASE_URL="mysql://root:dental@localhost:3306/dental_erp"
npx prisma migrate deploy # create the schema
npx prisma db seed # sample data (optional)
npm run devOpen http://localhost:3000.
You still need to fill in NEXTAUTH_SECRET, ENCRYPTION_KEY and CRON_SECRET
in .env — the app will not start without them. Each one has a
node -e "..." command beside it in .env.example that prints a valid value.
The app runs on your machine, not in Docker. Compose brings up the backing
services only. Bind-mounting node_modules into a container is slow enough on
Windows and macOS to spoil the edit-reload loop, and native hot reload is
better. docker-compose.dev.yml is a convenience for contributors and is
never suitable for production — every credential in it is weak and public.
| Service | Port | What it is for |
|---|---|---|
| MySQL | 3306 | The application database |
| Redis | 6379 | Reserved for caching and queues; nothing uses it yet |
| MinIO | 9000, 9001 | S3-compatible storage; reserved for Phase 3. Console on 9001 |
| Mailpit | 1025, 8025 | Captures every outbound email. Read them at http://localhost:8025 |
Mailpit is the useful one right away. Point the SMTP_* variables at it (the
values are commented into .env.example) and you can exercise password resets,
invitations and reminders with no credentials and no risk of emailing a real
person.
createbuckets runs once, creates the MinIO bucket and exits. Seeing it as
Exited (0) in docker compose ps is success, not a failure.
Useful commands:
docker compose -f docker-compose.dev.yml logs -f # tail the services
docker compose -f docker-compose.dev.yml down # stop, keep the data
docker compose -f docker-compose.dev.yml down -v # stop and wipe the dataPort 3306 already in use? You have MySQL installed locally. Either stop it,
or change the host port in docker-compose.dev.yml to '3307:3306' and update
DATABASE_URL to match.
If you were running this stack before August 2026, its compose project was renamed from
dental-erptodental-erp-dev, so that a production stack on the same machine cannot end up sharing volumes with it. Your old containers and volumes are still there under the previous name; clean them up once with:docker compose -p dental-erp -f docker-compose.dev.yml down -v
Install MySQL 8 yourself
Docker is not a requirement. With MySQL 8.0+ installed locally:
# Verify MySQL is accessible
mysql -u root -p -e "SELECT 1"
# Create the database
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS dental_erp"Then set DATABASE_URL in .env to your own credentials and continue from
npx prisma migrate deploy above.
prisma migrate deploy is the recommended path — it is repeatable and safe to
re-run when you upgrade. npx prisma db push also works and is handy while
developing, but it applies the schema without recording it in
prisma/_prisma_migrations, so later migrate deploy runs will fail against
that database.
Upgrading a database that was created with prisma db push
Tell Prisma the existing migrations are already reflected in your schema, then deploy as normal from that point on:
npx prisma migrate resolve --applied 20260127152236_multi_tenancy
npx prisma migrate resolve --applied 20260728120000_sync_schema_with_models
npx prisma migrate resolve --applied 20260801150000_inventory_lab_prisma_models
npx prisma migrate deploy| Role | Password | |
|---|---|---|
| Super Admin | admin@demo-dental.com |
Admin@123 |
Warning: Change the default password immediately in production.
See .env.example for all available variables. Key ones:
| Variable | Required | Description |
|---|---|---|
DATABASE_URL |
Yes | MySQL connection string |
NEXTAUTH_URL |
Yes | App URL (e.g., http://localhost:3000) |
NEXTAUTH_SECRET |
Yes | Random secret for session encryption |
ENCRYPTION_KEY |
Yes | 64-char hex string for AES-256-GCM encryption |
CRON_SECRET |
Yes | Secret for securing cron job endpoints |
OPENROUTER_API_KEY |
No | Required for AI features |
SMTP_HOST / SMTP_USER / SMTP_PASSWORD |
No | Required for email features |
SMS_API_KEY |
No | Required for SMS features |
npm run dev # Start development server
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint
npm run test # Run unit/integration tests (Vitest)
npm run test:watch # Run tests in watch mode
npm run test:coverage # Run tests with coverage report
npm run test:e2e # Run end-to-end tests (Playwright)
npm run test:all # Run all tests
# Database
npm run db:generate # Generate Prisma client
npm run db:push # Push schema without recording a migration (dev only)
npm run db:migrate # Create a migration from schema changes (development)
npm run db:migrate:deploy # Apply pending migrations (setup and deploys)
npm run db:seed # Seed sample data
npm run db:studio # Open Prisma Studio (DB GUI)dental-erp/
├── app/ # Next.js App Router
│ ├── (auth)/ # Authentication pages (login, signup, etc.)
│ ├── (dashboard)/ # Dashboard pages (all modules)
│ └── api/ # API routes
├── components/ # Reusable React components
│ ├── layout/ # Dashboard shell, sidebar, header
│ ├── ui/ # shadcn/ui components
│ └── imaging/ # Dental imaging components
├── config/ # App configuration (navigation, etc.)
├── lib/ # Utilities, helpers, AI skills
│ ├── ai/ # AI skill definitions
│ ├── api-helpers.ts # Auth & API utilities
│ └── prisma.ts # Prisma client singleton
├── prisma/
│ ├── schema.prisma # Database schema
│ └── seed.ts # Database seeder
├── __tests__/ # Test files
│ ├── unit/ # Unit tests
│ ├── integration/ # Integration tests (API routes)
│ ├── components/ # Component tests
│ ├── e2e/ # Playwright E2E tests
│ └── accessibility/ # Accessibility tests
├── .github/
│ └── workflows/ci.yml # CI pipeline
└── public/ # Static assets
The project has comprehensive test coverage:
- Unit tests — Business logic, utilities, AI skills
- Integration tests — API route handlers with mocked Prisma
- Component tests — React components with Testing Library
- E2E tests — Full user flows with Playwright
- Accessibility tests — WCAG 2.1 compliance with axe-core
# Run all unit/integration tests
npm test
# Run with coverage
npm run test:coverage
# Run E2E tests (requires running server)
npm run test:e2eSELF_HOSTING.md is the full guide — the short version is that you do not need a checkout, Node.js, or a build step:
mkdir -p /opt/dental-erp && cd /opt/dental-erp
curl -fsSLO https://raw.githubusercontent.com/abinauv/dental-erp/main/docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/abinauv/dental-erp/main/.env.production.example -o .env
# fill in .env, then
docker compose up -dThat pulls a published multi-architecture image (linux/amd64 and
linux/arm64) from ghcr.io/abinauv/dental-erp, waits for MySQL, runs the
database migrations as a one-shot container, and starts the app. Add
-f docker-compose.caddy.yml for HTTPS with automatic certificates.
SELF_HOSTING.md also covers backups and restores, upgrading, and what to do when something is wrong.
docker run -p 3000:3000 --env-file .env -v dental-uploads:/app/uploads ghcr.io/abinauv/dental-erp:latestMount a volume at
/app/uploads. On the defaultSTORAGE_DRIVER=local, uploaded files — patient documents, scans, signed consent forms — are written to the container filesystem. Without a volume they are destroyed the moment the container is replaced, which is every single redeploy. The-vflag above is not optional in any deployment you care about.Already running without one? Copy your files out before you next redeploy:
docker cp <container>:/app/uploads ./uploads-backupthen recreate the container with the volume mounted and copy them back. Setting
STORAGE_DRIVER=s3removes this whole class of problem by getting uploads off the container filesystem — see docs/STORAGE.md.
Running the image this way does not migrate the database. The image carries the Prisma CLI so it can do that itself:
docker run --rm --env-file .env ghcr.io/abinauv/dental-erp:latest node node_modules/prisma/build/index.js migrate deployTwo endpoints, and the distinction between them matters:
| Endpoint | Purpose | Checks the database | Use for |
|---|---|---|---|
/api/health |
Liveness | No | "Is this process alive?" |
/api/ready |
Readiness | Yes | "Should this instance get traffic?" |
Point liveness probes at /api/health and readiness probes at /api/ready.
Pointing a liveness probe at /api/ready means a brief database outage will
make your orchestrator kill and restart otherwise-healthy containers, turning a
short blip into a restart loop. /api/ready returns 503 when the database is
unreachable — it never throws.
The image carries a HEALTHCHECK against /api/health, so docker ps reports
container health with no extra configuration.
npm run build
npm startDeploying from the published image, all you need is Docker Engine 24+ with the Compose plugin. Building or developing from source needs:
- Node.js 20+
- MySQL 8.0+ (with a dedicated database)
- Reverse proxy (nginx/Caddy) for HTTPS in production —
docker-compose.caddy.ymlis a working example - A persistent volume mounted at
/app/uploads
- Self-hosting — running DentalERP on your own server from a published image, with backups, TLS and upgrades.
- Infrastructure Roadmap — how DentalERP is packaged and deployed, and what is planned next. Feedback welcome, especially on the phases not yet built.
- File storage — local disk vs S3, and how to move between them without losing files.
- Localization — locale support and the message catalogue.
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
If you discover a security vulnerability, please follow our Security Policy. Do not open a public issue for security vulnerabilities.
This project is licensed under the MIT License — see the LICENSE file for details.
- Next.js by Vercel
- Prisma for database ORM
- shadcn/ui for UI components
- Radix UI for accessible primitives
- OpenRouter for AI model access