> ## Content Index
> Fetch the complete content index at: https://blog.fabrixly.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Fabrixly Identity Server — Complete Setup Guide
- URL: https://blog.fabrixly.com/complete-setup-guide/
- Published: 2026-07-07T03:08:36.000Z
- Updated: 2026-07-07T07:07:37.000Z
- Author: Fabrixly
- Tags: Documentation, Setup, Deployment, Fabrixly-IDS

# Fabrixly Identity Server — Complete Setup Guide

This guide covers every deployment method for the **Fabrixly Identity Server (IDS)** and the **Fabrixly Licensing Portal**:

- [Local Development](#1-local-development) — run directly with Node.js
- [Docker Compose](#2-docker-compose) — quickest production-like setup
- [Kubernetes / Helm](#3-kubernetes--helm) — production-grade cluster deployment

---

## Architecture Overview

```
┌─────────────────────────────────────────────────┐
│                Fabrixly Identity Server             │
│  ┌──────────┐  ┌──────────┐  ┌───────────────┐  │
│  │ Backend  │  │ Console  │  │  Test Client  │  │
│  │ :3000    │  │ :80      │  │  :3001        │  │
│  └──────────┘  └──────────┘  └───────────────┘  │
│           ↕ all share PostgreSQL                │
│  ┌───────────────────────────────────────────┐  │
│  │              PostgreSQL :5432              │  │
│  └───────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────┐
│              Fabrixly Licensing Portal              │
│  ┌──────────────────────────────────────────┐  │
│  │  licensing-portal :3005                  │  │
│  │  (uses IDS as OIDC provider)             │  │
│  └──────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────┐  │
│  │  PostgreSQL :5432 (separate volume)      │  │
│  └──────────────────────────────────────────┘  │
└─────────────────────────────────────────────────┘

```

> **Important**: The Licensing Portal is deployed **separately** from the IDS. It authenticates via IDS using OIDC, but runs in its own container/namespace.

---

## Docker Image Tags

All images are published to Docker Hub under `kumaravinit/zero`:

| Image            | Latest Tag              | Versioned Tag          |
| ---------------- | ----------------------- | ---------------------- |
| IDS Backend      | ids-backend-latest      | ids-backend-1.0.1      |
| IDS Console      | ids-console-latest      | ids-console-1.0.1      |
| Test Client      | ids-test-client-latest  | ids-test-client-1.0.1  |
| Licensing Portal | licensing-portal-latest | licensing-portal-1.0.1 |

---

## 1\. Local Development

### Prerequisites

- Node.js 18+
- PostgreSQL 15+ running locally

### Steps

```bash
# Clone the repository
git clone https://github.com/your-org/fabrixly-identity-server.git
cd fabrixly-identity-server

# Install dependencies
npm install

# Copy env file
cp .env.example .env

# Edit .env — minimum required:
# DB_HOST=localhost
# DB_USERNAME=admin
# DB_PASSWORD=admin123
# SESSION_SECRET=<random-32-char-string>
# JWT_SECRET=<random-32-char-string>
# ISSUER=http://localhost:3000/oidc

# Run (seeds automatically in development)
npm run dev

```

The server seeds the system admin, countries, and default plans automatically in `development` mode.

---

## 2\. Docker Compose

### 2a. Fabrixly Identity Server

The IDS compose file brings up **4 services**: PostgreSQL, Backend, Console, and Test Client.

#### Step 1 — Create your `.env` file

```bash
cd fabrixly-identity-server
cp .env.example .env

```

Edit `.env` and set at **minimum**:

```dotenv
# ── Required secrets ─────────────────────────────────────────────────
SESSION_SECRET=replace-me-with-32-char-random-string
JWT_SECRET=replace-me-with-32-char-random-string

# ── Database ──────────────────────────────────────────────────────────
DB_USERNAME=admin
DB_PASSWORD=admin123
DB_NAME=identity_server

# ── Server ────────────────────────────────────────────────────────────
ISSUER=http://localhost:3000/oidc

# ── Email (required for OTP/invite flows) ─────────────────────────────
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASS=SG.your-sendgrid-api-key
SMTP_FROM="Fabrixly IDS" <no-reply@your-domain.com>

# ── Admin account (seeded on first start) ─────────────────────────────
SYSTEM_ADMIN_EMAIL=admin@system.com
SYSTEM_ADMIN_PASSWORD=admin123

# ── Auto-seed on first production run ─────────────────────────────────
AUTO_SEED=true

# ── Self-Hosting / Licensing ──────────────────────────────────────────
SELF_HOSTED=false
LICENSE_KEY=

```

#### Step 2 — Generate secure secrets

```bash
echo "SESSION_SECRET=$(openssl rand -base64 32)"
echo "JWT_SECRET=$(openssl rand -base64 32)"

```

Paste the output into your `.env` file.

#### Step 3 — Start the stack

```bash
docker compose up -d

```

This starts:

- **PostgreSQL** on port `5432`
- **Backend (IDS)** on port `3000`
- **Console (Admin UI)** on port `80`
- **Test Client** on port `3001`

#### Step 4 — Seed the database

The backend seeds automatically on first boot **if `AUTO_SEED=true`** is set. This creates:

- System admin account (`SYSTEM_ADMIN_EMAIL` / `SYSTEM_ADMIN_PASSWORD`)
- Default plans (Free, Pro, Enterprise)
- Country seed data

The seed is **idempotent** — safe to run multiple times; it won't duplicate data.

To force a re-seed manually:

```bash
docker compose exec backend sh -c "node -e \"require('./dist/seeds').runAll()\""

```

#### Step 5 — Verify

```bash
# Check all containers are running
docker compose ps

# Tail logs
docker compose logs -f backend

# Access
open http://localhost        # Admin Console
open http://localhost:3000   # Identity Server API (local dev only — production: https://ids.fabrixly.com)
open http://localhost:3001   # Test Client

```

Login with: `admin@system.com` / `admin123` (or your custom values).

#### Step 6 — Pin to a specific version

The default `docker-compose.yml` uses `1.0.0` tags. To use `1.0.1`:

Create `docker-compose.override.yml` alongside the main file:

```yaml
version: '3.8'
services:
  backend:
    image: kumaravinit/zero:ids-backend-1.0.1
  console:
    image: kumaravinit/zero:ids-console-1.0.1
  test-client:
    image: kumaravinit/zero:ids-test-client-1.0.1

```

Then run:

```bash
docker compose up -d
# Docker Compose automatically merges override files

```

---

### 2b. Fabrixly Licensing Portal

The Licensing Portal is deployed **independently**. It authenticates users via the running IDS.

#### Prerequisites

- IDS already running and accessible. Local dev default: `http://localhost:3000`. Production: `https://ids.fabrixly.com`
- An RSA `PRIVATE_KEY` for signing license JWTs (see below)

#### Step 1 — Navigate to the Licensing Portal repo

```bash
cd ../zero-licencing-portal

```

#### Step 2 — Generate the PRIVATE\_KEY

The licensing portal signs license tokens with an RSA private key:

```bash
node -e "
const crypto = require('crypto');
const { privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
process.stdout.write(privateKey);
" > private.pem

cat private.pem

```

Save this key securely — you'll need it every time you deploy.

#### Step 3 — Create `.env` file

```bash
cat > .env << EOF
PORT=3005
ISSUER=http://localhost:3000/oidc
CLIENT_ID=licensing-portal-client
CLIENT_SECRET=licensing_portal_secret
REDIRECT_URI=http://localhost:3005/callback
SESSION_SECRET=$(openssl rand -base64 32)
DATABASE_URL=postgresql://admin:admin123@postgres:5432/identity_server
IDS_BASE_URL=http://host.docker.internal:3000
IDS_ADMIN_EMAIL=admin@system.com
IDS_ADMIN_PASSWORD=admin123
PRIVATE_KEY=$(cat private.pem)
EOF

```

> **Note**: If running IDS via Docker Compose on the same machine, use `host.docker.internal:3000` (Mac/Windows) or the IDS container IP to connect from within the licensing portal container.

#### Step 4 — Start the Licensing Portal

```bash
docker compose up -d

```

Or run with the pre-built image (skip build):

```bash
docker compose -f docker-compose.yml up -d
# The compose file references `image: zero-licencing-portal:latest` by default

```

To use the published image:

```bash
# Edit docker-compose.yml: replace `build:` block with:
# image: kumaravinit/zero:licensing-portal-1.0.1
docker compose up -d

```

#### Step 5 — Verify

```bash
docker compose ps
docker compose logs -f licensing-portal
open http://localhost:3005

```

---

## 3\. Kubernetes / Helm

### 3a. Fabrixly Identity Server

#### Prerequisites

```bash
# Helm 3 + kubectl configured
helm version
kubectl cluster-info

# Install Helm dependencies (PostgreSQL subchart)
cd helm/fabrixly-ids
helm dependency update
cd ../..

```

#### Step 1 — Create namespace

```bash
kubectl create namespace fabrixly-ids

```

#### Step 2 — Create secrets

```bash
SESSION_SECRET=$(openssl rand -base64 32)
JWT_SECRET=$(openssl rand -base64 32)
DB_PASSWORD=$(openssl rand -base64 24)
PG_ADMIN_PASSWORD=$(openssl rand -base64 24)

cat > helm/fabrixly-ids/values-secrets-production.yaml << EOF
backend:
  secrets:
    SESSION_SECRET: "${SESSION_SECRET}"
    JWT_SECRET: "${JWT_SECRET}"
    DB_PASSWORD: "${DB_PASSWORD}"
    SMTP_USER: "your-smtp-user-or-apikey"
    SMTP_PASS: "your-smtp-password-or-apikey"

postgresql:
  auth:
    password: "${DB_PASSWORD}"
    postgresPassword: "${PG_ADMIN_PASSWORD}"
EOF

# Keep this out of git
echo "values-secrets-production.yaml" >> helm/fabrixly-ids/.gitignore

```

#### Step 3 — Create environment values file

Create `helm/fabrixly-ids/values-myenv.yaml`:

```yaml
global:
  domain: your-ids-domain.com

backend:
  image:
    repository: kumaravinit/zero
    tag: "ids-backend-1.0.1"

  env:
    NODE_ENV: production
    ISSUER: "https://your-ids-domain.com/oidc"
    DB_HOST: "postgresql-service"
    DB_PORT: "5432"
    DB_NAME: "identity_server"
    DB_SYNC: "false"
    DB_LOGGING: "false"
    SMTP_HOST: "smtp.sendgrid.net"
    SMTP_PORT: "587"
    SMTP_SECURE: "true"
    SMTP_FROM: "noreply@your-ids-domain.com"
    CONSOLE_URL: "https://your-ids-domain.com"
    AUTO_SEED: "true"
    SELF_HOSTED: "false"
    LICENSE_KEY: ""

console:
  image:
    repository: kumaravinit/zero
    tag: "ids-console-1.0.1"

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  hosts:
    - host: your-ids-domain.com
      paths:
        - path: /(api|oidc|interaction|password-reset|verification|legal)(/|$)(.*)
          pathType: ImplementationSpecific
          backend: backend
        - path: /
          pathType: Prefix
          backend: console
  tls:
    - secretName: fabrixly-ids-tls
      hosts:
        - your-ids-domain.com

```

#### Step 4 — Install

```bash
helm install fabrixly-ids ./helm/fabrixly-ids \
  -f helm/fabrixly-ids/values-myenv.yaml \
  -f helm/fabrixly-ids/values-secrets-production.yaml \
  --namespace fabrixly-ids \
  --wait --timeout 5m

```

#### Step 5 — Seed (if AUTO\_SEED not set)

```bash
BACKEND_POD=$(kubectl get pod -n fabrixly-ids -l app=fabrixly-ids-backend -o jsonpath='{.items[0].metadata.name}')
kubectl exec -n fabrixly-ids $BACKEND_POD -- sh -c "AUTO_SEED=true node dist/index.js"

```

Or set `AUTO_SEED: "true"` in the values and do a rolling restart:

```bash
helm upgrade fabrixly-ids ./helm/fabrixly-ids \
  -f helm/fabrixly-ids/values-myenv.yaml \
  -f helm/fabrixly-ids/values-secrets-production.yaml \
  --set backend.env.AUTO_SEED=true \
  --namespace fabrixly-ids

```

#### Step 6 — Upgrade

```bash
helm upgrade fabrixly-ids ./helm/fabrixly-ids \
  -f helm/fabrixly-ids/values-myenv.yaml \
  -f helm/fabrixly-ids/values-secrets-production.yaml \
  --namespace fabrixly-ids \
  --set backend.image.tag=ids-backend-1.0.1 \
  --set console.image.tag=ids-console-1.0.1

```

#### Step 7 — Verify

```bash
kubectl get pods -n fabrixly-ids
kubectl get svc -n fabrixly-ids
kubectl get ingress -n fabrixly-ids
kubectl logs -f deployment/fabrixly-ids-backend -n fabrixly-ids

```

---

### 3b. Fabrixly Licensing Portal

#### Step 1 — Create namespace and secrets

```bash
kubectl create namespace zero-licensing

# Generate RSA key
node -e "
const crypto = require('crypto');
const { privateKey } = crypto.generateKeyPairSync('rsa', {
  modulusLength: 2048,
  privateKeyEncoding: { type: 'pkcs8', format: 'pem' }
});
process.stdout.write(privateKey);
" > /tmp/licensing-private.pem

kubectl create secret generic licensing-portal-secrets \
  --from-literal=SESSION_SECRET="$(openssl rand -base64 32)" \
  --from-literal=IDS_ADMIN_PASSWORD="your-ids-admin-password" \
  --from-file=PRIVATE_KEY=/tmp/licensing-private.pem \
  --namespace zero-licensing

# Clean up temp key file
rm /tmp/licensing-private.pem

```

#### Step 2 — Create values file

```bash
cd ../zero-licencing-portal

```

Create `helm/zero-licencing-portal/values-production.yaml`:

```yaml
replicaCount: 1

image:
  repository: kumaravinit/zero
  tag: "licensing-portal-1.0.1"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 3005

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
  hosts:
    - host: licensing.your-domain.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: licensing-portal-tls
      hosts:
        - licensing.your-domain.com

env:
  nodeEnv: production
  port: 3005
  issuer: "https://your-ids-domain.com/oidc"
  clientId: "licensing-portal-client"
  redirectUri: "https://licensing.your-domain.com/callback"
  # IDS running in fabrixly-ids namespace — cluster-internal DNS
  idsBaseUrl: "http://fabrixly-ids-backend.fabrixly-ids.svc.cluster.local:3000"
  idsAdminEmail: "admin@system.com"

secrets:
  sessionSecret: ""        # injected from K8s secret
  databaseUrl: "postgresql://admin:password@fabrixly-ids-postgresql.fabrixly-ids.svc.cluster.local:5432/licensing_db"
  idsAdminPassword: ""     # injected from K8s secret
  privateKey: ""           # injected from K8s secret

```

#### Step 3 — Install

```bash
helm install zero-licensing ./helm/zero-licencing-portal \
  -f helm/zero-licencing-portal/values-production.yaml \
  --namespace zero-licensing \
  --wait --timeout 3m

```

#### Step 4 — Verify

```bash
kubectl get pods -n zero-licensing
kubectl logs -f deployment/zero-licensing-licensing-portal -n zero-licensing
open https://licensing.your-domain.com

```

---

## 4\. Build & Push Images

### Prerequisites

```bash
# Login to Docker Hub
docker login -u kumaravinit

```

### Build and push all images (latest + versioned)

```bash
cd fabrixly-identity-server
chmod +x scripts/utils/build-and-push-latest.sh
./scripts/utils/build-and-push-latest.sh

```

This script builds and pushes all four images with both `latest` and `1.0.1` tags.

### Bump the version

Edit line 6 of `scripts/utils/build-and-push-latest.sh`:

```bash
VERSION="1.0.2"   # ← bump this

```

Then re-run the script.

---

## 5\. Self-Hosted Mode

When enabling self-hosting for customers:

1. Set `SELF_HOSTED=true` — plan enforcement switches to the global license key
2. Set `LICENSE_KEY=<jwt>` — issued by the Licensing Portal
3. Per-org subscription plan selection is **hidden** from the Admin Console
4. Org limits (users, M2M tokens, social providers) are read from the license JWT

```dotenv
# .env (IDS backend)
SELF_HOSTED=true
LICENSE_KEY=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...

```

In Helm:

```yaml
backend:
  env:
    SELF_HOSTED: "true"
    LICENSE_KEY: "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."

```

---

## 6\. Environment Variable Reference

### IDS Backend

| Variable                | Required | Default          | Description                                     |
| ----------------------- | -------- | ---------------- | ----------------------------------------------- |
| NODE\_ENV               | Yes      | development      | development or production                       |
| PORT                    | No       | 3000             | HTTP listen port                                |
| ISSUER                  | Yes      | —                | Full OIDC issuer URL (must end with /oidc)      |
| SESSION\_SECRET         | Yes      | —                | Min 32-char secret for sessions                 |
| JWT\_SECRET             | Yes      | —                | Min 32-char secret for JWTs                     |
| DB\_HOST                | Yes      | localhost        | PostgreSQL hostname                             |
| DB\_PORT                | No       | 5432             | PostgreSQL port                                 |
| DB\_USERNAME            | Yes      | —                | PostgreSQL user                                 |
| DB\_PASSWORD            | Yes      | —                | PostgreSQL password                             |
| DB\_NAME                | Yes      | —                | PostgreSQL database name                        |
| DB\_SYNC                | No       | false            | Auto-sync schema — **never true in production** |
| SMTP\_HOST              | Yes      | —                | SMTP hostname                                   |
| SMTP\_PORT              | No       | 587              | SMTP port                                       |
| SMTP\_USER              | Yes      | —                | SMTP user / API key                             |
| SMTP\_PASS              | Yes      | —                | SMTP password                                   |
| SMTP\_FROM              | Yes      | —                | Sender email address                            |
| SYSTEM\_ADMIN\_EMAIL    | No       | admin@system.com | Seeded admin email                              |
| SYSTEM\_ADMIN\_PASSWORD | No       | admin123         | Seeded admin password                           |
| AUTO\_SEED              | No       | false            | Seed DB on startup if true (idempotent)         |
| SELF\_HOSTED            | No       | false            | Enable self-hosted license mode                 |
| LICENSE\_KEY            | No       | —                | License JWT (required when SELF\_HOSTED=true)   |
| GOOGLE\_CLIENT\_ID      | No       | —                | Google OAuth client ID                          |
| GOOGLE\_CLIENT\_SECRET  | No       | —                | Google OAuth client secret                      |
| GITHUB\_CLIENT\_ID      | No       | —                | GitHub OAuth App client ID                      |
| GITHUB\_CLIENT\_SECRET  | No       | —                | GitHub OAuth App client secret                  |

### Licensing Portal

| Variable             | Required | Default | Description                                     |
| -------------------- | -------- | ------- | ----------------------------------------------- |
| PORT                 | No       | 3005    | HTTP listen port                                |
| ISSUER               | Yes      | —       | IDS OIDC issuer URL                             |
| CLIENT\_ID           | Yes      | —       | OIDC client ID registered in IDS                |
| CLIENT\_SECRET       | Yes      | —       | OIDC client secret                              |
| REDIRECT\_URI        | Yes      | —       | OAuth2 callback URL                             |
| SESSION\_SECRET      | Yes      | —       | Session encryption secret                       |
| DATABASE\_URL        | Yes      | —       | PostgreSQL connection string                    |
| IDS\_BASE\_URL       | Yes      | —       | IDS API base URL (no /oidc suffix)              |
| IDS\_ADMIN\_EMAIL    | Yes      | —       | IDS system admin email                          |
| IDS\_ADMIN\_PASSWORD | Yes      | —       | IDS system admin password                       |
| PRIVATE\_KEY         | Yes      | —       | RSA PKCS#8 PEM private key for signing licenses |

---

## 7\. Troubleshooting

### Backend crashes immediately

Check PostgreSQL is ready and reachable:

```bash
docker compose logs postgres
docker compose restart backend

```

### AUTO\_SEED not running

Verify the env var is passed through:

```bash
docker compose exec backend printenv AUTO_SEED

```

### Social login returns `Configuration Error`

1. Check that the OAuth provider credentials (`GOOGLE_CLIENT_ID`, etc.) are set in the backend env
2. Ensure the org's plan allows the social provider (check `planService` in backend logs)
3. Verify the OAuth callback URL is whitelisted in the provider's developer console

### Licensing portal can't connect to IDS

From inside the licensing portal container, test connectivity:

```bash
docker compose exec licensing-portal wget -qO- http://host.docker.internal:3000/api/health

```

If unreachable, set `IDS_BASE_URL` to the correct host/port.

### License key is invalid at IDS

The `PRIVATE_KEY` used in the Licensing Portal to **sign** licenses must correspond to the public key embedded in the IDS to **verify** them. Regenerate consistently.

---

## Quick-Start Cheatsheet

```bash
# 1. Clone IDS
git clone https://github.com/your-org/fabrixly-identity-server.git
cd fabrixly-identity-server

# 2. Generate secrets
echo "SESSION_SECRET=$(openssl rand -base64 32)" > .env
echo "JWT_SECRET=$(openssl rand -base64 32)" >> .env
echo "AUTO_SEED=true" >> .env
echo "DB_USERNAME=admin" >> .env
echo "DB_PASSWORD=admin123" >> .env
echo "ISSUER=http://localhost:3000/oidc" >> .env

# 3. Start IDS
docker compose up -d

# 4. Watch logs until ready
docker compose logs -f backend

# 5. Open Admin Console
open http://localhost
# Credentials: admin@system.com / admin123

```