Skip to content

Creating a New Next.js Application

This guide walks you through creating a new Next.js application from scratch on the Proxmox host.

Estimated time: 5-10 minutes

Prerequisites

  • SSH access to Proxmox host
  • Node.js 22+ installed (already available on host)
  • K8s Supabase sandbox running (supabase-sandbox namespace)

Overview

The process consists of four stages:

  1. Create Next.js app using create-next-app
  2. Apply scaffold with Authentik auth, Supabase, and shadcn/ui
  3. Configure environment with Supabase credentials
  4. Run development server

Note: Authentication is now built-in. The scaffold includes the complete Authentik SSO pattern with AUTH_MODE switching.

The quickest way to create a new app:

/intents:scaffold-nextjs my-app

Or via the run-intent script:

./scripts/run-intent.sh intents/examples/create-nextjs-app.yaml --params app=my-app

This handles all stages automatically.


Manual Workflow

Stage 1: Create Next.js Application

Context: On Proxmox host

cd /root/projects

npx create-next-app@latest my-app \
  --typescript \
  --tailwind \
  --app \
  --src-dir \
  --eslint \
  --import-alias '@/*' \
  --no-turbopack

What these flags mean: - --typescript - Use TypeScript - --tailwind - Include Tailwind CSS v4 - --app - Use App Router (not Pages Router) - --src-dir - Use src/ directory structure (matches home-portal) - --eslint - Include ESLint configuration - --import-alias '@/*' - Set path alias for imports - --no-turbopack - Use standard Webpack bundler

Checkpoint: Verify app created:

ls /root/projects/my-app/package.json

Estimated time: 1-2 minutes


Stage 2: Apply App Scaffold

Context: On Proxmox host

The scaffold adds the complete standard stack: - Authentik SSO with AUTH_MODE switching - Supabase integration with schema isolation - shadcn/ui components - RBAC helpers

/root/tower-fleet/scripts/scaffold-nextjs.sh /root/projects/my-app my-app

What this adds:

my-app/
├── src/
│   ├── app/
│   │   ├── login/
│   │   │   └── page.tsx           # Login page
│   │   └── api/
│   │       └── auth/
│   │           ├── [...nextauth]/
│   │           │   └── route.ts   # NextAuth handlers
│   │           └── mode/
│   │               └── route.ts   # Auth mode API
│   ├── components/
│   │   ├── auth/
│   │   │   └── dev-banner.tsx     # Dev mode warning
│   │   ├── layout/
│   │   │   ├── session-provider.tsx
│   │   │   └── user-menu.tsx      # User avatar/menu
│   │   └── ui/                    # shadcn components
│   ├── lib/
│   │   ├── auth/
│   │   │   ├── index.ts           # Auth module entry
│   │   │   ├── config.ts          # AUTH_MODE config
│   │   │   ├── middleware.ts      # Route protection
│   │   │   ├── rbac.ts            # Tier helpers
│   │   │   ├── supabase-bridge.ts # JWT bridge
│   │   │   └── providers/
│   │   │       └── authentik.ts   # NextAuth config
│   │   ├── supabase/
│   │   │   ├── client.ts          # Browser client
│   │   │   └── server.ts          # Server client
│   │   └── utils/
│   │       └── cn.ts              # Tailwind class merger
│   ├── types/
│   │   ├── database.ts            # Type definitions
│   │   └── next-auth.d.ts         # Session types
│   └── middleware.ts              # Root middleware
├── .env.local.example             # Environment template
└── .gitignore

The scaffold also: - Installs all dependencies automatically - Initializes shadcn/ui with default components - Adds button, card, avatar, dropdown-menu components

Checkpoint: Verify scaffold applied:

ls /root/projects/my-app/src/lib/auth/
# Should see: index.ts, config.ts, middleware.ts, rbac.ts, supabase-bridge.ts, providers/

Estimated time: 2-3 minutes


Stage 3: Configure Environment

Context: On Proxmox host, in project directory

cd /root/projects/my-app

# Get Supabase credentials
kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.ANON_KEY}' | base64 -d
kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.SERVICE_ROLE_KEY}' | base64 -d
kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.JWT_SECRET}' | base64 -d

# Create .env.local from template
cp .env.local.example .env.local
# Edit .env.local with your keys

Required environment variables:

# Auth mode: none (dev) or authentik (production)
AUTH_MODE=none

# Supabase
NEXT_PUBLIC_SUPABASE_URL=http://10.89.97.20:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=<your-anon-key>
SUPABASE_SERVICE_ROLE_KEY=<your-service-role-key>
SUPABASE_JWT_SECRET=<your-jwt-secret>

Checkpoint: Verify environment configured:

cat .env.local | grep AUTH_MODE
# Should show AUTH_MODE=none

Estimated time: 1-2 minutes


Stage 4: Run Development Server

Context: In project directory

Option A: Direct run (foreground)

npm run dev

Option B: Tmux session (recommended for persistence)

tmux new -s my-app
npm run dev

# Detach: Ctrl+B, then D
# Reattach: tmux attach -t my-app

Checkpoint: Verify server running:

curl http://localhost:3000
# Should return HTML

Access your app: http://10.89.97.10:3000 (Proxmox host IP)

Estimated time: < 1 minute


Summary of Commands

Complete workflow (copy-paste friendly):

# Create Next.js app
cd /root/projects
npx create-next-app@latest my-app --typescript --tailwind --app --src-dir --eslint --import-alias '@/*' --no-turbopack --yes

# Apply scaffold (installs dependencies and shadcn)
/root/tower-fleet/scripts/scaffold-nextjs.sh /root/projects/my-app my-app

# Configure environment
cd my-app
ANON_KEY=$(kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.ANON_KEY}' | base64 -d)
SERVICE_KEY=$(kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.SERVICE_ROLE_KEY}' | base64 -d)
JWT_SECRET=$(kubectl get secret -n supabase-sandbox supabase-secrets -o jsonpath='{.data.JWT_SECRET}' | base64 -d)
AUTH_SECRET=$(openssl rand -base64 32)

cat > .env.local << EOF
AUTH_MODE=none
AUTH_SECRET=$AUTH_SECRET
NEXT_PUBLIC_SUPABASE_URL=http://10.89.97.20:8000
NEXT_PUBLIC_SUPABASE_ANON_KEY=$ANON_KEY
SUPABASE_SERVICE_ROLE_KEY=$SERVICE_KEY
SUPABASE_JWT_SECRET=$JWT_SECRET
EOF

# Start dev server
npm run dev

Using K8s Supabase with Schema Isolation

When using the shared K8s Supabase instance, each app needs its own schema.

Create Schema for New App

SCHEMA_NAME="my_app"  # Use underscores, not hyphens

# Create schema
kubectl exec -n supabase-sandbox postgres-0 -- psql -U postgres << EOF
CREATE SCHEMA IF NOT EXISTS $SCHEMA_NAME;
GRANT USAGE ON SCHEMA $SCHEMA_NAME TO postgres, authenticated, service_role, anon;
GRANT ALL ON SCHEMA $SCHEMA_NAME TO postgres;
ALTER DEFAULT PRIVILEGES IN SCHEMA $SCHEMA_NAME
  GRANT ALL ON TABLES TO postgres, authenticated, service_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA $SCHEMA_NAME
  GRANT SELECT ON TABLES TO anon;
EOF

# Update PostgREST to expose schema
CURRENT_SCHEMAS=$(kubectl get configmap -n supabase-sandbox supabase-config -o jsonpath='{.data.PGRST_DB_SCHEMA}')
kubectl patch configmap -n supabase-sandbox supabase-config --type=json \
  -p="[{\"op\": \"replace\", \"path\": \"/data/PGRST_DB_SCHEMA\", \"value\": \"$CURRENT_SCHEMAS,$SCHEMA_NAME\"}]"

# Restart PostgREST
kubectl rollout restart deployment -n supabase-sandbox rest

The scaffold automatically configures Supabase clients to use the correct schema (derived from app name).


Authentication Modes

The scaffold includes dual-mode authentication code:

Mode AUTH_MODE Behavior
Development none No authentication, all routes accessible
Production authentik Real OAuth/OIDC with Authentik SSO

What's Included vs Manual Setup

Included in Scaffold Manual Setup Required
Auth middleware & route protection Create Authentik Provider
NextAuth configuration Create Authentik Application
Login page & API routes Get OAuth credentials
Session provider & user menu Configure .env.local
RBAC helpers Set redirect URIs in Authentik

Switching to Production Auth

  1. Create OAuth2/OpenID Provider in Authentik admin (https://auth.bogocat.com/if/admin/):
  2. Name: {app-name}-provider
  3. Client type: Confidential
  4. Redirect URIs:
    • http://localhost:3000/api/auth/callback/authentik
    • https://{app}.bogocat.com/api/auth/callback/authentik
  5. Scopes: openid profile email groups

  6. Create Application in Authentik admin:

  7. Name: {app-name}
  8. Slug: {app-name}
  9. Provider: {app-name}-provider

  10. Configure environment (.env.local or K8s secrets):

    AUTH_MODE=authentik
    AUTHENTIK_ISSUER=https://auth.bogocat.com/application/o/{app-name}/
    AUTHENTIK_CLIENT_ID=<from-provider>
    AUTHENTIK_CLIENT_SECRET=<from-provider>
    

  11. Restart the application

Full guide: /root/tower-fleet/docs/workflows/migrating-to-authentik.md


Common Issues

Issue: npm command not found

Cause: Not in a shell with Node.js available

Solution: Node.js 22 is installed on the Proxmox host. Verify:

node --version  # Should show v22.x
npm --version

Issue: Supabase connection errors

Cause: Wrong URL or key, or supabase-sandbox not running

Solution:

# Check supabase-sandbox pods
kubectl get pods -n supabase-sandbox

# Verify Kong is accessible (sandbox)
curl http://10.89.97.20:8000/rest/v1/

# Check .env.local has correct values
cat .env.local

Issue: Permission denied on database

Cause: Schema not created or PostgREST not updated

Solution: Follow the schema isolation steps above.


Next Steps

After your app is running:

  1. Set up database schema - See Database Migrations
  2. Review implementation patterns - See App Conventions
  3. Deploy to production - See Production App Deployment
  4. Add to project tracking - Update /root/PROJECTS.md

Development vs Production Supabase

Environment Namespace URL Purpose
Sandbox supabase-sandbox http://10.89.97.20:8000 Development, testing
Production supabase http://10.89.97.20:8000 Deployed apps

Use --prod flag with scaffold script to configure for production Supabase:

/root/tower-fleet/scripts/scaffold-nextjs.sh /root/projects/my-app my-app --prod


Reference Implementation

The home-portal app is the reference implementation for the standard stack: - Location: /root/projects/home-portal - Fully implemented Authentik SSO - RBAC with tier-based access - Supabase integration with schema isolation