Porta v1.11.0
Skip to content

Quick Start ​

Get Porta running in 5 minutes with Docker.

Prerequisites ​

  • Docker with Docker Compose v2+

Looking for other setup methods?

This guide uses Docker Hub images for the fastest setup. For cloning the repo or developing from source, see Setup Alternatives.


Automated Install ​

The installer writes docker-compose.yml and a .env with generated secrets, starts the stack, applies migrations, and optionally bootstraps the admin system:

bash
curl -fsSL https://raw.githubusercontent.com/blendsdk/porta-identity/main/install-porta.sh | bash

It prompts for your public URL and SMTP relay, scans for a free host port to publish, and prints an nginx reverse-proxy example. For an unattended install, pass flags instead (run the script with --help for the full list):

bash
curl -fsSL https://raw.githubusercontent.com/blendsdk/porta-identity/main/install-porta.sh \
  | bash -s -- \
      --issuer-url https://auth.example.com \
      --smtp-host smtp.example.com --smtp-from noreply@example.com

Re-running with --force reuses every saved answer and only asks for keys that are missing or empty. --check lists those values without changing anything, and --fresh ignores the saved file and starts over.

The manual steps below describe the same deployment file by file.


Step 1: Create a Project Directory ​

bash
mkdir porta && cd porta

Step 2: Generate Required Secrets ​

Required — Do Not Skip

Porta requires 3 cryptographic secrets to operate securely. These protect session cookies, 2FA secrets, and signing keys. You must generate unique values — do not use the defaults or placeholders.

Run these commands to generate all three secrets:

bash
# 1. Cookie signing key (base64, at least 32 chars)
echo "COOKIE_KEYS=$(openssl rand -base64 32)"

# 2. Two-Factor encryption key (64 hex chars = 32 bytes, AES-256-GCM)
echo "TWO_FACTOR_ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")"

# 3. Signing key encryption key (64 hex chars = 32 bytes, AES-256-GCM)
echo "SIGNING_KEY_ENCRYPTION_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")"

Save the output — you'll paste these into your .env file in Step 4.

SecretPurposeFormat
COOKIE_KEYSSigns OIDC session cookiesBase64 string, ≥32 chars
TWO_FACTOR_ENCRYPTION_KEYEncrypts TOTP authenticator secrets at rest64 hex characters (32 bytes)
SIGNING_KEY_ENCRYPTION_KEYEncrypts ES256 signing key private keys at rest64 hex characters (32 bytes)

Step 3: SMTP — Email is Required ​

Porta requires a working SMTP server

Magic links, user invitations, password resets, and email-based 2FA all send emails. Without SMTP, these features will fail silently.

For local development, we include MailHog in the Docker Compose file below — it catches all outgoing emails and provides a web inbox at http://localhost:8025. No extra setup needed.

For production, configure a real SMTP server:

VariableExampleDescription
SMTP_HOSTsmtp.sendgrid.netSMTP server hostname
SMTP_PORT587SMTP port (587 for STARTTLS, 465 for SSL)
SMTP_USERapikeySMTP username
SMTP_PASSSG.xxxxxSMTP password or API key
SMTP_FROMnoreply@yourdomain.comSender email address

Popular options: SendGrid, Amazon SES, Postmark, Mailgun, or any SMTP-compatible service.


Step 4: Create docker-compose.yml ​

Create a file called docker-compose.yml:

yaml
services:
  # ── Porta OIDC Provider ─────────────────────
  porta:
    image: blendsdk/porta:latest
    container_name: porta-app
    restart: unless-stopped
    ports:
      - '${PORT:-3000}:3000'
    env_file:
      - .env
    environment:
      DATABASE_URL: postgresql://porta:${POSTGRES_PASSWORD:-porta_secret}@postgres:5432/porta
      REDIS_URL: redis://redis:6379
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_healthy
    healthcheck:
      test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
      interval: 30s
      timeout: 5s
      start_period: 30s
      retries: 3

  # ── PostgreSQL 16 ───────────────────────────
  postgres:
    image: postgres:16-alpine
    container_name: porta-postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: porta
      POSTGRES_USER: porta
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-porta_secret}
    volumes:
      - porta_pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U porta']
      interval: 5s
      timeout: 5s
      retries: 5

  # ── Redis 7 ─────────────────────────────────
  redis:
    image: redis:7-alpine
    container_name: porta-redis
    restart: unless-stopped
    healthcheck:
      test: ['CMD', 'redis-cli', 'ping']
      interval: 5s
      timeout: 5s
      retries: 5

  # ── MailHog (dev email testing) ─────────────
  mailhog:
    image: mailhog/mailhog
    container_name: porta-mailhog
    ports:
      - '8025:8025' # Web UI
      - '1025:1025' # SMTP
    profiles:
      - dev

volumes:
  porta_pgdata:
    driver: local

MailHog for development

Start with docker compose --profile dev up -d to include MailHog. Then open http://localhost:8025 to see all emails Porta sends.


Step 5: Create .env ​

Create a .env file and paste in your generated secrets from Step 2:

env
# ── Server ────────────────────────────────────
NODE_ENV=production
PORT=3000
HOST=0.0.0.0

# ── Database ──────────────────────────────────
POSTGRES_PASSWORD=porta_secret

# ── OIDC ──────────────────────────────────────
ISSUER_BASE_URL=https://porta.local:3443

# ── Secrets (paste values from Step 2) ────────
COOKIE_KEYS=<paste-your-cookie-key-here>
TWO_FACTOR_ENCRYPTION_KEY=<paste-your-2fa-key-here>
SIGNING_KEY_ENCRYPTION_KEY=<paste-your-signing-key-here>

# ── Email (MailHog for dev, real SMTP for prod)
SMTP_HOST=mailhog
SMTP_PORT=1025
SMTP_USER=
SMTP_PASS=
SMTP_FROM=noreply@porta.local

# ── Logging ───────────────────────────────────
LOG_LEVEL=info

# ── Startup ───────────────────────────────────
PORTA_AUTO_MIGRATE=true
TRUST_PROXY=false

Replace the secret placeholders!

Replace <paste-your-cookie-key-here>, <paste-your-2fa-key-here>, and <paste-your-signing-key-here> with the values you generated in Step 2. Porta will refuse to start with placeholder values in production.


Step 6: Start Services ​

bash
# For development (with MailHog email testing):
docker compose --profile dev up -d

# For production (without MailHog):
docker compose up -d

Wait a few seconds for PostgreSQL and Redis to become healthy, then verify:

bash
curl https://porta.local:3443/health

You should see {"status":"ok","database":"ok","redis":"ok"}.


Step 7: Bootstrap the Admin System ​

bash
docker exec -it porta-app porta init

This interactive command creates:

  • The super-admin organization (porta-admin)
  • The admin application with 42 RBAC permissions
  • A PKCE client for CLI authentication
  • Your first admin user (you'll be prompted for email, name, and password)

You can also run it non-interactively:

bash
docker exec porta-app porta init \
  --email admin@example.com \
  --given-name Admin \
  --family-name User \
  --password 'YourSecurePassword123!'

Step 8: Install the Standalone CLI ​

The Porta Docker image includes infrastructure commands only (init, migrate, seed, health). For full admin management, install the standalone CLI on your workstation:

bash
npm install -g @portaidentity/cli

Then authenticate against your Porta server:

bash
porta login --server https://porta.local:3443

The CLI opens your browser for OIDC authentication. After logging in, you can manage everything:

bash
porta whoami           # Check current identity
porta org list         # List organizations
porta version          # Show CLI/SDK/server versions

Docker wrapper (infrastructure only)

For init/migrate commands inside the container, you can also download the wrapper script:

bash
curl -fsSL https://raw.githubusercontent.com/blendsdk/porta-identity/main/docker/porta.sh \
  -o porta && chmod +x porta
./porta init
./porta migrate status

Step 9: Configure Your Environment ​

Before you provision applications and clients

Applications, modules, roles, permissions, and claim definitions are deployment-global; your organization owns its clients, users, and assignments. Read the Core ownership model before you create them.

Open the interactive administration shell:

bash
porta admin --server https://porta.local:3443

Create the organization, applications, roles, permissions, users, and OIDC clients needed by your project. Existing command-line CRUD commands are also available when you prefer scripts.

Moving configuration from another Porta installation

Export a selective JSON manifest from the source and import it into this installation. Import always previews before it applies changes.

bash
porta export manifest \
  --organization my-company \
  --category organizations \
  --category applications_authorization \
  --all-applications \
  --output my-company-porta.json

porta import manifest my-company-porta.json --mode keep-existing

Read Environment Portability for category selection, import modes, and credential handling.


Step 10: Verify Everything Works ​

bash
# Check health
curl https://porta.local:3443/health

# List organizations with the standalone CLI
porta org list

# Open MailHog to see test emails (if using dev profile)
# http://localhost:8025

Open https://porta.local:3443/health in your browser to confirm the server, database, and Redis are all connected.


Stopping & Cleanup ​

bash
# Stop all services
docker compose down

# Stop and delete all data (fresh start)
docker compose down -v

Troubleshooting ​

Container won't start ​

bash
docker compose logs porta

Common causes:

  • Missing or placeholder secrets — check your .env (see Step 2)
  • PostgreSQL not ready yet — the entrypoint waits up to 60 seconds
  • Port 3000 already in use — change PORT in your .env file

Emails not being sent ​

  • Development: Make sure you started with --profile dev for MailHog, and check http://localhost:8025
  • Production: Verify your SMTP settings. Test with telnet your-smtp-host 587.
  • Check Porta logs: docker compose logs porta | grep -i smtp

Login page loads but authentication fails ​

Most likely missing TRUST_PROXY=true when running behind a TLS-terminating reverse proxy (nginx, Traefik, cloud load balancer). See Environment Variables → Reverse Proxy.

Health check failing ​

bash
docker compose ps   # All services should show "healthy"
docker compose logs postgres  # Check database

Next Steps ​

Released under the MIT License.