Tutorial F: Production-Ready Cesivi Setup¶
Overview¶
This tutorial guides you through setting up a production-grade Cesivi Server with: - Authentication: OAuth2/OIDC with Azure AD or Keycloak - Storage: SQLite or PostgreSQL for persistence - Search: Lucene for full-text search - Security: HTTPS, proper secrets management - Monitoring: Health checks, metrics, logging - Platform: Docker with orchestration (Kubernetes-ready)
Use Case: Production deployment for QA environments, staging, or development infrastructure shared by teams.
Time Required: ~45 minutes
Prerequisites¶
- Docker Desktop with Kubernetes enabled (or a K8s cluster)
- kubectl configured
- An OIDC provider (Azure AD, Keycloak, Okta, etc.)
- SSL certificate (or use Let's Encrypt)
Architecture¶
┌──────────────────┐
│ Load Balancer │
│ (HTTPS :443) │
└────────┬─────────┘
│
┌────────────────────────┼────────────────────────┐
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ Cesivi │ │ Cesivi │ │ Cesivi │
│ Replica 1 │ │ Replica 2 │ │ Replica 3 │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
└────────────────────────┼────────────────────────┘
│
┌────────┴────────┐
│ PostgreSQL │
│ (Persistent) │
└─────────────────┘
Step 1: Create Namespace and Secrets¶
# Create namespace
kubectl create namespace cesivi
# Create secrets for database
kubectl create secret generic db-credentials \
--namespace cesivi \
--from-literal=POSTGRES_USER=sharepoint \
--from-literal=POSTGRES_PASSWORD=$(openssl rand -base64 32)
# Create secret for OIDC
kubectl create secret generic oidc-config \
--namespace cesivi \
--from-literal=OIDC_CLIENT_ID=your-client-id \
--from-literal=OIDC_CLIENT_SECRET=your-client-secret
# Create TLS secret (if you have certificates)
kubectl create secret tls sharepoint-tls \
--namespace cesivi \
--cert=path/to/tls.crt \
--key=path/to/tls.key
Step 2: Create ConfigMap¶
Create configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: cesivi-config
namespace: cesivi
data:
appsettings.Production.json: |
{
"Logging": {
"LogLevel": {
"Default": "Warning",
"Cesivi": "Information"
},
"Console": {
"FormatterName": "json"
}
},
"Cesivi": {
"HostName": "cesivi.example.com",
"UseHttps": true,
"HttpPort": 8080,
"HttpsPort": 8443,
"StorageProvider": "PostgreSQL",
"PostgresConnectionString": "Host=postgres;Database=sharepoint;Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}",
"SearchEngine": "Lucene",
"LuceneIndexPath": "/data/lucene-index",
"Identity": {
"Providers": {
"OAuth2": {
"Enabled": true,
"Priority": 50,
"Authority": "https://login.microsoftonline.com/your-tenant-id/v2.0",
"Audience": "api://cesivi",
"ValidateIssuer": true,
"ValidateAudience": true,
"ValidateLifetime": true,
"ClockSkewMinutes": 5
},
"AcceptAll": {
"Enabled": false
}
}
},
"Authentication": {
"AcceptAllCredentials": false,
"AllowAnonymous": false,
"EnableNTLM": false,
"EnableJWT": true,
"EnableBasic": false
},
"Session": {
"IdleTimeoutMinutes": 30,
"MaxSessionCount": 5000,
"MemoryPressureThresholdMB": 1024
}
}
}
Apply:
kubectl apply -f configmap.yaml
Step 3: Deploy PostgreSQL¶
Create postgres.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgres-pvc
namespace: cesivi
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
namespace: cesivi
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
env:
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: POSTGRES_PASSWORD
- name: POSTGRES_DB
value: sharepoint
volumeMounts:
- name: postgres-storage
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: postgres-storage
persistentVolumeClaim:
claimName: postgres-pvc
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: cesivi
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
Apply:
kubectl apply -f postgres.yaml
Step 4: Deploy Cesivi¶
Create cesivi.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: cesivi
namespace: cesivi
spec:
replicas: 3
selector:
matchLabels:
app: cesivi
template:
metadata:
labels:
app: cesivi
spec:
containers:
- name: cesivi
image: ghcr.io/your-org/cesivi:latest
ports:
- containerPort: 8080
name: http
env:
- name: ASPNETCORE_ENVIRONMENT
value: Production
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: POSTGRES_PASSWORD
volumeMounts:
- name: config
mountPath: /app/appsettings.Production.json
subPath: appsettings.Production.json
- name: lucene-index
mountPath: /data/lucene-index
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /_vti_bin/health
port: http
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /_vti_bin/health
port: http
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: config
configMap:
name: cesivi-config
- name: lucene-index
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: cesivi
namespace: cesivi
spec:
selector:
app: cesivi
ports:
- port: 80
targetPort: 8080
name: http
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cesivi
namespace: cesivi
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- cesivi.example.com
secretName: sharepoint-tls
rules:
- host: cesivi.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cesivi
port:
number: 80
Apply:
kubectl apply -f cesivi.yaml
Step 5: Set Up Monitoring¶
Create monitoring.yaml:
apiVersion: v1
kind: ServiceMonitor
metadata:
name: cesivi
namespace: cesivi
labels:
release: prometheus
spec:
selector:
matchLabels:
app: cesivi
endpoints:
- port: http
path: /_metrics
interval: 30s
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: cesivi-alerts
namespace: cesivi
spec:
groups:
- name: cesivi
rules:
- alert: CesiviDown
expr: up{job="cesivi"} == 0
for: 5m
labels:
severity: critical
annotations:
summary: "Cesivi is down"
- alert: CesiviHighMemory
expr: container_memory_usage_bytes{container="cesivi"} > 900000000
for: 10m
labels:
severity: warning
annotations:
summary: "Cesivi memory usage is high"
Step 6: Configure Azure AD (Example)¶
Register Application in Azure AD¶
- Go to Azure Portal → Azure Active Directory → App registrations
- Click "New registration"
- Name: "Cesivi"
- Supported account types: "Single tenant" or "Multitenant"
- Redirect URI:
https://cesivi.example.com/signin-oidc
Configure API Permissions¶
- Add permission: Microsoft Graph → User.Read
- Grant admin consent
Create Client Secret¶
- Go to "Certificates & secrets"
- Create new client secret
- Copy the value (you won't see it again)
Update Configuration¶
# Update the secret
kubectl create secret generic oidc-config \
--namespace cesivi \
--from-literal=OIDC_CLIENT_ID=<application-id> \
--from-literal=OIDC_CLIENT_SECRET=<client-secret> \
--dry-run=client -o yaml | kubectl apply -f -
Step 7: Verify Deployment¶
# Check pods
kubectl get pods -n cesivi
# Check services
kubectl get svc -n cesivi
# Check ingress
kubectl get ingress -n cesivi
# View logs
kubectl logs -l app=cesivi -n cesivi
# Test health endpoint
curl https://cesivi.example.com/_vti_bin/health
Step 8: Test with OAuth2 Token¶
# Get token from Azure AD (example using MSAL)
TOKEN=$(az account get-access-token \
--resource api://cesivi \
--query accessToken -o tsv)
# Test API
curl https://cesivi.example.com/_api/web \
-H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json;odata=verbose"
Security Checklist¶
- [ ] HTTPS enabled with valid certificate
- [ ] OAuth2/OIDC configured with proper issuer validation
- [ ] AcceptAll provider disabled
- [ ] Anonymous access disabled
- [ ] Database credentials in Kubernetes secrets
- [ ] Resource limits configured
- [ ] Network policies applied
- [ ] Pod security policies enabled
- [ ] Audit logging enabled
- [ ] Backup strategy for PostgreSQL
Scaling¶
# Scale horizontally
kubectl scale deployment cesivi -n cesivi --replicas=5
# Configure HPA
kubectl autoscale deployment cesivi \
-n cesivi \
--min=3 --max=10 \
--cpu-percent=70
Backup and Recovery¶
# Backup PostgreSQL
kubectl exec -n cesivi postgres-xxx -- \
pg_dump -U sharepoint sharepoint > backup.sql
# Restore
kubectl exec -i -n cesivi postgres-xxx -- \
psql -U sharepoint sharepoint < backup.sql
Summary¶
You have deployed a production-grade Cesivi Server with: - High Availability: 3 replicas with load balancing - Secure Auth: OAuth2/OIDC with Azure AD - Persistent Storage: PostgreSQL database - Full-Text Search: Lucene engine - Monitoring: Prometheus metrics and alerts - TLS: HTTPS with valid certificates