Everything you need to run ɳSelf in production — hardening, env vars, secrets, scaling, and monitoring.
# 1. Install nself
curl -fsSL https://install.nself.org | sh
# 2. Initialize for your domain
nself init --domain yourdomain.com --env prod
# 3. Apply security hardening (runs automatically on prod init)
nself prod harden
# 4. Request TLS certificate
nself ssl request --domain yourdomain.com
# 5. Deploy
nself deploy prodɳSelf runs on any Linux VPS. Minimum specs for a small production instance:
| Load | CPU | RAM | Disk | Hetzner equivalent |
|---|---|---|---|---|
| Personal / team (<10 users) | 2 vCPU | 4 GB | 40 GB SSD | CX22 |
| Small business (10–100 users) | 4 vCPU | 8 GB | 80 GB SSD | CX32 |
| Medium (>100 users) | 8 vCPU | 16 GB | 160 GB SSD | CX42 |
Ubuntu 22.04 LTS or Debian 12 recommended. Docker 24+ required (installed automatically by nself install).
All env vars live in .environments/prod/.env (shared config) and .environments/prod/.env.secrets (secrets, never committed). The cascade at runtime is:
.env.dev → .env.local → .env.prod → .env.secrets → .env.computed# .environments/prod/.env
BASE_DOMAIN=yourdomain.com
POSTGRES_DB=nself
HASURA_GRAPHQL_ADMIN_SECRET=<generate: openssl rand -hex 32>
JWT_SECRET=<generate: openssl rand -hex 32>
ANON_KEY=<generate with: nself keys gen anon>
SERVICE_ROLE_KEY=<generate with: nself keys gen service>
# Storage
STORAGE_BACKEND=local # or: s3, r2, gcs, azure
STORAGE_S3_BUCKET= # if using S3-compatible
STORAGE_S3_ENDPOINT=
STORAGE_S3_ACCESS_KEY=
STORAGE_S3_SECRET_KEY=
# Mail (optional — choose one provider)
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASS=Never commit .env.secrets. Use a secrets manager for teams:
# Inject from 1Password / Vault / AWS SSM at deploy time
nself env set --env prod HASURA_GRAPHQL_ADMIN_SECRET "$(op read op://vault/nself/hasura-secret)"
# Or export from Vault before deploying
export VAULT_ADDR=https://vault.yourdomain.com
nself deploy prod --env-from-vault secret/nself/prodRun once after init, and again after major upgrades:
nself prod harden
# What it does:
# ✓ Sets Postgres connection limits and pg_hba.conf
# ✓ Disables Hasura console in production
# ✓ Rotates JWT signing key (with zero-downtime reload)
# ✓ Configures Nginx security headers
# ✓ Enables rate limiting (100 req/min per IP by default)
# ✓ Sets Docker resource limits per service
# ✓ Enables fail2ban rules for auth endpoints
# ✓ Validates firewall rules (ports 80/443 only)
# Verify hardening score
nself prod check| Check | Command | Expected |
|---|---|---|
| Postgres not publicly exposed | nself prod check --item postgres-exposure | PASS |
| Hasura console disabled | nself prod check --item hasura-console | PASS |
| JWT secret length | nself prod check --item jwt-length | ≥ 32 bytes |
| TLS grade | nself ssl verify --domain yourdomain.com | A or A+ |
| Rate limiting active | nself prod check --item rate-limit | PASS |
| Audit log enabled | nself prod check --item audit-log | PASS |
# Dry run — see exactly what will happen
nself deploy prod --dry-run
# Deploy with health checks
nself deploy prod
# Output:
# Connecting to 5.75.235.42...
# Syncing configuration... done
# Pulling images... done
# Running migrations... done (3 new)
# Starting services... done
# Health check: api.yourdomain.com... ok
# Health check: auth.yourdomain.com... ok
# Deploy complete in 47s# Update to latest nself release
nself update
# Rolling restart of all services
nself deploy prod --strategy rolling
# Restart a single service without downtime
nself service restart hasura --rolling# List recent deploys
nself deploy history
# Roll back to the previous deploy
nself deploy rollback
# Roll back to a specific deploy ID
nself deploy rollback deploy_20260428_143022ɳSelf ships a full monitoring stack (Prometheus + Grafana + Loki + Alertmanager). It is disabled in dev and enabled automatically in prod.
# Open Grafana (port-forwarded to localhost)
nself monitor open grafana
# Open Prometheus query UI
nself monitor open prometheus
# Tail all service logs
nself logs --follow
# Tail a specific service
nself logs hasura --follow --tail 100# In .environments/prod/.env:
ALERT_EMAIL=ops@yourdomain.com
ALERT_SMTP_HOST=smtp.yourdomain.com
ALERT_SLACK_WEBHOOK=https://hooks.slack.com/...
# Default alert rules (always active):
# - Service down > 1 min
# - Postgres disk > 80%
# - Memory > 90% for 5 min
# - Auth failure spike (>50/min)
# - Certificate expiry < 14 days# In .environments/prod/.env:
HASURA_REPLICAS=3
HASURA_GRAPHQL_MAX_CONNECTIONS=100
# Restart to apply
nself deploy prod --services hasura# .environments/prod/.env — per-service memory limits
POSTGRES_MEMORY_LIMIT=2g
HASURA_MEMORY_LIMIT=1g
REDIS_MEMORY_LIMIT=512m
AUTH_MEMORY_LIMIT=256m# Add a streaming replica
nself db replica add --host replica.yourdomain.com
# Hasura automatically routes read queries to replicas
# Set in .env:
HASURA_GRAPHQL_READ_REPLICA_URLS=postgres://user:pass@replica:5432/nself# Full stack health check
nself health
# Output:
# postgres ✓ healthy (15ms)
# hasura ✓ healthy (23ms)
# auth ✓ healthy (18ms)
# storage ✓ healthy (12ms)
# redis ✓ healthy (2ms)
# nginx ✓ healthy
# Single service
nself health postgres
# Continuous watch (useful during deploys)
nself health --watch --interval 5s# JWT secret rotation (Hasura + Auth reload atomically)
nself secrets rotate jwt
# Admin secret rotation
nself secrets rotate hasura-admin
# Full rotation (all secrets, rolling)
nself secrets rotate --all# Check pending migrations
nself db status
# Apply pending migrations (runs automatically on deploy)
nself db migrate --env prod
# Apply a specific migration file
nself db migrate apply 007_add_index.sql --env prod# Enable maintenance page (Nginx serves 503)
nself maintenance on
# Do your work...
# Restore traffic
nself maintenance offnself deploy prod --dry-run before every production deploy.nself backup create to your CI pipeline..env.secrets out of git — use a secrets manager or CI vault.nself ssl status — auto-renewal fires at 30 days.nself prod check weekly to catch configuration drift.HASURA_GRAPHQL_ENABLE_CONSOLE=false in prod — always.nself version pin v1.1.0.