Skip to content

Deployment Guide

This guide covers deploying JIM to a production environment, including prerequisites, architecture, installation procedures for both connected and air-gapped environments, TLS configuration, and operational guidance.

Quick Start

The Getting Started guide gets you running in under five minutes. This page covers production hardening, reverse proxies, upgrades, and operational best practices.

Prerequisites

Hardware Requirements

Component Minimum Recommended
CPU 2 cores 4+ cores
RAM 4 GB 8+ GB
Storage 20 GB 50+ GB (depends on identity data volume)

Storage scales with the number of identity objects and the frequency of synchronisation runs (change history, logs, etc.).

Memory Scaling by Identity Object Count

The worker service loads all objects from a connector into memory during full import processing (CSOs, attribute values, RPEIs, duplicate detection structures). Memory requirements scale linearly with the number of objects in the largest Connected System.

These figures are for the host machine (or VM) running the Docker stack -- they must cover the operating system, all JIM containers, and the database.

Connected System Size Minimum Host RAM Recommended Host RAM
Up to 10,000 objects 4 GB 8 GB
10,000 -- 50,000 objects 8 GB 16 GB
50,000 -- 100,000 objects 20 GB 24 GB
100,000 -- 250,000 objects 24 GB 32 GB
250,000 -- 500,000 objects 48 GB 64 GB

Why large imports need significant memory

During a full import, the worker loads all imported objects with their attributes into memory before the save phase begins (for duplicate detection, deletion detection, and reference resolution). A full import of 100,000 objects with 20 attributes each produces a worker peak working set of approximately 2.3 GB. The database requires an additional 1--2 GB during bulk inserts. Combined with the web, scheduler, and operating system overhead, total system memory consumption reaches 8--10 GB for 100K objects.

Note

These requirements apply to the largest single full import. If you have multiple Connected Systems of 50K objects each but import them sequentially (not concurrently), size for 50K, not the sum. Delta imports process only changed objects and require significantly less memory.

Large group memberships drive memory more than object count

A group is loaded with its full member list during processing, so a single large group can dominate the working set: a group with 495,000 members loads 495,000 reference values. JIM's largest validated scenario (a cross-domain synchronisation between two directories of roughly 500,000 objects each, with groups of up to 495,000 members) peaked at approximately 55 GB total host RAM with the full stack, database, and both directories resident. Size toward the upper figure in the table when provisioning or synchronising very large groups; a deployment of the same object count with only small groups needs considerably less.

Software Requirements

  • Docker Engine 20.10+ with Compose v2
  • An OIDC identity provider (e.g. Entra ID, Keycloak, AD FS) -- see SSO Setup

Network Requirements

JIM's services communicate internally over a Docker bridge network (jim-network). The only port that needs to be exposed externally is the web/API port on jim.web (container port 80).

Direction Port Purpose
Inbound Your chosen host port (e.g. 443 via reverse proxy) Web UI and REST API
Outbound Varies OIDC provider, LDAP targets, file shares, etc.

In air-gapped environments, no outbound connectivity is required after initial deployment.


Architecture Overview

JIM runs as a Docker Compose stack with four services:

The JIM Docker Compose stack Four Docker Compose services. jim.web hosts the Blazor UI and REST API, jim.worker processes synchronisation tasks, and jim.scheduler triggers scheduled runs. All three connect to jim.database, a PostgreSQL instance that can be the bundled container or an external server. jim.web Blazor UI + REST API jim.worker sync processor jim.scheduler cron & interval triggers jim.database PostgreSQL 18 bundled or external

All three JIM services coordinate through PostgreSQL; the Scheduler queues tasks and the Worker polls them, so no service calls another directly. The bundled database container is optional; JIM can use an external PostgreSQL server instead. Moving dots trace database traffic.

Service Description
jim.web Blazor Server UI and REST API (/api/)
jim.worker Processes import, synchronisation, and export tasks
jim.scheduler Triggers synchronisation runs on cron or interval schedules
jim.database PostgreSQL 18 (optional bundled container)

Docker Volumes

Volume Purpose
jim-db-volume PostgreSQL data (bundled DB only)
jim-logs-volume Application and database logs
jim-keys-volume Encryption keys

Back up the key volume with the database

jim-keys-volume holds the encryption keys that protect every stored secret (Connected System credentials, the SSO secret, Schedule SQL-step connection strings). The database ciphertext cannot be decrypted without these keys, so a database backup restored without its matching keys leaves every secret unrecoverable. Back up the key volume and the database together, as a matched pair. See Backup & Disaster Recovery.

Bundled vs External PostgreSQL

Bundled External
Setup Automatic -- included in Docker stack You manage PostgreSQL separately
Started with --profile with-db flag No profile flag needed
Best for Evaluations, small deployments Production, existing DBA team
Backup Docker volume snapshots Your existing DB backup tooling
Tuning Default settings in compose file Full control

Tip

Start with bundled PostgreSQL for evaluation. Migrate to external for production workloads where you need backup, high availability, or monitoring integration.


Connected Deployment

The setup script downloads compose files, walks you through configuration, and starts JIM:

curl -fsSL https://junctional.io/get | bash

Or download and inspect first:

curl -fsSL -o setup.sh https://raw.githubusercontent.com/TetronIO/JIM/main/deploy/setup.sh
less setup.sh
bash setup.sh

The script will:

  1. Check prerequisites (Docker, Compose v2, curl)
  2. Auto-detect the latest release
  3. Download compose files and environment template
  4. Walk you through database and SSO configuration
  5. Optionally start JIM

Option 2 -- Manual Setup

mkdir jim && cd jim

# Download compose files and environment template
curl -fsSL -o docker-compose.yml \
  https://github.com/TetronIO/JIM/releases/latest/download/docker-compose.yml
curl -fsSL -o docker-compose.production.yml \
  https://github.com/TetronIO/JIM/releases/latest/download/docker-compose.production.yml
curl -fsSL -o .env \
  https://github.com/TetronIO/JIM/releases/latest/download/.env.example

# Edit .env -- configure SSO and database settings (see Configuration Reference)

# Start with bundled PostgreSQL
docker compose -f docker-compose.yml -f docker-compose.production.yml \
  --profile with-db up -d

# Or with external PostgreSQL (set JIM_DB_HOSTNAME in .env)
docker compose -f docker-compose.yml -f docker-compose.production.yml up -d

Air-Gapped Deployment

Each release includes a bundle (jim-release-X.Y.Z.tar.gz) containing pre-built Docker images, compose files, and installation instructions. This section covers the full air-gapped deployment procedure.

Bundle Contents

jim-release-X.Y.Z/
+-- docker-images/
|   +-- jim-web.tar           # Docker image for web/API service
|   +-- jim-worker.tar        # Docker image for worker service
|   +-- jim-scheduler.tar     # Docker image for scheduler service
|   +-- postgres-18.tar       # PostgreSQL image (if included)
+-- compose/
|   +-- docker-compose.yml
|   +-- docker-compose.override.yml
|   +-- docker-compose.production.yml
|   +-- .env.example
+-- powershell/
|   +-- JIM/                  # PowerShell module directory
+-- docs/
|   +-- README.md
|   +-- CHANGELOG.md
|   +-- INSTALL.md
+-- checksums.sha256          # SHA256 checksums for verification
+-- README.txt                # Quick start guide

Prerequisites

Before deploying JIM in an air-gapped environment, ensure you have:

  • Docker Engine (20.10+) and Docker Compose (v2+) installed
  • PostgreSQL 18 -- either as a container or external database server
  • A DNS name or IP address for the JIM server
  • TLS certificates if enabling HTTPS (recommended for production)
  • An OIDC identity provider accessible from the air-gapped network (e.g. AD FS, Keycloak)

Step 1: Transfer and Verify the Bundle

# Transfer jim-release-X.Y.Z.tar.gz via approved process

# Extract the bundle
tar -xzf jim-release-X.Y.Z.tar.gz
cd jim-release-X.Y.Z

# Verify checksums
sha256sum -c checksums.sha256

Step 2: Load Docker Images

# Load JIM images
docker load -i docker-images/jim-web.tar
docker load -i docker-images/jim-worker.tar
docker load -i docker-images/jim-scheduler.tar

# If using bundled PostgreSQL (optional)
docker load -i docker-images/postgres-18.tar

Step 3: Set Up PostgreSQL (Select an Option)

Option A: Use the bundled PostgreSQL container (simpler, suitable for smaller deployments)

# Start with bundled database
docker compose --profile with-db up -d

Option B: Use an external PostgreSQL server (recommended for production)

  1. Create a database and user:

    CREATE DATABASE jim;
    CREATE USER jim WITH ENCRYPTED PASSWORD 'your_secure_password';
    GRANT ALL PRIVILEGES ON DATABASE jim TO jim;
    
  2. Update .env with your database connection details (see Configuration Reference)

  3. Start JIM without the database profile:

    docker compose up -d
    

Step 4: Configure Environment

cd compose
cp .env.example .env

Edit .env with your settings. See Configuration Reference for the full list of variables.

Step 5: Configure DNS

Ensure your JIM server is resolvable by name in your network:

  1. Add a DNS A record pointing to your JIM server's IP address, or
  2. Add an entry to /etc/hosts on client machines:

    192.168.1.100  jim.your-domain.local
    

The OIDC redirect URIs configured in your identity provider must match the JIM server's accessible URL.

Step 6: File Connector storage (optional)

The File Connector ships pre-configured to read and write at /connector-files inside the container, backed by a Docker-managed volume named jim-connector-files-volume. No setup is required for the default case: start the stack and the volume is created automatically with correct ownership.

To put files in or pull them out:

# Push an import file into the volume
docker cp ./Users.csv jim.worker:/connector-files/Users.csv

# Pull an exported file out
docker cp jim.worker:/connector-files/Exports.csv ./Exports.csv

Then configure the File Connector's File Path setting as /connector-files/Users.csv.

If you need to integrate with an external system that writes to a fixed network location (e.g. an SMB or NFS share), bind-mount that path over a subdirectory of /connector-files. See the JIM File Connector documentation for the full pattern, including the UID 1654 ownership requirement for bind-mounted host paths.

Step 7: Start Services

# With bundled PostgreSQL
docker compose --profile with-db up -d

# With external PostgreSQL
docker compose up -d

# Check all services are running
docker compose ps

# View logs
docker compose logs -f

Step 8: Verify Startup

JIM sets the database up automatically on first startup; there is no manual database step. Watch the logs to confirm:

docker compose logs jim.worker --tail=50

The worker prepares the database before it starts accepting work, and jim.web will not serve requests until that has finished; while it waits, jim.web logs JIM.Application is not ready yet. Sleeping... once per second. The definitive signal is the readiness endpoint, which returns 503 Service Unavailable until JIM is ready and 200 OK afterwards:

curl -s -o /dev/null -w '%{http_code}\n' http://localhost:5200/api/v1/health/ready

Troubleshooting

If the database setup fails (e.g. due to a permissions issue), the worker logs will contain the full error. Fix the underlying issue and restart the services.

Step 9: Access JIM

  1. Open your browser to https://jim.your-domain.local (or http://localhost:5200 if no TLS)
  2. Log in with your SSO credentials
  3. Verify access - the initial admin user (configured via JIM_SSO_INITIAL_ADMIN) will have full access

TLS and Reverse Proxy

The JIM containers serve HTTP on port 80 internally. For production, place a reverse proxy in front to handle TLS termination.

Important

Blazor Server uses WebSockets (SignalR). Your reverse proxy must support WebSocket connections, or the UI will fall back to long polling with degraded performance.

nginx Example

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

    ssl_certificate     /etc/ssl/certs/jim.example.com.pem;
    ssl_certificate_key /etc/ssl/private/jim.example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass http://localhost:5200;
        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 (required for Blazor Server)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }
}

Port Mapping

The base docker-compose.yml does not expose ports externally. You need to add a port mapping. Create a docker-compose.ports.yml override:

services:
  jim.web:
    ports:
      - "5200:80"

Then include it in your compose command:

docker compose -f docker-compose.yml -f docker-compose.production.yml \
  -f docker-compose.ports.yml --profile with-db up -d

Alternatively, add the ports mapping directly to docker-compose.production.yml after downloading it.


Health Monitoring

Health Endpoints

Endpoint Purpose
/api/v1/health Basic liveness check
/api/v1/health/ready Readiness check (includes database connectivity)

The jim.web container includes a Docker healthcheck using the readiness endpoint.

The jim.worker and jim.scheduler containers use file-based healthcheck monitoring. Each service writes a heartbeat file periodically during normal operation, and the Docker healthcheck verifies the file is recent. This means docker compose ps and orchestrators like Docker Swarm or Kubernetes can detect when a worker or scheduler has stalled, even if the process itself has not exited.

Logging

JIM writes structured logs to the jim-logs-volume Docker volume. Configure log level via .env:

JIM_LOG_LEVEL=Information

Valid levels: Verbose, Debug, Information, Warning, Error, Fatal.

View logs with Docker Compose:

# Follow all service logs
docker compose -f docker-compose.yml -f docker-compose.production.yml logs -f

# View recent logs for a specific service
docker compose -f docker-compose.yml -f docker-compose.production.yml \
  logs jim.web --tail=100

JIM also includes a Logs page in the web UI for viewing application and database logs.


PowerShell Module

JIM includes a cross-platform PowerShell module for scripting, automation, and Identity as Code (IDaC). The module works on Windows, macOS, and Linux and requires PowerShell 7.0+.

Connected Installation

Install directly from the PowerShell Gallery:

Install-Module -Name JIM

To update to a newer version:

Update-Module -Name JIM

Air-Gapped Installation

There are two options for installing the module in disconnected environments.

Each release bundle (jim-release-X.Y.Z.tar.gz) includes the module pre-packaged in powershell/JIM/. After extracting the bundle, copy the module to a PSModulePath directory:

# Windows
Copy-Item -Recurse ./powershell/JIM "$env:USERPROFILE\Documents\PowerShell\Modules\"

# Linux / macOS
Copy-Item -Recurse ./powershell/JIM "~/.local/share/powershell/Modules/"

On a connected machine, use Save-Module to download the module to a local directory without installing it:

Save-Module -Name JIM -Path C:\Modules

Transfer the C:\Modules\JIM\ directory to the disconnected environment, then copy it to a PSModulePath directory:

# Windows
Copy-Item -Recurse C:\Transfer\JIM "$env:USERPROFILE\Documents\PowerShell\Modules\"

# Linux / macOS
Copy-Item -Recurse /mnt/transfer/JIM "~/.local/share/powershell/Modules/"

Verifying the Installation

Import-Module JIM
Get-Module JIM    # Verify the module loaded and check the version

Connecting to JIM

# Interactive -- opens browser for SSO sign-in
Connect-JIM -Url "https://jim.example.com"

# Automation -- use an API key (recommended for scripts and CI/CD)
Connect-JIM -Url "https://jim.example.com" -ApiKey "jim_xxxxxxxxxxxx"

# Verify the connection
Test-JIMConnection

Production Readiness Checklist

Use this checklist before going live:

  • TLS configured via reverse proxy (minimum TLS 1.2)
  • Strong, unique database password set
  • SSO configured and tested with your identity provider
  • Initial admin user can log in and access the administration UI
  • Database backup strategy in place and tested
  • Encryption keys (jim-keys-volume / JIM_ENCRYPTION_KEY_PATH) backed up alongside the database, as a matched pair (see Backup & Disaster Recovery)
  • Full restore (database plus keys) rehearsed, with a Connected System confirmed to reconnect
  • Log level set appropriately (Information for production)
  • Health endpoint monitored by your alerting system
  • Firewall rules restrict access to JIM's port to authorised networks
  • Docker restart policy is unless-stopped (set by production override)
  • Upgrade procedure documented and tested in staging (see Upgrading)
  • PowerShell module installed and connected (if using automation/IDaC)

Air-Gapped Network Checklist

For air-gapped deployments, also verify:

  • All Docker images loaded successfully (docker images | grep jim)
  • PostgreSQL is accessible and the database has been set up
  • SSO/OIDC identity provider is accessible from JIM server
  • DNS resolves JIM server name correctly
  • TLS certificates are valid and trusted (if using HTTPS)
  • Firewall allows inbound traffic to JIM's web port only (your chosen host port, e.g. 443 via reverse proxy, or the mapped HTTP port). The bundled PostgreSQL container publishes no host port; the database is reached only over the internal jim-network bridge
  • If using an external PostgreSQL server: the JIM host can reach it on 5432 (outbound, allowed on the database server's firewall)
  • File connector volumes mounted (if using File Connector)
  • Encryption key set backed up and included in the offline backup routine (see Backup & Disaster Recovery)
  • Initial admin user can log in
  • Logs are being written to the configured path
  • Logs are being written to configured path