QUICK START SAAS - nself-org/cli GitHub Wiki
Complete step-by-step guide to launch a production-ready SaaS platform with billing, multi-tenancy, and white-label capabilities.
Time Estimate: 15-20 minutes Difficulty: Beginner Prerequisites: Docker Desktop installed
Note: As of v0.9.6, commands have been consolidated. This guide uses the new v1.0 command structure where applicable:
nself envโnself config envnself scaleโnself perf scale- Other commands referenced use their consolidated forms
By the end of this tutorial, you'll have:
- Multi-tenant SaaS backend with database
- Stripe billing integration (subscriptions & usage-based)
- White-label customization per tenant
- Authentication & authorization
- Production-ready deployment
Timeline:
[0-5 min] Install nself & initialize project
[5-10 min] Configure billing & multi-tenancy
[10-15 min] Deploy to production
[15-20 min] Test & verify
curl -sSL https://install.nself.org | bashnself version
# Expected: nself v0.4.8 or latergit clone https://github.com/nself-org/cli.git ~/.nself
echo 'export PATH="$PATH:$HOME/.nself/bin"' >> ~/.bashrc
source ~/.bashrcmkdir my-saas && cd my-saas
nself init --template saasAnswer the prompts:
Project name: my-saas
Environment: dev
Base domain: local.nself.org (default for local)
Enable monitoring? Yes
my-saas/
โโโ .env # Configuration
โโโ .env.secrets # Auto-generated secrets
โโโ schema.dbml # SaaS database schema (auto-generated)
โโโ nself/
cat schema.dbmlPre-configured SaaS schema includes:
-
organizations- Tenant organizations -
organization_members- Team members -
subscriptions- Billing subscriptions -
subscription_usage- Usage tracking -
invoices- Invoice history -
payment_methods- Saved payment methods -
billing_events- Billing event log
nself buildWhat happens:
- Generates
docker-compose.yml(25+ services) - Creates nginx configurations
- Sets up SSL certificates
- Initializes database schema
- Configures Hasura GraphQL
nself startFirst start downloads Docker images (2-5 minutes)
nself statusExpected output:
Service Status:
postgres โ healthy
hasura โ healthy
auth โ healthy
nginx โ healthy
redis โ healthy
minio โ healthy
monitoring โ 10/10 services healthy
nself urlsKey URLs:
- API: https://api.local.nself.org
- Auth: https://auth.local.nself.org
- Admin: https://admin.local.nself.org
- Grafana: https://grafana.local.nself.org
nself db schema apply schema.dbmlThis command:
- Creates SQL migration from DBML
- Runs migration
- Generates sample data
- Seeds test organizations
nself db shell-- List all tables
\dt
-- Check organizations table
SELECT * FROM organizations;
-- Check subscriptions table
SELECT * FROM subscriptions;Exit with \q
- Go to Stripe Dashboard
- Create account (free)
- Get test API keys:
- Publishable key:
pk_test_... - Secret key:
sk_test_PLACEHOLDER...
- Publishable key:
nself plugin install stripeEdit .env and add:
# Stripe Configuration
STRIPE_API_KEY=sk_test_PLACEHOLDER_secret_key_here
STRIPE_PUBLISHABLE_KEY=pk_test_your_publishable_key_here
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_here# Create a subscription product
nself plugin stripe products create \
--name "Pro Plan" \
--price 29.99 \
--interval month
# Create usage-based product
nself plugin stripe products create \
--name "API Calls" \
--price 0.01 \
--type usagenself plugin stripe syncSyncs:
- Customers
- Products & Prices
- Subscriptions
- Invoices
- Payment methods
Edit .env:
# Multi-Tenancy Settings
HASURA_GRAPHQL_JWT_SECRET='{"type":"HS256","key":"your-jwt-secret-here"}'
HASURA_GRAPHQL_ENABLE_CONSOLE=true
HASURA_GRAPHQL_ENABLE_TELEMETRY=false
# Tenant isolation
TENANT_ISOLATION_ENABLED=true
TENANT_COLUMN_NAME=organization_id# Access Hasura console
open https://api.local.nself.orgConfigure row-level security:
- Go to Data โ organizations โ Permissions
- For "user" role:
- Select:
{"organization_members":{"user_id":{"_eq":"X-Hasura-User-Id"}}} - Insert:
{"owner_id":{"_eq":"X-Hasura-User-Id"}} - Update: Same as Select
- Delete: Same as Select
- Select:
This ensures users only see their organization's data
nself whitelabel initnself whitelabel branding create "My SaaS" \
--tagline "Your tagline here"nself whitelabel branding set-colors \
--primary #0066cc \
--secondary #00cc66 \
--accent #ff6600nself whitelabel logo upload ./logo.png --type main# List available templates
nself whitelabel email list
# Customize welcome email
nself whitelabel email edit welcomeAvailable templates:
-
welcome- Welcome new users -
password-reset- Password reset -
verify-email- Email verification -
invite- Team invitations -
subscription-created- New subscription -
subscription-cancelled- Cancellation -
invoice-paid- Payment confirmation -
payment-failed- Failed payment
nself config env create prodEdit .env.prod:
ENV=prod
PROJECT_NAME=my-saas
BASE_DOMAIN=myapp.com
# Production database
POSTGRES_DB=myapp_prod
POSTGRES_USER=postgres
POSTGRES_PASSWORD=generate-secure-password-here
# Production Stripe (LIVE keys)
STRIPE_API_KEY=sk_live_your_live_key_here
STRIPE_PUBLISHABLE_KEY=pk_live_your_live_key_here
# Security
HASURA_GRAPHQL_ADMIN_SECRET=generate-secure-secret-here
AUTH_JWT_SECRET=generate-jwt-secret-here
# Disable dev features
HASURA_GRAPHQL_ENABLE_CONSOLE=false
HASURA_GRAPHQL_DEV_MODE=falseRequirements:
- Ubuntu 20.04+ or Debian 11+
- 2GB RAM minimum
- Docker & Docker Compose installed
- Domain with DNS configured
DNS Configuration:
A @ -> your-server-ip
A * -> your-server-ip
CNAME api -> myapp.com
CNAME auth -> myapp.com
CNAME admin -> myapp.com
Create .environments/prod/server.json:
{
"host": "your-server-ip",
"user": "root",
"port": 22,
"path": "/var/www/my-saas",
"sshKey": "~/.ssh/id_rsa"
}nself deploy prodDeployment process:
- Connects via SSH
- Uploads project files
- Installs dependencies
- Runs migrations
- Starts services
- Provisions SSL certificates
nself deploy status prodUsing Hasura console (https://api.myapp.com):
mutation CreateOrganization {
insert_organizations_one(object: {
name: "Acme Corp"
slug: "acme-corp"
plan: "pro"
status: "active"
}) {
id
name
slug
}
}mutation CreateSubscription {
insert_subscriptions_one(object: {
organization_id: "org-id-from-above"
stripe_subscription_id: "sub_xxxxx"
plan: "pro"
status: "active"
current_period_start: "2026-01-01"
current_period_end: "2026-02-01"
price_amount: 2999
currency: "usd"
}) {
id
status
}
}mutation TrackUsage {
insert_subscription_usage_one(object: {
subscription_id: "sub-id-from-above"
metric_name: "api_calls"
quantity: 1000
timestamp: "now()"
}) {
id
quantity
}
}query GetRevenue {
subscriptions_aggregate(where: {status: {_eq: "active"}}) {
aggregate {
sum {
price_amount
}
count
}
}
}# Subscription stats
nself plugin stripe subscriptions stats
# Recent invoices
nself plugin stripe invoices list --limit 10
# MRR (Monthly Recurring Revenue)
nself db query "SELECT SUM(price_amount/100) as mrr FROM subscriptions WHERE status = 'active'"# Usage by organization
nself db query "
SELECT o.name, SUM(u.quantity) as total_usage
FROM subscription_usage u
JOIN subscriptions s ON u.subscription_id = s.id
JOIN organizations o ON s.organization_id = o.id
WHERE u.timestamp > NOW() - INTERVAL '30 days'
GROUP BY o.name
ORDER BY total_usage DESC
"# All logs
nself logs
# Specific service
nself logs hasura
nself logs auth
# Follow logs in real-time
nself logs -f-
Go to Stripe Dashboard > Webhooks
-
Add endpoint:
https://myapp.com/webhooks/stripe -
Select events:
-
customer.created,customer.updated -
subscription.created,subscription.updated,subscription.deleted -
invoice.paid,invoice.payment_failed -
payment_intent.succeeded,payment_intent.payment_failed
-
-
Copy webhook secret to
.env:
STRIPE_WEBHOOK_SECRET=whsec_your_webhook_secret_here- Rebuild and restart:
nself build && nself restart# List recent webhook events
nself plugin stripe webhook list
# Check webhook health
nself plugin stripe webhook stats
# Retry failed webhook
nself plugin stripe webhook retry evt_xxxxx โโโโโโโโโโโโโโโโโโโโโโโ
โ Load Balancer โ
โ (nginx + SSL) โ
โโโโโโโโโโโโฌโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโ
โ โ โ
โโโโโโโโโผโโโโโโโโโ โโโโโโโผโโโโโโโ โโโโโโโโโผโโโโโโโโโ
โ GraphQL API โ โ Auth โ โ Admin UI โ
โ (Hasura) โ โ (nHost) โ โ (nself) โ
โโโโโโโโโฌโโโโโโโโโ โโโโโโโฌโโโโโโโ โโโโโโโโโฌโโโโโโโโโ
โ โ โ
โโโโโโโโโผโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโ
โ PostgreSQL Database โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Organizations โ Subscriptions โ Usage โ โ
โ โ Members โ Invoices โ Events โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โโโโโโโโโผโโโโโโโโโ โโโโโโโโโผโโโโโโโโโ
โ Stripe API โ โ MinIO Storage โ
โ (Billing) โ โ (Files/Logos) โ
โโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ
# Diagnose issues
nself doctor
# Check Docker
docker ps
# View logs
nself logs postgres
nself logs hasura# Verify database is running
nself status postgres
# Check connection
nself db query "SELECT 1"
# Restart database
nself restart postgres# Verify API key
curl -u sk_test_PLACEHOLDER: https://api.stripe.com/v1/customers?limit=1
# Check plugin status
nself plugin status stripe
# Verbose sync
nself plugin stripe sync --verbose# Check certificate
nself ssl check myapp.com
# Renew certificate
nself ssl renew myapp.com
# Check nginx config
nself config validate nginx# Check what's using ports
lsof -i :5432 # PostgreSQL
lsof -i :8080 # Hasura
lsof -i :443 # nginx
# Auto-fix port conflicts
AUTO_FIX=true nself build- Database Workflow Guide - Extend your schema
- Custom Services - Add microservices
- White-Label System - Full customization guide
# Enable search
nself service enable meilisearch
# Enable email
nself service enable mailpit
# Enable functions
nself service enable functionsSuggested stack:
- React + TypeScript
- Apollo Client (GraphQL)
- TailwindCSS
- nHost SDK (authentication)
Quick setup:
npx create-react-app frontend --template typescript
cd frontend
npm install @apollo/client @nhost/react graphql- Use live Stripe keys
- Configure production database backups
- Set up monitoring alerts
- Configure custom domain SSL
- Enable rate limiting
- Configure CORS
- Set up CDN
- Configure backup schedule
- Document API endpoints
- Set up error tracking
Development (Local):
- nself: Free
- Docker: Free
- Stripe: Free (test mode)
- Total: $0/month
Production (Small):
- VPS (2GB RAM): $10-20/month
- Domain: $12/year
- SSL: Free (Let's Encrypt)
- Stripe: 2.9% + $0.30 per transaction
- Total: ~$15/month + transaction fees
Production (Medium):
- VPS (4GB RAM): $20-40/month
- Domain: $12/year
- SSL: Free
- Stripe: 2.9% + $0.30 per transaction
- Total: ~$25-40/month + transaction fees
# Scale specific services
nself perf scale hasura --replicas 3
nself perf scale auth --replicas 2
# Enable load balancing
LOAD_BALANCER_ENABLED=true nself buildEdit .env:
# Increase PostgreSQL resources
POSTGRES_SHARED_BUFFERS=256MB
POSTGRES_MAX_CONNECTIONS=200
POSTGRES_WORK_MEM=8MB
# Increase Hasura resources
HASURA_GRAPHQL_MAX_CACHE_SIZE=1000
HASURA_GRAPHQL_CONNECTIONS_PER_READ_REPLICA=5# Analyze query performance
nself db analyze
# Add indexes
nself db query "CREATE INDEX idx_org_members ON organization_members(organization_id)"
# Configure connection pooling
PGBOUNCER_ENABLED=true nself build- Quick Start: B2B Platform - B2B-specific setup
- Quick Start: Marketplace - Multi-vendor platforms
- Quick Start: Agency - Agency/reseller setup
- Stripe Integration - Complete Stripe guide
- Custom Domains - Domain configuration
- Documentation: https://docs.nself.org
- GitHub Issues: https://github.com/nself-org/cli/issues
- Discord: https://discord.gg/nself
- Email: [email protected]
Congratulations! You've built a production-ready SaaS in 15 minutes.
Time to customize and launch your product!