Version: 1.0 Last Updated: 2025-11-02 Target Audience: Operations engineers, DevOps teams
This guide covers upgrading your MrWhoOidc deployment to newer versions safely with minimal downtime.
Complete this checklist BEFORE starting any upgrade:
.env file and docker-compose.yml to safe locationCreate a full database backup before every upgrade:
# Create backup directory
mkdir -p backups
# Set backup filename with timestamp
BACKUP_FILE="backups/mrwhooidc-backup-$(date +%Y%m%d-%H%M%S).sql.gz"
# Create compressed backup
docker compose exec -T postgres pg_dump -U oidc authdb | gzip > "$BACKUP_FILE"
# Verify backup created
ls -lh "$BACKUP_FILE"
# Should show file size (e.g., 5.2M)
# Test backup integrity (optional but recommended)
gunzip -t "$BACKUP_FILE"
echo $?
# Should output: 0 (success)
Backup Script (backups/backup-db.sh):
#!/bin/bash
set -e
BACKUP_DIR="./backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="$BACKUP_DIR/mrwhooidc-backup-$TIMESTAMP.sql.gz"
echo "Creating backup: $BACKUP_FILE"
# Create backup
docker compose exec -T postgres pg_dump -U oidc authdb | gzip > "$BACKUP_FILE"
# Verify
if [ -f "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "✓ Backup complete: $BACKUP_FILE ($SIZE)"
else
echo "✗ Backup failed!"
exit 1
fi
Make executable: chmod +x backups/backup-db.sh
# Backup configuration files
cp .env backups/.env.backup-$(date +%Y%m%d-%H%M%S)
cp docker-compose.yml backups/docker-compose.yml.backup-$(date +%Y%m%d-%H%M%S)
# Verify
ls -lh backups/*.backup-*
For critical deployments, backup entire Docker volumes:
# Stop services (optional - for consistent backup)
docker compose stop webauth
# Backup PostgreSQL volume
docker run --rm \
-v mrwhooidc_postgres-data:/source:ro \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/postgres-volume-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .
# Backup Redis volume (if using Redis)
docker run --rm \
-v mrwhooidc_redis-data:/source:ro \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/redis-volume-$(date +%Y%m%d-%H%M%S).tar.gz -C /source .
# Restart services
docker compose start webauth
Follow these steps for zero-downtime or minimal-downtime upgrades:
# Create database backup
./backups/backup-db.sh
# Backup configuration
cp .env backups/.env.backup-$(date +%Y%m%d-%H%M%S)
Edit docker-compose.yml to specify new version:
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:v1.2.0 # Update from v1.1.0
# ... rest of configuration
Alternative: Use .env file for version management:
# Add to .env
MRWHOOIDC_VERSION=v1.2.0
Then in docker-compose.yml:
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:${MRWHOOIDC_VERSION:-latest}
# Pull new image
docker compose pull webauth
# Verify new image downloaded
docker images | grep mrwhooidc
# Should show new version tag
# Stop webauth service (keeps postgres/redis running)
docker compose stop webauth
# Or stop all services for major upgrades
docker compose down
# NOTE: 'down' stops all services but preserves volumes
# Start services with new image
docker compose up -d
# For major upgrades, use --force-recreate
docker compose up -d --force-recreate webauth
# Watch logs for successful startup
docker compose logs -f webauth
# Look for:
# - "Applying database migration X" (if migrations present)
# - "Application started"
# - No error messages
# Press Ctrl+C to exit log view
See Verification Steps section below.
For non-critical environments:
# Backup, pull, and upgrade in one command
./backups/backup-db.sh && docker compose pull && docker compose up -d --force-recreate
For critical deployments behind a load balancer:
Note: Requires shared PostgreSQL and Redis between instances.
MrWhoOidc automatically applies database migrations on startup.
# During upgrade, logs will show:
# 2025-11-02 10:00:00 [INF] Checking for pending migrations...
# 2025-11-02 10:00:01 [INF] Applying migration: 20251102_AddClientMetadata
# 2025-11-02 10:00:02 [INF] Migration applied successfully
# 2025-11-02 10:00:03 [INF] Database schema up to date
# 2025-11-02 10:00:04 [INF] Application starting...
Backward-Compatible Changes (safe during rolling updates):
Non-Backward-Compatible Changes (require downtime):
Release notes will indicate if downtime is required.
If migration fails:
docker compose logs webauth | grep -i migrationChoose your update strategy based on risk tolerance:
# docker-compose.yml
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:v1.2.3
Pros:
Cons:
Use Case: Production deployments, compliance requirements
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:v1.2
Pros:
Cons:
Use Case: Staging, pre-production environments
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:v1
Pros:
Cons:
Use Case: Development, testing
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:latest
Pros:
Cons:
Use Case: Local development only
Use environment variable for flexibility:
# docker-compose.yml
services:
webauth:
image: ghcr.io/popicka70/mrwhooidc:${MRWHOOIDC_VERSION:-v1.2.3}
# .env
MRWHOOIDC_VERSION=v1.2.3
Benefits:
.env (not committed to git)If upgrade fails or introduces issues, rollback to previous version:
Assumes database backup exists and database schema is backward-compatible.
# Step 1: Stop current services
docker compose stop webauth
# Step 2: Restore previous image version
# Edit docker-compose.yml or .env to previous version
# Example: v1.2.0 (previous) instead of v1.2.1 (failed)
# Step 3: Pull previous image (if not cached)
docker compose pull webauth
# Step 4: Start with previous version
docker compose up -d webauth
# Step 5: Verify
curl -k https://localhost:8443/t/default/.well-known/openid-configuration
Use if database migration is not backward-compatible:
# Step 1: Stop all services
docker compose down
# Step 2: Restore database backup
# List available backups
ls -lh backups/*.sql.gz
# Restore specific backup
gunzip < backups/mrwhooidc-backup-YYYYMMDD-HHMMSS.sql.gz | \
docker compose run --rm -T postgres psql -h postgres -U oidc authdb
# Or if services are running:
gunzip < backups/mrwhooidc-backup-YYYYMMDD-HHMMSS.sql.gz | \
docker compose exec -T postgres psql -U oidc authdb
# Step 3: Restore previous image version
# Edit docker-compose.yml to previous version
# Step 4: Start services
docker compose up -d
# Step 5: Verify
curl -k https://localhost:8443/t/default/.well-known/openid-configuration
If database restore fails or complete rollback needed:
# Step 1: Stop and remove everything (DESTRUCTIVE)
docker compose down -v
# WARNING: -v removes volumes (data loss)
# Step 2: Restore volume backups (if available)
# Restore postgres volume
docker run --rm \
-v mrwhooidc_postgres-data:/target \
-v $(pwd)/backups:/backup \
alpine sh -c "cd /target && tar xzf /backup/postgres-volume-YYYYMMDD-HHMMSS.tar.gz"
# Step 3: Restore configuration
cp backups/.env.backup-YYYYMMDD-HHMMSS .env
cp backups/docker-compose.yml.backup-YYYYMMDD-HHMMSS docker-compose.yml
# Step 4: Start services
docker compose up -d
After upgrade, verify system health:
# All services should be "Up (healthy)"
docker compose ps
# Expected output:
# NAME STATUS
# webauth Up (healthy)
# postgres Up (healthy)
# redis Up (healthy)
# View recent logs
docker compose logs --tail=50 webauth
# Look for:
# - "Application started"
# - "Listening on https://[::]:8443"
# - No error messages
# - Migration success messages (if applicable)
# Test OIDC discovery endpoint
curl -k https://localhost:8443/t/default/.well-known/openid-configuration
# Should return JSON with OIDC metadata
# Check "issuer" field matches your OIDC_PUBLIC_BASE_URL
# Test JSON Web Key Set endpoint
curl -k https://localhost:8443/jwks
# Should return JSON with signing keys
# Access admin interface
# https://localhost:8443/admin/clients
# Verify:
# - Login page loads
# - Can authenticate
# - Client list loads
# - No JavaScript errors in browser console
# Check database connection
docker compose exec postgres psql -U oidc authdb -c "SELECT COUNT(*) FROM clients;"
# Should return count of clients (not an error)
# Test Redis connection
docker compose exec redis redis-cli ping
# Expected: PONG
# Check webauth connected to Redis
docker compose logs webauth | grep -i redis
# Should show: "Redis connection established"
Perform a basic authentication test:
# Monitor resource usage
docker stats --no-stream
# Verify:
# - Memory usage within expected limits
# - CPU not pegged at 100%
# Verify deployed version matches target
docker compose images webauth
# Or check application logs for version info
docker compose logs webauth | grep -i version
Symptoms:
docker compose ps shows webauth as “Exited” or “Restarting”Diagnosis:
# Check logs for errors
docker compose logs webauth
# Look for:
# - Migration errors
# - Configuration errors
# - Missing environment variables
# - Database connection failures
Solutions:
.env file for missing/incorrect valuesdocker compose ps postgresSymptoms:
Diagnosis:
# Check if database locked
docker compose exec postgres psql -U oidc authdb -c "SELECT * FROM pg_locks WHERE NOT granted;"
# Check long-running queries
docker compose exec postgres psql -U oidc authdb -c "SELECT pid, now() - pg_stat_activity.query_start AS duration, query FROM pg_stat_activity WHERE state = 'active';"
Solutions:
docker compose restart postgresSymptoms:
Diagnosis:
# Test discovery endpoint with verbose output
curl -v -k https://localhost:8443/t/default/.well-known/openid-configuration
# Check for:
# - HTTP 500 errors
# - Connection refused
# - Timeout
Solutions:
OIDC_PUBLIC_BASE_URL correctdocker compose logs webauth | grep -i exceptionSymptoms:
Diagnosis:
# Check resource usage
docker stats
# Check Redis connection (if enabled)
docker compose exec redis redis-cli INFO stats
# Check database connections
docker compose exec postgres psql -U oidc authdb -c "SELECT count(*) FROM pg_stat_activity;"
Solutions:
REDIS_ENABLED=true and Redis healthydocker compose restart webauthSymptoms:
Solution:
CRITICAL: Rollback immediately and restore database backup.
# Full rollback with database restore
docker compose down
gunzip < backups/mrwhooidc-backup-YYYYMMDD-HHMMSS.sql.gz | \
docker compose run --rm -T postgres psql -h postgres -U oidc authdb
# Restore previous image version and restart
Use this checklist to test upgrades in staging before production:
Establish a backup retention strategy based on your requirements:
Development:
Staging:
Production:
Add to backups/backup-db.sh:
#!/bin/bash
set -e
BACKUP_DIR="./backups"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_FILE="$BACKUP_DIR/mrwhooidc-backup-$TIMESTAMP.sql.gz"
echo "Creating backup: $BACKUP_FILE"
docker compose exec -T postgres pg_dump -U oidc authdb | gzip > "$BACKUP_FILE"
if [ -f "$BACKUP_FILE" ]; then
SIZE=$(du -h "$BACKUP_FILE" | cut -f1)
echo "✓ Backup complete: $BACKUP_FILE ($SIZE)"
# Retention: Delete backups older than 30 days
find "$BACKUP_DIR" -name "mrwhooidc-backup-*.sql.gz" -mtime +30 -delete
echo "✓ Old backups cleaned (>30 days)"
else
echo "✗ Backup failed!"
exit 1
fi
For production deployments, store backups offsite:
AWS S3:
# Upload to S3 after backup
aws s3 cp "$BACKUP_FILE" s3://your-bucket/mrwhooidc-backups/
Azure Blob Storage:
# Upload to Azure
az storage blob upload \
--account-name youraccount \
--container-name backups \
--file "$BACKUP_FILE" \
--name "mrwhooidc/$(basename $BACKUP_FILE)"
Google Cloud Storage:
# Upload to GCS
gsutil cp "$BACKUP_FILE" gs://your-bucket/mrwhooidc-backups/
Document Version: 1.0 Last Updated: 2025-11-02 Maintained By: MrWhoOidc Project