# Backup Service Deployment Guide ## Overview This guide covers deploying the backup-service to various environments. The service is designed to run in Docker containers with PostgreSQL as the database. **Critical Security Requirement:** The backup-service MUST be deployed on a physically separate server from identity-service to maintain MPC security. --- ## Deployment Architecture ``` ┌─────────────────────────────────────────────────────────────────────────┐ │ Production Architecture │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ Server A (Identity) Server B (Backup) │ │ ┌─────────────────────┐ ┌─────────────────────┐ │ │ │ identity-service │ │ backup-service │ │ │ │ ┌───────────────┐ │ │ ┌───────────────┐ │ │ │ │ │ PostgreSQL │ │ │ │ PostgreSQL │ │ │ │ │ │ (identity-db) │ │ │ │ (backup-db) │ │ │ │ │ └───────────────┘ │ │ └───────────────┘ │ │ │ └─────────────────────┘ └─────────────────────┘ │ │ │ ▲ │ │ │ Internal Network │ │ │ └───────────────────────────────┘ │ │ (Service-to-Service JWT) │ │ │ └─────────────────────────────────────────────────────────────────────────┘ ``` --- ## Docker Deployment ### Production Dockerfile ```dockerfile # Dockerfile # Multi-stage build for smaller image size # Stage 1: Build FROM node:20-alpine AS builder WORKDIR /app # Copy package files COPY package*.json ./ COPY prisma ./prisma/ # Install all dependencies (including devDependencies for build) RUN npm ci # Copy source code COPY . . # Generate Prisma client RUN npx prisma generate # Build application RUN npm run build # Stage 2: Production FROM node:20-alpine AS production WORKDIR /app # Create non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nestjs -u 1001 # Copy package files COPY package*.json ./ # Install production dependencies only RUN npm ci --only=production && npm cache clean --force # Copy built application COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules/.prisma ./node_modules/.prisma COPY --from=builder /app/prisma ./prisma # Change ownership to non-root user RUN chown -R nestjs:nodejs /app # Switch to non-root user USER nestjs # Expose port EXPOSE 3002 # Health check HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD wget --no-verbose --tries=1 --spider http://localhost:3002/health || exit 1 # Start application CMD ["node", "dist/main.js"] ``` ### Build and Push Image ```bash # Build image docker build -t rwa-durian/backup-service:latest . # Tag for registry docker tag rwa-durian/backup-service:latest registry.example.com/backup-service:v1.0.0 # Push to registry docker push registry.example.com/backup-service:v1.0.0 ``` --- ## Docker Compose Deployment ### Production Compose File ```yaml # docker-compose.prod.yml version: '3.8' services: backup-service: image: rwa-durian/backup-service:latest container_name: backup-service restart: unless-stopped ports: - "3002:3002" environment: - DATABASE_URL=postgresql://postgres:${DB_PASSWORD}@backup-db:5432/rwa_backup?schema=public - APP_PORT=3002 - APP_ENV=production - SERVICE_JWT_SECRET=${SERVICE_JWT_SECRET} - ALLOWED_SERVICES=${ALLOWED_SERVICES} - BACKUP_ENCRYPTION_KEY=${BACKUP_ENCRYPTION_KEY} - BACKUP_ENCRYPTION_KEY_ID=${BACKUP_ENCRYPTION_KEY_ID} - MAX_RETRIEVE_PER_DAY=3 - MAX_STORE_PER_MINUTE=10 - AUDIT_LOG_RETENTION_DAYS=365 depends_on: backup-db: condition: service_healthy networks: - backup-network logging: driver: "json-file" options: max-size: "10m" max-file: "3" deploy: resources: limits: cpus: '1' memory: 512M reservations: cpus: '0.5' memory: 256M backup-db: image: postgres:15-alpine container_name: backup-db restart: unless-stopped environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DB_PASSWORD} POSTGRES_DB: rwa_backup volumes: - backup-db-data:/var/lib/postgresql/data - ./init-db.sql:/docker-entrypoint-initdb.d/init.sql:ro ports: - "5433:5432" # Different port to avoid conflicts healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 networks: - backup-network logging: driver: "json-file" options: max-size: "10m" max-file: "3" volumes: backup-db-data: driver: local networks: backup-network: driver: bridge ``` ### Environment File ```bash # .env.production DB_PASSWORD=your-strong-database-password-here SERVICE_JWT_SECRET=your-super-secret-service-jwt-key-min-32-chars ALLOWED_SERVICES=identity-service,recovery-service BACKUP_ENCRYPTION_KEY=your-256-bit-encryption-key-in-hex-64-chars BACKUP_ENCRYPTION_KEY_ID=key-v1 ``` ### Deploy Commands ```bash # Pull latest image docker-compose -f docker-compose.prod.yml pull # Start services docker-compose -f docker-compose.prod.yml up -d # Run database migrations docker-compose -f docker-compose.prod.yml exec backup-service \ npx prisma migrate deploy # View logs docker-compose -f docker-compose.prod.yml logs -f backup-service # Stop services docker-compose -f docker-compose.prod.yml down ``` --- ## Kubernetes Deployment ### Namespace and ConfigMap ```yaml # kubernetes/namespace.yaml apiVersion: v1 kind: Namespace metadata: name: rwa-backup --- # kubernetes/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: backup-service-config namespace: rwa-backup data: APP_PORT: "3002" APP_ENV: "production" ALLOWED_SERVICES: "identity-service,recovery-service" MAX_RETRIEVE_PER_DAY: "3" MAX_STORE_PER_MINUTE: "10" AUDIT_LOG_RETENTION_DAYS: "365" ``` ### Secrets ```yaml # kubernetes/secrets.yaml apiVersion: v1 kind: Secret metadata: name: backup-service-secrets namespace: rwa-backup type: Opaque stringData: DATABASE_URL: "postgresql://postgres:password@backup-db:5432/rwa_backup?schema=public" SERVICE_JWT_SECRET: "your-super-secret-service-jwt-key-min-32-chars" BACKUP_ENCRYPTION_KEY: "your-256-bit-encryption-key-in-hex-64-chars" BACKUP_ENCRYPTION_KEY_ID: "key-v1" ``` ### Deployment ```yaml # kubernetes/deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: backup-service namespace: rwa-backup spec: replicas: 2 selector: matchLabels: app: backup-service template: metadata: labels: app: backup-service spec: securityContext: runAsNonRoot: true runAsUser: 1001 containers: - name: backup-service image: registry.example.com/backup-service:v1.0.0 ports: - containerPort: 3002 envFrom: - configMapRef: name: backup-service-config - secretRef: name: backup-service-secrets resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" livenessProbe: httpGet: path: /health/live port: 3002 initialDelaySeconds: 15 periodSeconds: 20 readinessProbe: httpGet: path: /health/ready port: 3002 initialDelaySeconds: 5 periodSeconds: 10 ``` ### Service ```yaml # kubernetes/service.yaml apiVersion: v1 kind: Service metadata: name: backup-service namespace: rwa-backup spec: selector: app: backup-service ports: - protocol: TCP port: 3002 targetPort: 3002 type: ClusterIP ``` ### PostgreSQL StatefulSet ```yaml # kubernetes/postgres.yaml apiVersion: apps/v1 kind: StatefulSet metadata: name: backup-db namespace: rwa-backup spec: serviceName: backup-db replicas: 1 selector: matchLabels: app: backup-db template: metadata: labels: app: backup-db spec: containers: - name: postgres image: postgres:15-alpine ports: - containerPort: 5432 env: - name: POSTGRES_DB value: rwa_backup - name: POSTGRES_USER value: postgres - name: POSTGRES_PASSWORD valueFrom: secretKeyRef: name: backup-db-secrets key: password volumeMounts: - name: postgres-data mountPath: /var/lib/postgresql/data resources: requests: memory: "256Mi" cpu: "250m" limits: memory: "512Mi" cpu: "500m" volumeClaimTemplates: - metadata: name: postgres-data spec: accessModes: ["ReadWriteOnce"] resources: requests: storage: 10Gi ``` ### Deploy to Kubernetes ```bash # Apply all manifests kubectl apply -f kubernetes/ # Check deployment status kubectl -n rwa-backup get pods # View logs kubectl -n rwa-backup logs -f deployment/backup-service # Run migrations kubectl -n rwa-backup exec -it deployment/backup-service -- \ npx prisma migrate deploy ``` --- ## Database Management ### Initial Setup ```bash # Run migrations on first deployment npx prisma migrate deploy # Or push schema directly (development only) npx prisma db push ``` ### Backup and Restore ```bash # Backup database docker-compose exec backup-db pg_dump -U postgres rwa_backup > backup.sql # Restore database docker-compose exec -T backup-db psql -U postgres rwa_backup < backup.sql ``` ### Migration in Production ```bash # Generate migration (development) npx prisma migrate dev --name add_new_field # Apply migration (production) npx prisma migrate deploy ``` --- ## Environment Variables Reference ### Required Variables | Variable | Description | Example | |----------|-------------|---------| | `DATABASE_URL` | PostgreSQL connection string | `postgresql://user:pass@host:5432/db` | | `SERVICE_JWT_SECRET` | JWT secret for service auth (min 32 chars) | Random 64+ char string | | `ALLOWED_SERVICES` | Comma-separated allowed services | `identity-service,recovery-service` | | `BACKUP_ENCRYPTION_KEY` | 256-bit key in hex (64 chars) | 64 hex characters | | `BACKUP_ENCRYPTION_KEY_ID` | Key identifier | `key-v1` | ### Optional Variables | Variable | Default | Description | |----------|---------|-------------| | `APP_PORT` | `3002` | Server port | | `APP_ENV` | `development` | Environment (development/production) | | `MAX_RETRIEVE_PER_DAY` | `3` | Max retrieves per user per day | | `MAX_STORE_PER_MINUTE` | `10` | Max stores per minute | | `AUDIT_LOG_RETENTION_DAYS` | `365` | Audit log retention period | --- ## Security Considerations ### Network Security 1. **Isolate backup-service network** - Use private subnets - Restrict access to identity-service only - Use VPN or VPC peering for cross-server communication 2. **Firewall rules** ```bash # Allow only identity-service IP iptables -A INPUT -p tcp --dport 3002 -s identity-service-ip -j ACCEPT iptables -A INPUT -p tcp --dport 3002 -j DROP ``` 3. **TLS/SSL** - Use reverse proxy (nginx/traefik) for TLS termination - Enable mutual TLS for service-to-service communication ### Secret Management 1. **Use secret management services** - AWS Secrets Manager - HashiCorp Vault - Kubernetes Secrets with encryption at rest 2. **Rotate secrets regularly** - Rotate encryption keys annually - Rotate JWT secrets quarterly - Use key versioning for encryption keys ### Database Security 1. **Use strong passwords** 2. **Enable SSL for database connections** 3. **Regular backups with encryption** 4. **Limit database user permissions** --- ## Monitoring and Logging ### Health Endpoints | Endpoint | Purpose | |----------|---------| | `GET /health` | Basic health check | | `GET /health/ready` | Readiness probe (includes DB check) | | `GET /health/live` | Liveness probe | ### Prometheus Metrics (Optional) ```yaml # Add to deployment - name: PROMETHEUS_ENABLED value: "true" - name: PROMETHEUS_PORT value: "9102" ``` ### Log Aggregation Configure log driver for centralized logging: ```yaml logging: driver: "fluentd" options: fluentd-address: "localhost:24224" tag: "backup-service" ``` --- ## Troubleshooting ### Common Issues #### Service won't start ```bash # Check logs docker-compose logs backup-service # Common causes: # 1. Database not ready # 2. Missing environment variables # 3. Invalid encryption key format ``` #### Database connection failed ```bash # Check database is running docker-compose ps backup-db # Check database logs docker-compose logs backup-db # Test connection docker-compose exec backup-service \ npx prisma db pull ``` #### Authentication errors ```bash # Verify JWT secret matches between services # Check ALLOWED_SERVICES includes calling service # Verify token format and expiration ``` ### Recovery Procedures #### Database Recovery ```bash # Stop service docker-compose stop backup-service # Restore from backup docker-compose exec -T backup-db psql -U postgres rwa_backup < backup.sql # Run migrations docker-compose exec backup-service npx prisma migrate deploy # Start service docker-compose start backup-service ``` #### Key Rotation 1. Add new key to encryption service 2. Re-encrypt existing data with new key 3. Update `BACKUP_ENCRYPTION_KEY_ID` 4. Remove old key after transition period --- ## Scaling ### Horizontal Scaling The service is stateless and can be horizontally scaled: ```yaml # Docker Compose scale docker-compose up -d --scale backup-service=3 # Kubernetes replicas kubectl -n rwa-backup scale deployment/backup-service --replicas=3 ``` ### Load Balancing Use a load balancer in front of multiple instances: ```nginx # nginx.conf upstream backup_service { least_conn; server backup-service-1:3002; server backup-service-2:3002; server backup-service-3:3002; } server { listen 443 ssl; server_name backup-api.example.com; location / { proxy_pass http://backup_service; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } } ``` ### Database Scaling For high availability: 1. Use managed PostgreSQL (AWS RDS, GCP Cloud SQL) 2. Configure read replicas for read scaling 3. Use connection pooling (PgBouncer) --- ## Maintenance ### Regular Tasks | Task | Frequency | Command | |------|-----------|---------| | Database backup | Daily | `pg_dump rwa_backup > backup.sql` | | Log rotation | Weekly | Automatic with log driver config | | Security updates | Monthly | Rebuild and redeploy image | | Audit log cleanup | Monthly | `DELETE FROM share_access_logs WHERE created_at < NOW() - INTERVAL '365 days'` | ### Update Procedure ```bash # 1. Build new image docker build -t rwa-durian/backup-service:v1.1.0 . # 2. Push to registry docker push registry.example.com/backup-service:v1.1.0 # 3. Update deployment docker-compose pull docker-compose up -d # 4. Run migrations if needed docker-compose exec backup-service npx prisma migrate deploy # 5. Verify health curl http://localhost:3002/health ```