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:
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
Requirement Minimum Recommended Nodes 3 5+ CPU per node 4 cores 8+ cores Memory per node 16 GB 32+ GB Storage per node 100 GB SSD 200+ GB NVMe
Network Requirements
Requirement Specification Pod CIDR /16 (65,536 IPs) Service CIDR /16 Load Balancer MetalLB or hardware Ingress NGINX or Traefik
Storage Requirements
Component Size Type PostgreSQL 100 GB SSD MongoDB 100 GB SSD Redis 20 GB SSD Object Storage 500 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
# 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
# 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.
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
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-nam e > -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
Verify credentials are correct
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
Check spam/junk folder
Verify domain SPF/DKIM records
Check if the domain is verified with the email provider
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