Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.jitera.ai/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through deploying Jitera Self-Hosted on self-managed Kubernetes clusters, including kubeadm, k3s, RKE2, and other distributions.
All examples in this guide use the following sample values. Replace them with your actual values:
  • Helm release name / namespace: jitera (i.e., helm install jitera ./charts/jitera --namespace jitera) — Kubernetes resource names are prefixed with this (e.g., jitera-automation-rails, jitera-ultron)
  • MinIO bucket names: jitera-default, jitera-public, jitera-export, jitera-ultron
  • Domain: jitera.yourdomain.com
  • MinIO domains: minio.example.com, minio-console.example.com
  • Secret names: jitera-registry (image pull secret), jitera-tls (TLS certificate)

Prerequisites Checklist

Complete the Deployment Requirements checklist first, then confirm the following on-premises-specific items:
  • Storage provisioner configured (Longhorn, Rook-Ceph, or local-path)
  • Load balancer solution (MetalLB or hardware)
  • TLS certificate (or cert-manager for Let’s Encrypt)
The third-party tool procedures in this guide (MetalLB, cert-manager, Longhorn, Rook-Ceph) are provided as examples. Refer to the official documentation for each tool for the most up-to-date instructions:

Infrastructure Requirements

Kubernetes Cluster

RequirementMinimumRecommended
Nodes35+
CPU per node4 cores8+ cores
Memory per node16 GB32+ GB
Storage per node100 GB SSD200+ GB NVMe

Network Requirements

RequirementSpecification
Pod CIDR/16 (65,536 IPs)
Service CIDR/16
Load BalancerMetalLB or hardware
IngressNGINX or Traefik

Storage Requirements

ComponentSizeType
PostgreSQL100 GBSSD
MongoDB100 GBSSD
Redis20 GBSSD
Object Storage500 GB+HDD or SSD

Storage Provisioner Setup

Option 1: Local Path Provisioner

For development or small deployments:
# Install local-path-provisioner
kubectl apply -f https://raw.githubusercontent.com/rancher/local-path-provisioner/master/deploy/local-path-storage.yaml

# Set as default storage class
kubectl patch storageclass local-path -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

# Verify
kubectl get storageclass

Option 2: Longhorn

For production with replication:
# Install Longhorn
helm repo add longhorn https://charts.longhorn.io
helm repo update

helm install longhorn longhorn/longhorn \
  --namespace longhorn-system \
  --create-namespace \
  --set defaultSettings.defaultDataPath="/var/lib/longhorn"

# Set as default storage class
kubectl patch storageclass longhorn -p '{"metadata": {"annotations":{"storageclass.kubernetes.io/is-default-class":"true"}}}'

Option 3: Rook-Ceph

For large-scale distributed storage:
# Clone Rook repository
git clone --single-branch --branch release-1.13 https://github.com/rook/rook.git
cd rook/deploy/examples

# Install Rook operator
kubectl create -f crds.yaml -f common.yaml -f operator.yaml

# Create Ceph cluster
kubectl create -f cluster.yaml

# Create storage class
kubectl create -f csi/rbd/storageclass.yaml

Load Balancer Setup (MetalLB)

Install MetalLB

# Install MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml

# Wait for pods
kubectl wait --namespace metallb-system \
  --for=condition=ready pod \
  --selector=app=metallb \
  --timeout=90s

Configure IP Address Pool

# metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: default-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.1.240-192.168.1.250  # Adjust to your network
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: default
  namespace: metallb-system
spec:
  ipAddressPools:
  - default-pool
kubectl apply -f metallb-config.yaml

TLS Certificate Options

Option 1: cert-manager with Let’s Encrypt

# Install cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update

helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true

# Create ClusterIssuer
cat <<EOF | kubectl apply -f -
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@yourdomain.com
    privateKeySecretRef:
      name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: nginx
EOF

Option 2: Self-Signed Certificate

# Generate self-signed certificate
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key \
  -out tls.crt \
  -subj "/CN=jitera.yourdomain.com"

# Create Kubernetes secret
kubectl create secret tls jitera-tls \
  --namespace jitera \
  --cert=tls.crt \
  --key=tls.key

Option 3: Existing Certificate

# Create secret from existing certificate files
kubectl create secret tls jitera-tls \
  --namespace jitera \
  --cert=/path/to/fullchain.pem \
  --key=/path/to/privkey.pem

Installation Steps

Step 1: Install Ingress Controller

# Install NGINX Ingress Controller
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

helm install ingress-nginx ingress-nginx/ingress-nginx \
  --namespace ingress-nginx \
  --create-namespace

# Verify external IP is assigned (via MetalLB)
kubectl get svc -n ingress-nginx ingress-nginx-controller

Step 2: Create Namespace and Secrets

# Create namespace
kubectl create namespace jitera

# Create registry secret
kubectl create secret docker-registry jitera-registry \
  --namespace jitera \
  --docker-server=registry.jitera.com \
  --docker-username="your-username" \
  --docker-password="your-password"

Step 3: Deploy MinIO (Object Storage)

Jitera requires object storage for file uploads, generated code artifacts, and project exports. For on-premises deployments, MinIO provides S3-compatible storage. See Object Storage Configuration for bucket requirements and CORS details, and the MinIO CORS documentation for CORS configuration instructions.

In-cluster MinIO

Enable the built-in MinIO deployment:
minio:
  enabled: true
  mode: distributed  # or standalone
  replicas: 4        # for distributed mode
  persistence:
    enabled: true
    size: 10Gi
  resources:
    requests:
      memory: "512Mi"
      cpu: "250m"
    limits:
      memory: "2Gi"
      cpu: "1000m"

storage:
  provider: Minio
  secret:
    minio:
      AWS_ACCESS_KEY_ID: "<MINIO_ACCESS_KEY>"
      AWS_SECRET_ACCESS_KEY: "<MINIO_SECRET_KEY>"
      AWS_REGION: "ap-northeast-1"
      AWS_BUCKET: "jitera-default"
      AWS_PUBLIC_BUCKET: "jitera-public"
      AWS_EXPORT_PROJECT_BUCKET: "jitera-export"
      AWS_ULTRON_BUCKET: "jitera-ultron"
      S3_FORCE_PATH_STYLE: "true"

ingress:
  minio:
    enabled: true
    domainName: minio.example.com
    console:
      enabled: true
      domainName: minio-console.example.com
Generate credentials:
# Generate access key
pwgen 20 1

# Generate secret key
pwgen 40 1

External MinIO

For an existing MinIO deployment:
minio:
  enabled: false

storage:
  provider: Minio
  secret:
    minio:
      AWS_ACCESS_KEY_ID: "<MINIO_ACCESS_KEY>"
      AWS_SECRET_ACCESS_KEY: "<MINIO_SECRET_KEY>"
      AWS_REGION: "ap-northeast-1"
      AWS_BUCKET: "jitera-default"
      AWS_PUBLIC_BUCKET: "jitera-public"
      AWS_EXPORT_PROJECT_BUCKET: "jitera-export"
      AWS_ULTRON_BUCKET: "jitera-ultron"
      S3_FORCE_PATH_STYLE: "true"

# Configure the external MinIO endpoint
ultron:
  env:
    S3_ENDPOINT: "https://minio.external.com"
When using MinIO, S3_FORCE_PATH_STYLE must be set to "true" for compatibility.
When using the built-in MinIO deployment, the Helm chart creates the required buckets and credentials automatically. For external MinIO, the access key and secret key must have read/write permissions on all 4 buckets — create a policy with s3:GetObject, s3:PutObject, s3:DeleteObject, s3:ListBucket permissions and attach it to the access key. See MinIO documentation for detailed installation options.

Step 4: Configure Email

For on-premises deployments, you can use any external SMTP provider (e.g., SendGrid — see SMTP Service) or the built-in Postfix mail server.

External SMTP

mailer:
  smtp_settings:
    address: smtp.yourdomain.com      # your SMTP server address
    user_name: "<SMTP_USERNAME>"
    password: "<SMTP_PASSWORD>"
  default_from_email: "noreply@yourdomain.com"

Built-in Mail Server (Postfix)

For air-gapped environments or testing, Jitera includes a built-in Postfix mail server.
The built-in mail server is intended for testing and air-gapped deployments. For production, use a managed email service for better deliverability.
smtp:
  enabled: true
  replicaCount: 1
  config:
    hostname: mail.yourdomain.com
    allowedNetworks: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
    messageSizeLimit: "52428800"
  auth:
    enabled: false
  resources:
    requests:
      memory: "256Mi"
      cpu: "100m"
    limits:
      memory: "512Mi"
      cpu: "500m"

mailer:
  smtp_settings:
    address: jitera-smtp.jitera.svc.cluster.local
    user_name: ""
    password: ""
  default_from_email: "noreply@yourdomain.com"
To enable SMTP authentication on the built-in server:
smtp:
  enabled: true
  auth:
    enabled: true
    users:
      - username: jitera
        password: "<SMTP_PASSWORD>"

mailer:
  smtp_settings:
    address: jitera-smtp.jitera.svc.cluster.local
    user_name: jitera
    password: "<SMTP_PASSWORD>"
  default_from_email: "noreply@yourdomain.com"

Step 5: Create Values File

# values-onprem.yaml
global:
  domain: jitera.yourdomain.com

  imageRegistry: registry.jitera.com
  imagePullSecrets:
    - name: jitera-registry

# Secrets
secrets:
  jiteraRegistry:
    username: "your-registry-username"
    password: "your-registry-password"

  github:
    clientId: "your-github-client-id"
    clientSecret: "your-github-client-secret"

  openai:
    apiKey: "your-openai-api-key"


# Storage - MinIO (S3-compatible)
storage:
  type: s3
  s3:
    endpoint: "http://minio.minio:9000"
    bucket: jitera
    accessKeyId: "minioadmin"
    secretAccessKey: "minioadmin123"
    region: "ap-northeast-1"
    forcePathStyle: true

# Email - Generic SMTP
email:
  smtp:
    host: smtp.yourdomain.com
    port: 587
    username: "smtp-username"
    password: "smtp-password"
    fromAddress: "noreply@yourdomain.com"
    fromName: "Jitera"
    tls: true

# Ingress - NGINX with cert-manager
ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/proxy-body-size: "100m"
  tls:
    enabled: true
    secretName: jitera-tls

# For self-signed or existing certificate (no cert-manager):
# ingress:
#   enabled: true
#   className: nginx
#   annotations:
#     nginx.ingress.kubernetes.io/proxy-body-size: "100m"
#   tls:
#     enabled: true
#     secretName: jitera-tls  # Pre-created secret

# Database - In-cluster
postgresql:
  enabled: true
  primary:
    persistence:
      enabled: true
      size: 100Gi
      storageClass: ""  # Uses default storage class

mongodb:
  enabled: true
  persistence:
    enabled: true
    size: 100Gi
    storageClass: ""

redis:
  enabled: true
  master:
    persistence:
      enabled: true
      size: 20Gi
      storageClass: ""

# Resource allocation
api:
  replicas: 2
  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2"
      memory: "4Gi"

web:
  replicas: 2
  resources:
    requests:
      cpu: "200m"
      memory: "512Mi"
    limits:
      cpu: "1"
      memory: "2Gi"

worker:
  replicas: 2
  resources:
    requests:
      cpu: "500m"
      memory: "1Gi"
    limits:
      cpu: "2"
      memory: "4Gi"

# Node affinity (optional)
nodeSelector: {}

# Tolerations (optional)
tolerations: []

Step 6: Install Jitera

# Extract the Jitera Helm chart zip
unzip jitera-helm-chart.zip

# Install Jitera
helm install jitera ./charts/jitera \
  --namespace jitera \
  --values values-onprem.yaml \
  --wait \
  --timeout 15m

Air-Gapped Deployment

For environments without internet access, follow these additional steps.

Step 1: Mirror Container Images

On a machine with internet access:
# List of images to mirror
IMAGES=(
  "registry.jitera.com/jitera/api:latest"
  "registry.jitera.com/jitera/web:latest"
  "registry.jitera.com/jitera/worker:latest"
  "registry.jitera.com/jitera/automation:latest"
  "postgres:15"
  "mongo:6"
  "redis:7"
  "minio/minio:latest"
)

# Pull and save images
for img in "${IMAGES[@]}"; do
  docker pull $img
done

docker save ${IMAGES[@]} -o jitera-images.tar

Step 2: Load Images to Private Registry

# Load images
docker load -i jitera-images.tar

# Tag and push to private registry
PRIVATE_REGISTRY="registry.internal.local"

for img in "${IMAGES[@]}"; do
  new_tag="${PRIVATE_REGISTRY}/${img#*/}"
  docker tag $img $new_tag
  docker push $new_tag
done

Step 3: Update Values for Air-Gapped

# values-airgapped.yaml
global:
  imageRegistry: registry.internal.local

  # If private registry requires auth
  imagePullSecrets:
    - name: private-registry

# Override image repositories
postgresql:
  image:
    registry: registry.internal.local
    repository: library/postgres
    tag: "15"

mongodb:
  image:
    registry: registry.internal.local
    repository: library/mongo
    tag: "6"

redis:
  image:
    registry: registry.internal.local
    repository: library/redis
    tag: "7"

Step 4: Install Helm Charts Offline

# Transfer the Jitera Helm chart zip to the air-gapped environment
# Extract and install from local chart
unzip jitera-helm-chart.zip

helm install jitera ./charts/jitera \
  --namespace jitera \
  --values values-airgapped.yaml

Post-Installation

Step 1: Verify Pods

kubectl get pods -n jitera

Step 2: Configure DNS

Create DNS records pointing both hostnames (main domain and chat domain) to the load balancer IP. See Deployment Requirements for hostname details.
app.example.com     A  <load-balancer-ip>
chat.example.com    A  <load-balancer-ip>

Step 3: Verify Application

# Test health endpoint
curl -k https://jitera.yourdomain.com/api/health

# Access the application
# Navigate to https://jitera.yourdomain.com

Troubleshooting

Storage Provisioner Issues

# Check PVC status
kubectl get pvc -n jitera

# If Pending, check storage class
kubectl describe pvc <pvc-name> -n jitera

# Check storage provisioner logs
kubectl logs -n longhorn-system -l app=longhorn-manager

MinIO Object Storage Issues

# Check which storage provider is configured
kubectl get configmap jitera-ultron -n jitera -o jsonpath='{.data.STORAGE_DISK}'

# Check MinIO pod status
kubectl get pods -n jitera -l app=minio

# Check MinIO logs
kubectl logs -n jitera -l app=minio --tail=100

# Common issues:
# - Insufficient storage (check PVC)
# - Insufficient replicas for distributed mode (minimum 4)

# Port forward MinIO console for verification
kubectl port-forward svc/jitera-minio-console 9001:9001 -n jitera
# Access at http://localhost:9001
# Login with the credentials configured in minio.rootUser / minio.rootPassword

# Check MinIO access via mc client
kubectl exec -it deploy/jitera-minio -n jitera -- \
  mc ls local/jitera-default/

# Check storage config in the Automation secret (secrets.yml)
kubectl get secret jitera-automation -n jitera \
  -o jsonpath='{.data.secrets\.yml}' | base64 -d | grep -A 5 'storage_service\|aws:'

# Test MinIO connectivity from a pod
kubectl exec -it deploy/jitera-automation-rails -n jitera -- \
  curl -I http://jitera-minio:9000/minio/health/live

Load Balancer Issues

# Check MetalLB speaker logs
kubectl logs -n metallb-system -l component=speaker

# Verify IP pool configuration
kubectl get ipaddresspool -n metallb-system

# Check service external IP
kubectl get svc -n ingress-nginx

Certificate Issues

# Check certificate status
kubectl get certificate -n jitera

# Check cert-manager logs
kubectl logs -n cert-manager -l app=cert-manager

# For self-signed certificate issues
openssl s_client -connect jitera.yourdomain.com:443 -servername jitera.yourdomain.com

Network Connectivity

# Test internal DNS
kubectl run -it --rm debug --image=busybox --restart=Never -- nslookup jitera-api.jitera.svc.cluster.local

# Test external connectivity
kubectl run -it --rm debug --image=curlimages/curl --restart=Never -- curl -I https://api.github.com

Database Connection

# Test PostgreSQL
kubectl run -it --rm psql --image=postgres:15 --restart=Never -- \
  psql -h jitera-postgresql -U jitera -d jitera

# Test MongoDB
kubectl run -it --rm mongo --image=mongo:6 --restart=Never -- \
  mongosh "mongodb://jitera-mongodb:27017/jitera"

# Test Redis
kubectl run -it --rm redis --image=redis:7 --restart=Never -- \
  redis-cli -h jitera-redis-master ping

Email Testing

# Access Rails console
kubectl exec -it deploy/jitera-automation-rails -n jitera -- rails console

# Send test email
ActionMailer::Base.mail(
  from: 'noreply@yourdomain.com',
  to: 'test@example.com',
  subject: 'Test Email',
  body: 'This is a test email from Jitera.'
).deliver_now
# Check environment variables
kubectl exec -it deploy/jitera-automation-rails -n jitera -- \
  env | grep -i smtp

# Check automation logs for email errors
kubectl logs deploy/jitera-automation-rails -n jitera | grep -i mail

# Check built-in SMTP logs
kubectl logs deploy/jitera-smtp -n jitera

Email Connection Refused

# Verify SMTP server is reachable
kubectl exec -it deploy/jitera-automation-rails -n jitera -- \
  telnet smtp.example.com 587

# Ensure outbound port 587 (or 465, 25) is allowed by your firewall

Email Authentication Failed

  1. Verify credentials are correct
  2. Check if the SMTP username format is correct (e.g., apikey for SendGrid)
# Check secret values
kubectl get secret jitera-mailer -n jitera -o yaml

Emails Not Delivered

  1. Check spam/junk folder
  2. Verify domain SPF/DKIM records
  3. Check if the domain is verified with the email provider
  4. Review email provider logs/dashboard for bounces

Built-in SMTP Not Sending

# Check Postfix status
kubectl exec -it deploy/jitera-smtp -n jitera -- postfix status

# Check mail queue
kubectl exec -it deploy/jitera-smtp -n jitera -- mailq

# Force queue flush
kubectl exec -it deploy/jitera-smtp -n jitera -- postfix flush

Requirements

Mandatory and optional deployment requirements

Architecture

Understanding the architecture

Troubleshooting

Common issues and solutions