Straightforward migration guide from Nhost to ɳSelf
Nhost and ɳSelf share the same core technology stack, making migration straightforward. The primary difference is deployment model.
| Aspect | Nhost | ɳSelf |
|---|---|---|
| Interface | Web dashboard + CLI | CLI + optional web admin |
| Hosting | Managed cloud or self-hosted | Self-hosted only |
| Configuration | Web UI, config files | .env file + CLI commands |
| Deployment | nhost deploy | nself build && nself start |
| Backups | Automatic (cloud) | Manual + Scheduled |
Before you start, ensure you have:
# Install nself
curl -sSL https://install.nself.org | bash
# Install Nhost CLI (for data export)
npm install -g nhost
# Install PostgreSQL client tools
brew install postgresql # macOS
sudo apt-get install postgresql-client # Ubuntu# 1. Document current setup
nhost config show > nhost-config.txt
nhost env list > nhost-env.txt
# 2. Create full backup
nhost db dump > nhost-backup.sql
nhost metadata export > nhost-metadata.json
# 3. List all services in use
# - Auth providers enabled
# - Functions deployed
# - Environment variablesEstimated time: 30 minutes
mkdir nhost-migration && cd nhost-migration
nself init --wizard# .env configuration
PROJECT_NAME=my-nhost-migration
ENV=dev
BASE_DOMAIN=localhost
# Database (use same credentials pattern)
POSTGRES_DB=myapp_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your-secure-password
# Hasura
HASURA_GRAPHQL_ADMIN_SECRET=your-admin-secret
HASURA_GRAPHQL_JWT_SECRET={"type":"HS256","key":"your-jwt-secret-min-32-chars"}
# Auth (nHost Auth - same service Nhost uses)
AUTH_SERVER_URL=http://auth.localhost
AUTH_CLIENT_URL=http://localhost:3000
# Storage (enable MinIO)
MINIO_ENABLED=true
MINIO_ROOT_USER=minioadmin
MINIO_ROOT_PASSWORD=minioadmin
# Optional services
REDIS_ENABLED=true
FUNCTIONS_ENABLED=true
MAILPIT_ENABLED=truenself build
nself start
nself doctor # Verify all services are running
nself urls # List all available URLsEstimated time: 1-2 hours
# Option A: Using Nhost CLI
nhost db dump > nhost-database.sql
# Option B: Using pg_dump directly
pg_dump -h db.nhost.run -U nhost -d your-nhost-db > nhost-database.sql
# Option C: From Nhost Dashboard
# Go to Database → Backups → Download Latest Backup# Clean the dump file (remove Nhost-specific settings)
cat nhost-database.sql | \
grep -v "nhost_" | \
sed 's/nhost/postgres/g' > cleaned-database.sql# Using nself CLI (recommended)
nself db import cleaned-database.sql
# Verify import
nself db shell-- In psql
\dt -- List tables
SELECT count(*) FROM auth.users; -- Verify data
\qEstimated time: 30 minutes
Authentication users are stored in the same auth.users table. If you imported the database dump, users are already migrated.
nself db shell-- Verify users migrated
SELECT id, email, created_at FROM auth.users LIMIT 10;
-- Check roles
SELECT * FROM auth.roles;
-- Check user roles
SELECT * FROM auth.user_roles;Map Nhost OAuth config to ɳSelf:
# In .env:
# GitHub
AUTH_PROVIDER_GITHUB_ENABLED=true
AUTH_PROVIDER_GITHUB_CLIENT_ID=your-github-client-id
AUTH_PROVIDER_GITHUB_CLIENT_SECRET=your-github-secret
# Google
AUTH_PROVIDER_GOOGLE_ENABLED=true
AUTH_PROVIDER_GOOGLE_CLIENT_ID=your-google-client-id
AUTH_PROVIDER_GOOGLE_CLIENT_SECRET=your-google-secret
# Facebook
AUTH_PROVIDER_FACEBOOK_ENABLED=true
AUTH_PROVIDER_FACEBOOK_CLIENT_ID=your-facebook-app-id
AUTH_PROVIDER_FACEBOOK_CLIENT_SECRET=your-facebook-secretRestart auth service:
nself restart --service=authCritical: Your JWT secret must match Nhost's secret if you want existing tokens to work.
# Find Nhost JWT secret (from Nhost Dashboard → Settings → Environment Variables)
# Copy EXACT value to ɳSelf .env
HASURA_GRAPHQL_JWT_SECRET='{"type":"HS256","key":"your-exact-nhost-jwt-secret"}'
# Rebuild and restart
nself build
nself restartcurl -X POST http://auth.localhost/v1/signin/email-password \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "password"
}'
# Should return access_token and refresh_tokenEstimated time: 1-2 hours
#!/bin/bash
# download-nhost-storage.sh
NHOST_PROJECT_URL="https://yourproject.nhost.run"
NHOST_BUCKET="default"
OUTPUT_DIR="./nhost-storage-backup"
mkdir -p $OUTPUT_DIR/$NHOST_BUCKET
# Get file list and download
curl "$NHOST_PROJECT_URL/v1/storage/files?bucketId=$NHOST_BUCKET" | \
jq -r '.files[].id' | \
while read FILE_ID; do
echo "Downloading $FILE_ID..."
curl "$NHOST_PROJECT_URL/v1/storage/files/$FILE_ID" \
-o "$OUTPUT_DIR/$NHOST_BUCKET/$FILE_ID"
done# Access MinIO Console: http://minio.localhost
# OR use MinIO CLI
docker exec -it $(docker ps -qf "name=minio") mc alias set local http://localhost:9000 minioadmin minioadmin
# Create buckets to match Nhost
docker exec -it $(docker ps -qf "name=minio") mc mb local/default
docker exec -it $(docker ps -qf "name=minio") mc mb local/avatars
# Set public policy if needed
docker exec -it $(docker ps -qf "name=minio") mc policy set download local/default# Upload all files
mc mirror ./nhost-storage-backup/default ɳSelf/defaultEstimated time: 30 minutes
# Using Hasura Console
# 1. Open Hasura Console: https://yourproject.nhost.run/console
# 2. Go to Settings → Metadata Actions → Export Metadata
# OR using Hasura CLI
cd /tmp/nhost-metadata
hasura metadata export --endpoint https://yourproject.nhost.run/v1/graphql --admin-secret YOUR_ADMIN_SECRET# Option 1: Using Hasura CLI
cd /tmp/nhost-metadata
hasura metadata apply --endpoint http://api.localhost/v1/graphql --admin-secret your-admin-secret
# Option 2: Via Hasura Console
# 1. Open http://api.localhost
# 2. Go to Settings → Metadata Actions → Import Metadata# Check tables tracked
curl http://api.localhost/v1/graphql \
-H "x-hasura-admin-secret: your-admin-secret" \
-d '{"query": "{ __schema { types { name } } }"}'Estimated time: 1 hour
Nhost and ɳSelf both support serverless functions with the same Express.js handler signature.
# Enable functions in nself
# In .env:
FUNCTIONS_ENABLED=true
# Rebuild to create functions service
nself build
nself restart
# Copy Nhost functions to ɳSelf
cp -r /path/to/nhost/functions/* ./functions/src/
# Install dependencies
cd functions
npm install
# Test functions locally
nself logs --service=functionsNo changes needed - Same Express.js handler signature:
// Nhost function
import { Request, Response } from 'express'
export default (req: Request, res: Response) => {
res.json({ message: 'Hello from Nhost!' })
}
// nself function (identical)
import { Request, Response } from 'express'
export default (req: Request, res: Response) => {
res.json({ message: 'Hello from ɳSelf!' })
}The Nhost JavaScript SDK can work with ɳSelf by configuring custom URLs.
import { NhostClient } from '@nhost/nhost-js'
const nhost = new NhostClient({
subdomain: 'myproject',
region: 'us-east-1'
})import { NhostClient } from '@nhost/nhost-js'
const nhost = new NhostClient({
backendUrl: 'http://auth.localhost',
graphqlUrl: 'http://api.localhost/v1/graphql',
storageUrl: 'http://minio.localhost',
authUrl: 'http://auth.localhost/v1'
})NEXT_PUBLIC_NHOST_SUBDOMAIN=myproject
NEXT_PUBLIC_NHOST_REGION=us-east-1NEXT_PUBLIC_API_URL=http://api.localhost
NEXT_PUBLIC_AUTH_URL=http://auth.localhost
NEXT_PUBLIC_STORAGE_URL=http://minio.localhostSymptom: Existing tokens don't work after migration
Solution: Use exact same JWT secret from Nhost
# Find in Nhost Dashboard → Settings → Env Vars
HASURA_GRAPHQL_JWT_SECRET='{"type":"HS256","key":"exact-nhost-secret"}'
nself restartSymptom: Connection refused when importing database
Solution: Wait for PostgreSQL to be ready
nself doctor
# Or check manually
docker exec $(docker ps -qf "name=postgres") pg_isreadySymptom: 404 errors when accessing files
Solution: Configure bucket policies
# Set bucket to public (if needed)
docker exec -it $(docker ps -qf "name=minio") mc policy set download local/defaultIf migration fails:
# Revert environment variables
NEXT_PUBLIC_NHOST_SUBDOMAIN=myproject
NEXT_PUBLIC_NHOST_REGION=us-east-1
# Rebuild and deploy
npm run build
vercel deployAfter migration, verify everything works:
nself db shell → \dt)After migration, optimize ɳSelf for production:
# Database optimization
nself db analyze
# Enable connection pooling
PGBOUNCER_ENABLED=true
PGBOUNCER_POOL_MODE=transaction
# Enable Redis caching
REDIS_ENABLED=true
AUTH_REDIS_ENABLED=true
# Enable monitoring
MONITORING_ENABLED=true
# Rebuild and restart
nself build
nself restartMigrating from Nhost to ɳSelf is straightforward due to shared technology (PostgreSQL, Hasura, nHost Auth). Main effort is in data export/import and frontend URL configuration.
Timeline Summary:
Total: 6-10 hours for complete migration
Recommended Approach:
Need help? Check our support channels or join our Discord community for migration assistance.