Upgrades
Upgrades
Section titled “Upgrades”Upgrade Mantis components safely with zero-downtime strategies.
Upgrade Planning
Section titled “Upgrade Planning”Version Compatibility
Section titled “Version Compatibility”Before upgrading, verify compatibility:
| Component | Compatibility |
|---|---|
| Mandible ↔ Thorax | Must match major.minor version |
| Thorax ↔ Tarsus | Backward compatible within major version |
| Database schema | Managed by migrations |
Pre-Upgrade Checklist
Section titled “Pre-Upgrade Checklist”- Review release notes for breaking changes
- Backup database
- Backup configuration files
- Test upgrade in staging environment
- Schedule maintenance window (if needed)
- Notify stakeholders
- Verify rollback plan
Upgrade Order
Section titled “Upgrade Order”Always upgrade in this order:
Docker Compose Upgrades
Section titled “Docker Compose Upgrades”Standard Upgrade
Section titled “Standard Upgrade”# Images are built locally -- there is no registry to pull from./docker.sh build # or: docker compose build
# Stop the app tiers but KEEP Postgres running -- migrations connect to itdocker compose stop mandible thorax lens
# Run migrations against the live databasecd instinctdiesel migration runcd ..
# Start with the newly built imagesdocker compose up -d
# Verify healthdocker compose pscurl -k https://localhost:3000/api/v1/healthRolling Upgrade (Zero Downtime)
Section titled “Rolling Upgrade (Zero Downtime)”Rollback
Section titled “Rollback”# Rebuild at the previous tag and restartexport MANTIS_IMAGE_TAG=1.2.3docker compose up -d --buildKubernetes Upgrades
Section titled “Kubernetes Upgrades”Standard Rolling Update
Section titled “Standard Rolling Update”# <registry> is your own; see the caution above.kubectl set image deployment/mandible mandible=<registry>/mantis-mandible:v1.3.0 -n mantiskubectl set image deployment/thorax thorax=<registry>/mantis-thorax:v1.3.0 -n mantiskubectl set image deployment/lens lens=<registry>/mantis-lens:v1.3.0 -n mantis
# Monitor rolloutkubectl rollout status deployment/mandible -n mantiskubectl rollout status deployment/thorax -n mantisBlue-Green Deployment
Section titled “Blue-Green Deployment”apiVersion: apps/v1kind: Deploymentmetadata: name: mandible-v2 namespace: mantisspec: replicas: 2 selector: matchLabels: app: mantis component: mandible version: v2 template: metadata: labels: app: mantis component: mandible version: v2 spec: containers: - name: mandible image: <registry>/mantis-mandible:v1.3.0 # ... rest of specSwitch traffic:
# Deploy new versionkubectl apply -f deployments/mandible-v2.yaml
# Wait for readykubectl rollout status deployment/mandible-v2 -n mantis
# Update service selector to new versionkubectl patch service mandible -n mantis -p '{"spec":{"selector":{"version":"v2"}}}'
# Verify traffic flows to new versionkubectl get endpoints mandible -n mantis
# Remove old deployment after validationkubectl delete deployment mandible-v1 -n mantisCanary Deployment
Section titled “Canary Deployment”# Deploy canary with subset of trafficapiVersion: apps/v1kind: Deploymentmetadata: name: mandible-canary namespace: mantisspec: replicas: 1 selector: matchLabels: app: mantis component: mandible track: canary template: metadata: labels: app: mantis component: mandible track: canary spec: containers: - name: mandible image: <registry>/mantis-mandible:v1.3.0Rollback
Section titled “Rollback”# Rollback to previous revisionkubectl rollout undo deployment/mandible -n mantis
# Or to specific revisionkubectl rollout undo deployment/mandible --to-revision=2 -n mantis
# Check rollback statuskubectl rollout status deployment/mandible -n mantisDatabase Migrations
Section titled “Database Migrations”Running Migrations
Section titled “Running Migrations”Migrations must run before deploying new application code:
# Using diesel CLI (recommended)cd instinctdiesel migration run
# Kubernetes - the Helm chart runs migrations itself. On an upgrade,# `<release>-migrate` is a pre-upgrade hook Job (mandible with --migrate-only) that# Helm waits on before the new pods roll; a first install has no Job, and mandible# migrates as it boots. The Job is deleted on success, so nothing is left to wait# on afterwards. Follow it while it runs with:kubectl logs -n mantis job/<release>-migrate -f
# Set mandible.migrations.enabled=false to run migrations yourself instead.Migration Best Practices
Section titled “Migration Best Practices”- Always backup before migrations
# As a superuser, not `mantis`: audit_log_entries forces row-level security on its# owner too, so a `mantis` dump silently omits every tenant-scoped audit row -- the# data this backup exists to protect.pg_dump -h localhost -U postgres mantis > backup-$(date +%Y%m%d).sqlpg_dumpall -h localhost -U postgres --globals-only > roles-$(date +%Y%m%d).sqlSee Database Backup for the full procedure.
- Run migrations in a transaction
Mantis migrations run in transactions by default. If a migration fails, changes are rolled back.
- Test migrations on a copy first
# Create test databasecreatedb mantis_upgrade_testpsql -d mantis_upgrade_test -f backup.sql # pg_restore only reads archive-format dumps
# Run migration against test databasecd instinctDATABASE_URL="postgres://localhost/mantis_upgrade_test" diesel migration runHandling Migration Failures
Section titled “Handling Migration Failures”If a migration fails:
- Check the error message in logs
- Do not retry without investigating
- Fix the underlying issue
- Migration state is tracked in
__diesel_schema_migrationstable
-- Check migration statusSELECT * FROM __diesel_schema_migrations ORDER BY run_on DESC;
-- If needed, manually mark migration as rolled back-- WARNING: Only do this if you understand the consequences-- The version is the migration directory's timestamp without separators, e.g.-- 2026-08-31-054756-0000_... is 202608310547560000; it must delete exactly one row.DELETE FROM __diesel_schema_migrations WHERE version = 'YYYYMMDDHHMMSSNNNN';Tarsus Agent Upgrades
Section titled “Tarsus Agent Upgrades”Upgrade Strategies
Section titled “Upgrade Strategies”| Strategy | Description | Downtime |
|---|---|---|
| Rolling | Upgrade agents one at a time | None |
| Batch | Upgrade groups by environment | Minimal |
| Full | Upgrade all agents simultaneously | Brief |
Rolling Upgrade Script
Section titled “Rolling Upgrade Script”#!/bin/bash# List target IDs via the REST API (there is no `mantisctl targets` subcommand)AGENTS=$(curl -s -H "Authorization: Bearer $TOKEN" \ "https://api.mantis.example.com/api/v1/targets" | jq -r '.data[].id')NEW_VERSION="1.3.0"
for agent in $AGENTS; do echo "Upgrading $agent..."
# Deploy upgrade package with mantisctl mantisctl deployment create \ --solution-id "$TARSUS_UPGRADE_SOLUTION_ID" \ --target "$agent" \ --var "version=$NEW_VERSION" \ --follow
echo "Completed $agent"doneManual Agent Upgrade
Section titled “Manual Agent Upgrade”On each target server:
# Stop the agent instance (templated unit -- one per agent)sudo systemctl stop tarsus@web-01
# Backup current binarysudo cp /usr/bin/tarsus /usr/bin/tarsus.backup
# Download new versioncurl -L https://releases.mantis.example.com/tarsus/v1.3.0/tarsus-linux-amd64 \ -o /usr/bin/tarsus
# Set permissionssudo chmod 755 /usr/bin/tarsus
# Start the agent instancesudo systemctl start tarsus@web-01
# Verifytarsus --versionConfiguration Changes
Section titled “Configuration Changes”Adding New Settings
Section titled “Adding New Settings”New configuration options typically have sensible defaults. For example, Thorax exposes a
[session] section and Mandible exposes a [client_session] section:
# Thorax session tuning[session]heartbeat_timeout_secs = 90
# Mandible client-session tuning[client_session]max_concurrent_commands = 10Deprecated Settings
Section titled “Deprecated Settings”Check the release notes for any renamed or removed settings before upgrading. The current
database pool keys are max_connections, min_idle, and connection_timeout_secs:
[database]max_connections = 10min_idle = 2connection_timeout_secs = 10Environment Variable Changes
Section titled “Environment Variable Changes”Update environment files:
# Check for new/changed variablesdiff .env.example .env
# Update environmentnano .envVersion-Specific Guides
Section titled “Version-Specific Guides”Breaking Changes Log
Section titled “Breaking Changes Log”| Version | Breaking Change | Migration Path |
|---|---|---|
| v1.0.0 | Initial release | N/A |
| v1.0.0 | The audit log’s entry hash is computed over microsecond timestamps, where it previously used milliseconds. | Every audit row, archive and dead-letter line written before the change hashes differently, so the chain over them cannot be recomputed. |
The audit chain does not survive the hash change
Section titled “The audit chain does not survive the hash change”On a database carrying pre-change audit rows:
GET /api/v1/audit/integrity/{tenant_id}reports the chain broken withfailure_kind=content_hash, and records that verdict inaudit_integrity_checks.- Retention wedges:
archive_and_drop_partitionrefuses any quarter holding pre-change rows withChainInvalid, so those partitions are never archived or dropped. - Archive verification reports every pre-change archive as
ChainBroken. - Startup replay skips pre-change dead-letter lines as corrupt.
Rows written after the upgrade chain normally. There is no rehash tool: the chain exists so that a rewritten row is detectable, and a tool that recomputed it would be one. Verify the integrity of a pre-change chain before upgrading, keep that verdict, and read any post-upgrade failure over pre-change rows against it. A QA or demonstration install that needs a verifiable chain end to end should be created fresh rather than upgraded.
Monitoring Upgrades
Section titled “Monitoring Upgrades”Health Checks During Upgrade
Section titled “Health Checks During Upgrade”# Continuous health monitoringwatch -n 2 'curl -ks https://localhost:3000/api/v1/health | jq'
# Check specific componentcurl -k https://localhost:3000/api/v1/health/readyKey Metrics to Watch
Section titled “Key Metrics to Watch”| Metric | Normal | Alert |
|---|---|---|
| Request latency | < 100ms | > 500ms |
| Error rate | < 0.1% | > 1% |
| Active connections | Stable | Spike/drop |
| Memory usage | Stable | Growing |
Log Analysis
Section titled “Log Analysis”# Watch for errors during upgradedocker compose logs -f --since 5m | grep -i error
# Kuberneteskubectl logs -l app=mantis -n mantis -f --since=5m | grep -i errorRollback Procedures
Section titled “Rollback Procedures”Docker Compose Rollback
Section titled “Docker Compose Rollback”# Immediate rollbackdocker compose downexport MANTIS_IMAGE_TAG=previous-versiondocker compose up -d
# Database rollback (if needed), as a superuser: the audit table's row security# and roles are not the application user's to restorepsql -U postgres mantis < backup.sqlKubernetes Rollback
Section titled “Kubernetes Rollback”# Rollback deploymentskubectl rollout undo deployment/mandible -n mantiskubectl rollout undo deployment/thorax -n mantiskubectl rollout undo deployment/lens -n mantis
# Database rollback (if needed)kubectl exec -it postgres-0 -n mantis -- psql -U postgres -d mantis -f /backup/backup.sqlDatabase Rollback
Section titled “Database Rollback”# Restore from backup, as a superuser (see Database Backup for why)pg_restore -h localhost -U postgres -d mantis --clean backup.dump
# Or from SQL dumppsql -h localhost -U postgres -d mantis < backup.sqlMaintenance Windows
Section titled “Maintenance Windows”Scheduling
Section titled “Scheduling”For upgrades requiring downtime:
- Announce maintenance - Notify users 24-48 hours in advance
- Freeze deployments - Prevent new deployments during upgrade
- Backup everything - Database, configs, certificates
- Perform upgrade - Follow upgrade procedures
- Validate - Run smoke tests
- Resume operations - Unfreeze and notify users
Creating Deployment Freeze
Section titled “Creating Deployment Freeze”# In Lens UI: Deployment → Freezes → New Freeze (path /freezes/new)# Or via API:curl -X POST https://api.mantis.example.com/api/v1/deployment-freezes \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "Upgrade Maintenance", "start_time": "2024-01-15T02:00:00Z", "end_time": "2024-01-15T04:00:00Z" }'# Omitting tenant_id/solution_id/environment_id applies the freeze globally.Troubleshooting
Section titled “Troubleshooting”Upgrade Failed
Section titled “Upgrade Failed”- Check logs for specific errors
- Verify all prerequisites met
- Ensure database migrations completed
- Check connectivity between components
Version Mismatch
Section titled “Version Mismatch”# Check tarsus version (supports --version via clap)tarsus --version
# Check mandible and thorax versions via the server info endpointcurl -ks https://localhost:3000/api/v1/server/info | jq '.server_version'
# Verify the API is healthy after the upgradecurl -k https://localhost:3000/api/v1/health | jq '.status'Database Migration Stuck
Section titled “Database Migration Stuck”# Check migration statuspsql -U mantis -c "SELECT * FROM __diesel_schema_migrations"
# Check for lockspsql -U mantis -c "SELECT * FROM pg_locks WHERE NOT granted"Next Steps
Section titled “Next Steps”- Production Checklist - Verify production readiness
- Backup & Recovery - Backup procedures
- Troubleshooting - Common issues
