This guide explains how to set up MovaLab for the first time on a fresh installation. MovaLab is self-hosted using Docker and local Supabase.
When you deploy MovaLab for the first time, the database starts empty (only 3 system roles exist). The app includes a setup wizard at /onboarding that:
- Automatically activates when no superadmin exists in the database
- Generates a one-time setup token printed to the server terminal
- Guides you through creating the first superadmin account
- Includes an interactive tutorial after account creation
- Automatically disables after the first superadmin is created
Before starting, ensure you have:
- Node.js 18.0+ (Download)
- Docker Desktop installed and running (Download)
- Git for cloning the repository
git clone https://github.com/itigges22/MovaLab.git
cd MovaLab
npm run setupThe npm run setup script (scripts/first-time-setup.sh) automatically:
- Checks prerequisites (Node.js, Docker, Supabase CLI)
- Installs npm dependencies
- Creates
.env.localfrom the template (if it doesn't exist) - Starts local Supabase via Docker
- Applies all 7 database migrations
- Loads seed data (3 system roles: Superadmin, Client, No Assigned Role)
What gets created in the database:
- Tables with Row Level Security policies
- System roles (Superadmin, Client, No Assigned Role)
- Database functions (auto clock-out, week start date, etc.)
- Onboarding system tables (setup_tokens, onboarding_state)
No departments, users, accounts, or projects are created. The platform starts empty.
npm run devOpen http://localhost:3000.
- The app automatically redirects to
/onboarding - Check your terminal -- a one-time setup token is printed to the server console output
- Enter the setup token in the wizard
- Create your superadmin account (email + password)
- Follow the interactive tutorial that walks you through the platform
Once you're logged in as superadmin:
- Create Departments -- Go to Admin > Departments
- Create Roles -- Go to Admin > Roles (assign permissions and departments)
- Invite Team Members -- Go to Admin > Invite Users (sends email invitations)
- Create Accounts -- Add client accounts and assign account managers
- Set Up Workflows -- Go to Admin > Workflows to create workflow templates
Team members are invited via the invitation system:
- Go to Admin > Invite Users
- Enter the team member's email address
- An invitation email is sent (via Nodemailer in production, via Inbucket locally)
- In local development, check Inbucket at http://localhost:54324 to see the invitation email
- The invited user clicks the link, creates their account, and is assigned a role
The setup token is generated once and printed to the server terminal. It is stored in the setup_tokens database table and expires after use. This prevents unauthorized users from becoming superadmin.
The onboarding wizard creates a new Supabase Auth user as part of the flow. The superadmin account is tied to a verified email address.
After the first superadmin is created, the onboarding wizard is disabled. Visiting /onboarding after setup will redirect to the main app.
Cause: The dev server may not have started cleanly.
Solution:
- Stop the dev server (Ctrl+C)
- Run
npm run devagain - Visit http://localhost:3000 -- the token should appear in the terminal
Cause: A superadmin already exists in the database.
Solution: This is expected. If you need to start fresh:
npm run docker:reset
npm run devCause: Docker Desktop may not be running.
Solution:
- Start Docker Desktop
- Wait for it to fully initialize
- Run
npm run docker:startornpm run setup
Solution:
npm run docker:stop
npm run docker:start
npm run docker:healthOnce your superadmin account is created, the platform is ready for daily use:
- Invite your team via Admin > Invite Users
- Create client accounts via Accounts section
- Start projects and assign team members
- Build workflows to standardize your processes
- Track time with clock in/out or manual entry
The .env.local file is created automatically from .env.local.template during setup. Key variables:
| Variable | Default | Description |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
http://127.0.0.1:54321 |
Local Supabase API URL |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_DEFAULT_KEY |
sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH |
Local publishable key |
SUPABASE_SERVICE_ROLE_KEY |
(set in template) | Service role key for server-side operations |
NEXT_PUBLIC_APP_URL |
http://localhost:3000 |
Application URL |
NEXT_PUBLIC_DEMO_MODE |
false |
Enable demo mode (optional) |
SMTP_* |
(commented out) | SMTP settings for production email delivery |
Symptom: After resetting the database (npx supabase db reset), you get stuck in a loop between /welcome and /login, or the onboarding wizard doesn't appear.
Cause: Your browser has stale authentication tokens from a previous session. The tokens reference a user that no longer exists in the reset database, so the auth refresh fails (HTTP 400), but the browser keeps trying.
Fix: Clear all site data for your domain:
- Open browser DevTools (F12)
- Go to Application → Storage → click Clear site data
- Or open an incognito/private window and navigate to your site
Symptom: You click "Begin Setup" but no token appears in the server terminal.
Cause: In production mode (npm start), console.log is stripped. The setup token uses console.warn which survives production builds.
Fix: Check the terminal where npm start is running. The token appears as:
========================================
SUPERADMIN SETUP TOKEN
Token: abc123...
Expires in 15 minutes
========================================
If running in background (nohup npm start > movalab.log 2>&1 &), check the log:
tail -20 movalab.logSymptom: PGRST205 error — table not found in schema cache.
Cause: PostgREST (Supabase's API layer) caches the database schema. After applying new migrations, the cache is stale.
Fix: Restart Supabase to refresh the schema cache:
npx supabase stop --no-backup && npx supabase startSymptom: supabase start hangs, or containers keep dying.
Cause: Insufficient memory. Supabase runs ~12 Docker containers that need ~2-3 GB RAM. With Next.js and the OS, you need at least 4 GB total (8 GB recommended).
Fix:
docker system prune -f # Free up Docker disk/memory
systemctl restart docker # Restart Docker daemon
npx supabase start # Try againIf it persists, upgrade to an 8 GB VPS.
Symptom: Browser console shows "Mixed Content", "CORS policy", or "Content Security Policy" errors when trying to reach Supabase.
Cause: The browser is accessing the site via HTTPS but Supabase is on HTTP, or the Supabase URL doesn't match the CSP.
Fix: This is handled automatically by the setup script — Supabase is proxied through Nginx at /supabase/ so everything stays on the same origin. If you see these errors:
- Make sure Nginx is running:
systemctl status nginx - Make sure the config is correct:
nginx -t - Rebuild the app:
rm -rf .next && npm run build && npm start
Symptom: supabase start fails with "Rate exceeded" errors when pulling images.
Fix: Log into Docker Hub (free account gets 200 pulls/6hr instead of 100):
docker loginThen retry npx supabase start. Already-pulled images are cached.
- Docker Setup Guide -- Local development environment details
- Demo Mode Guide -- Running demos locally
- Contributing Guide -- Development workflow