Self-Hosting

UniAuth is designed to be self-hosted, giving you full control over your identity infrastructure. This guide covers system requirements, configuration, database setup, and production deployment.

System Requirements

ComponentMinimumRecommended
Node.js20.x22.x LTS
PostgreSQL1516+
RedisOptional7.x (for distributed rate limiting)
RAM512 MB1 GB+
Disk1 GB10 GB+ (for logs and database)

Environment Variables

Create a .env.local file in the project root with the following variables:

Required

VariableDescriptionExample
DB_HOSTPostgreSQL hostnamelocalhost
DB_PORTPostgreSQL port5432
DB_USERDatabase userpostgres
DB_PASSWORDDatabase passwordyour-secure-password
DB_NAMEDatabase nameuniauth_db
JWT_SECRETSecret for signing JWTs (min 32 chars)a-long-random-string...
ENCRYPTION_KEYAES-256-GCM key (exactly 64 hex chars)0a1b2c3d...64 hex chars
HOSTPublic-facing URL (used for OAuth redirects, WebAuthn RP ID)https://auth.example.com

Optional: Email (SMTP)

SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=your-smtp-user
SMTP_PASS=your-smtp-password
SMTP_SECURE=false        # true for port 465
[email protected]

When SMTP is not configured, emails are logged to the console. This is useful for development but should be configured for production.

Optional: Social Login (OAuth)

# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret

# GitHub OAuth
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

Optional: SMS 2FA (Twilio)

TWILIO_ACCOUNT_SID=your-twilio-sid
TWILIO_AUTH_TOKEN=your-twilio-auth-token
TWILIO_PHONE_NUMBER=+1234567890

Optional: Redis

REDIS_URL=redis://localhost:6379

Redis enables distributed rate limiting across multiple instances. Without Redis, rate limiting uses an in-memory store (suitable for single-instance deployments).

Generating Secrets

Use the following commands to generate cryptographically secure secrets:

# Generate ENCRYPTION_KEY (64 hex chars = 32 bytes = AES-256)
openssl rand -hex 32

# Generate JWT_SECRET
openssl rand -base64 48

# Alternatively, using Node.js:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

Database Setup

UniAuth uses PostgreSQL. Create the database and apply the schema:

# Create the database
psql -U postgres -c "CREATE DATABASE uniauth_db;"

# Apply the main schema
psql -U postgres -d uniauth_db -f scripts/pg-schema.sql

# Apply migrations (in order)
psql -U postgres -d uniauth_db -f scripts/migration-crypto.sql
psql -U postgres -d uniauth_db -f scripts/migration-hardening.sql

The schema creates all required tables including users, sessions, oauth_clients, roles, system_settings, and others. The connection pool is managed by lib/db.ts using the pg driver.

Quick Start

# Clone the repository
git clone https://github.com/your-org/uniauth.git
cd uniauth

# Install dependencies
npm install

# Copy and edit environment variables
cp .env.example .env.local
# Edit .env.local with your values

# Build for production
npm run build

# Start the server (default port 4000)
npm start

# Or run in development mode
npm run dev

The server listens on port 4000 by default. You can change this with the PORT environment variable.

Running Behind a Reverse Proxy

In production, UniAuth should run behind a reverse proxy that handles TLS termination. Below are example configurations for common web servers.

Nginx

server {
    listen 443 ssl http2;
    server_name auth.example.com;

    ssl_certificate     /etc/ssl/certs/auth.example.com.pem;
    ssl_certificate_key /etc/ssl/private/auth.example.com.key;

    # Security headers (UniAuth adds its own, but these are belt-and-suspenders)
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;

    location / {
        proxy_pass http://127.0.0.1:4000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket support (for dev hot reload)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name auth.example.com;
    return 301 https://$host$request_uri;
}

Apache

<VirtualHost *:443>
    ServerName auth.example.com

    SSLEngine on
    SSLCertificateFile    /etc/ssl/certs/auth.example.com.pem
    SSLCertificateKeyFile /etc/ssl/private/auth.example.com.key

    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:4000/
    ProxyPassReverse / http://127.0.0.1:4000/

    RequestHeader set X-Forwarded-Proto "https"
    RequestHeader set X-Forwarded-Port "443"
</VirtualHost>

<VirtualHost *:80>
    ServerName auth.example.com
    Redirect permanent / https://auth.example.com/
</VirtualHost>

Important: HOST Environment Variable

When running behind a reverse proxy, the HOST environment variable must be set to your public-facing URL (e.g., https://auth.example.com). UniAuth uses this for OAuth redirect URLs, WebAuthn Relying Party ID, and OIDC Discovery. Without it, URLs will resolve to localhost:4000.

HTTPS / TLS Setup

TLS is strongly recommended for production. UniAuth relies on HTTPS for:

  • Secure cookie transmission (Secure flag on auth cookies)
  • HSTS headers (automatically enabled in production)
  • WebAuthn (passkeys require a secure context)
  • OAuth redirect URI validation

For free TLS certificates, use Let's Encrypt with certbot or your hosting provider's built-in certificate management.

Production Checklist

Before going live, verify the following:

  • HOST is set to your public URL (e.g., https://auth.example.com).
  • ENCRYPTION_KEY is generated using openssl rand -hex 32 (64 hex chars). Back up this key securely — losing it means losing access to encrypted data.
  • JWT_SECRET is strong — at least 32 random characters.
  • HTTPS is enabled via a reverse proxy with a valid TLS certificate.
  • HSTS is active — UniAuth sends HSTS headers automatically in production.
  • SMTP is configured for email verification, password resets, and email-based 2FA.
  • Database backups are scheduled — regular pg_dump exports.
  • Rate limiting works — test that login rate limits (10 per 15 min) are enforced.
  • Firewall rules — only expose ports 80/443 publicly. PostgreSQL (5432) and Redis (6379) should be internal only.
  • Admin account is set up — the first user registered can be promoted to admin via the database or by setting the email in the admin configuration.