# Gondulf Deployment Guide This guide covers deploying Gondulf IndieAuth Server using OCI-compliant containers with both **Podman** (recommended) and **Docker** (alternative). ## Table of Contents 1. [Quick Start](#quick-start) 2. [Container Engine Support](#container-engine-support) 3. [Prerequisites](#prerequisites) 4. [Building the Container Image](#building-the-container-image) 5. [Development Deployment](#development-deployment) 6. [Production Deployment](#production-deployment) 7. [Backup and Restore](#backup-and-restore) 8. [systemd Integration](#systemd-integration) 9. [Troubleshooting](#troubleshooting) 10. [Security Considerations](#security-considerations) ## Quick Start ### Podman (Rootless - Recommended) ```bash # 1. Clone and configure git clone https://github.com/yourusername/gondulf.git cd gondulf cp .env.example .env # Edit .env with your settings # 2. Build image podman build -t gondulf:latest . # 3. Run container podman run -d --name gondulf \ -p 8000:8000 \ -v gondulf_data:/data:Z \ --env-file .env \ gondulf:latest # 4. Verify health curl http://localhost:8000/health ``` ### Docker (Alternative) ```bash # 1. Clone and configure git clone https://github.com/yourusername/gondulf.git cd gondulf cp .env.example .env # Edit .env with your settings # 2. Build and run with compose docker-compose up -d # 3. Verify health curl http://localhost:8000/health ``` ## Container Engine Support Gondulf supports both Podman and Docker with identical functionality. ### Podman (Primary) **Advantages**: - Daemonless architecture (no background process) - Rootless mode for enhanced security - Native systemd integration - Pod support for multi-container applications - OCI-compliant **Recommended for**: Production deployments, security-focused environments ### Docker (Alternative) **Advantages**: - Wide ecosystem and tooling support - Familiar to most developers - Extensive documentation **Recommended for**: Development, existing Docker environments ### Compatibility Matrix | Feature | Podman | Docker | |---------|--------|--------| | Container build | ✅ | ✅ | | Container runtime | ✅ | ✅ | | Compose files | ✅ (podman-compose) | ✅ (docker-compose) | | Rootless mode | ✅ Native | ⚠️ Experimental | | systemd integration | ✅ Built-in | ⚠️ Manual | | Health checks | ✅ | ✅ | ## Prerequisites ### System Requirements - **Operating System**: Linux (recommended), macOS, Windows (WSL2) - **CPU**: 1 core minimum, 2+ cores recommended - **RAM**: 512 MB minimum, 1 GB+ recommended - **Disk**: 5 GB available space ### Container Engine Choose ONE: **Option 1: Podman** (Recommended) ```bash # Fedora/RHEL/CentOS sudo dnf install podman podman-compose # Ubuntu/Debian sudo apt install podman podman-compose # Verify installation podman --version podman-compose --version ``` **Option 2: Docker** ```bash # Ubuntu/Debian sudo apt install docker.io docker-compose # Or install from Docker's repository: # https://docs.docker.com/engine/install/ # Verify installation docker --version docker-compose --version ``` ### Rootless Podman Setup (Recommended) For enhanced security, configure rootless Podman: ```bash # 1. Check subuid/subgid configuration grep $USER /etc/subuid grep $USER /etc/subgid # Should show: username:100000:65536 (or similar) # If missing, run: sudo usermod --add-subuids 100000-165535 $USER sudo usermod --add-subgids 100000-165535 $USER # 2. Enable user lingering (services persist after logout) loginctl enable-linger $USER # 3. Verify rootless setup podman system info | grep rootless # Should show: runRoot: /run/user/1000/... ``` ## Building the Container Image ### Using Podman ```bash # Build image podman build -t gondulf:latest . # Verify build podman images | grep gondulf # Test run podman run --rm gondulf:latest python -m gondulf --version ``` ### Using Docker ```bash # Build image docker build -t gondulf:latest . # Verify build docker images | grep gondulf # Test run docker run --rm gondulf:latest python -m gondulf --version ``` ### Build Arguments The Dockerfile supports multi-stage builds that include testing: ```bash # Build with tests (default) podman build -t gondulf:latest . # If build fails, tests have failed - check build output ``` ## Development Deployment Development deployment includes: - Live code reload - MailHog for local email testing - Debug logging enabled - No TLS requirements ### Using Podman Compose ```bash # Start development environment podman-compose -f docker-compose.yml -f docker-compose.development.yml up # Access services: # - Gondulf: http://localhost:8000 # - MailHog UI: http://localhost:8025 # View logs podman-compose logs -f gondulf # Stop environment podman-compose down ``` ### Using Docker Compose ```bash # Start development environment docker-compose -f docker-compose.yml -f docker-compose.development.yml up # Access services: # - Gondulf: http://localhost:8000 # - MailHog UI: http://localhost:8025 # View logs docker-compose logs -f gondulf # Stop environment docker-compose down ``` ### Development Configuration Create `.env` file from `.env.example`: ```bash cp .env.example .env ``` Edit `.env` with development settings: ```env GONDULF_SECRET_KEY=dev-secret-key-minimum-32-characters GONDULF_BASE_URL=http://localhost:8000 GONDULF_DATABASE_URL=sqlite:///./data/gondulf.db GONDULF_SMTP_HOST=mailhog GONDULF_SMTP_PORT=1025 GONDULF_SMTP_USE_TLS=false GONDULF_DEBUG=true GONDULF_LOG_LEVEL=DEBUG ``` ## Production Deployment Production deployment includes: - nginx reverse proxy with TLS termination - Rate limiting and security headers - Persistent volume for database - Health checks and auto-restart - Proper logging configuration ### Step 1: Configuration ```bash # 1. Copy environment template cp .env.example .env # 2. Generate secret key python -c "import secrets; print(secrets.token_urlsafe(32))" # 3. Edit .env with your production settings nano .env ``` Production `.env` example: ```env GONDULF_SECRET_KEY= GONDULF_BASE_URL=https://auth.example.com GONDULF_DATABASE_URL=sqlite:////data/gondulf.db GONDULF_SMTP_HOST=smtp.sendgrid.net GONDULF_SMTP_PORT=587 GONDULF_SMTP_USERNAME=apikey GONDULF_SMTP_PASSWORD= GONDULF_SMTP_FROM=noreply@example.com GONDULF_SMTP_USE_TLS=true GONDULF_HTTPS_REDIRECT=true GONDULF_TRUST_PROXY=true GONDULF_SECURE_COOKIES=true GONDULF_DEBUG=false GONDULF_LOG_LEVEL=INFO ``` ### Step 2: TLS Certificates Obtain TLS certificates (Let's Encrypt recommended): ```bash # Create SSL directory mkdir -p deployment/nginx/ssl # Option 1: Let's Encrypt (recommended) sudo certbot certonly --standalone -d auth.example.com sudo cp /etc/letsencrypt/live/auth.example.com/fullchain.pem deployment/nginx/ssl/ sudo cp /etc/letsencrypt/live/auth.example.com/privkey.pem deployment/nginx/ssl/ # Option 2: Self-signed (development/testing only) openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ -keyout deployment/nginx/ssl/privkey.pem \ -out deployment/nginx/ssl/fullchain.pem # Secure permissions chmod 600 deployment/nginx/ssl/privkey.pem chmod 644 deployment/nginx/ssl/fullchain.pem ``` ### Step 3: nginx Configuration Edit `deployment/nginx/conf.d/gondulf.conf`: ```nginx # Change server_name to your domain server_name auth.example.com; # ← CHANGE THIS ``` ### Step 4: Deploy with Podman (Recommended) ```bash # Build image podman build -t gondulf:latest . # Start services podman-compose -f docker-compose.yml -f docker-compose.production.yml up -d # Verify health curl https://auth.example.com/health # View logs podman-compose logs -f ``` ### Step 5: Deploy with Docker (Alternative) ```bash # Build and start docker-compose -f docker-compose.yml -f docker-compose.production.yml up -d # Verify health curl https://auth.example.com/health # View logs docker-compose logs -f ``` ### Step 6: Verify Deployment ```bash # 1. Check health endpoint curl https://auth.example.com/health # Expected: {"status":"healthy","database":"connected"} # 2. Check OAuth metadata curl https://auth.example.com/.well-known/oauth-authorization-server | jq # Expected: JSON with issuer, authorization_endpoint, token_endpoint # 3. Verify HTTPS redirect curl -I http://auth.example.com/ # Expected: 301 redirect to HTTPS # 4. Check security headers curl -I https://auth.example.com/ | grep -E "(Strict-Transport|X-Frame|X-Content)" # Expected: HSTS, X-Frame-Options, X-Content-Type-Options headers # 5. Test TLS configuration # Visit: https://www.ssllabs.com/ssltest/analyze.html?d=auth.example.com # Target: Grade A or higher ``` ## Backup and Restore ### Automated Backups The backup scripts auto-detect Podman or Docker. #### Create Backup ```bash # Using included script (works with both Podman and Docker) ./deployment/scripts/backup.sh # Or with custom backup directory ./deployment/scripts/backup.sh /path/to/backups # Or using compose (Podman) podman-compose --profile backup run --rm backup # Or using compose (Docker) docker-compose --profile backup run --rm backup ``` Backup details: - Uses SQLite `VACUUM INTO` for safe hot backups - No downtime required - Automatic compression (gzip) - Integrity verification - Automatic cleanup of old backups (default: 7 days retention) #### Scheduled Backups with cron ```bash # Create cron job for daily backups at 2 AM crontab -e # Add this line: 0 2 * * * cd /path/to/gondulf && ./deployment/scripts/backup.sh >> /var/log/gondulf-backup.log 2>&1 ``` ### Restore from Backup **CAUTION**: This will replace the current database! ```bash # Restore from backup ./deployment/scripts/restore.sh /path/to/backups/gondulf_backup_20251120_120000.db.gz # The script will: # 1. Stop the container (if running) # 2. Create a safety backup of current database # 3. Restore from the specified backup # 4. Verify integrity # 5. Restart the container (if it was running) ``` ### Test Backup/Restore ```bash # Run automated backup/restore tests ./deployment/scripts/test-backup-restore.sh # This verifies: # - Backup creation # - Backup integrity # - Database structure # - Compression # - Queryability ``` ## systemd Integration ### Rootless Podman (Recommended) **Method 1: Podman-Generated Unit** (Recommended) ```bash # 1. Start container normally first podman run -d --name gondulf \ -p 8000:8000 \ -v gondulf_data:/data:Z \ --env-file /home/$USER/gondulf/.env \ gondulf:latest # 2. Generate systemd unit file cd ~/.config/systemd/user/ podman generate systemd --new --files --name gondulf # 3. Stop the manually-started container podman stop gondulf podman rm gondulf # 4. Enable and start service systemctl --user daemon-reload systemctl --user enable --now container-gondulf.service # 5. Enable lingering (service runs without login) loginctl enable-linger $USER # 6. Verify status systemctl --user status container-gondulf ``` **Method 2: Custom Unit File** ```bash # 1. Copy unit file mkdir -p ~/.config/systemd/user/ cp deployment/systemd/gondulf-podman.service ~/.config/systemd/user/gondulf.service # 2. Edit paths if needed nano ~/.config/systemd/user/gondulf.service # 3. Reload and enable systemctl --user daemon-reload systemctl --user enable --now gondulf.service loginctl enable-linger $USER # 4. Verify status systemctl --user status gondulf ``` **systemd User Service Commands**: ```bash # Start service systemctl --user start gondulf # Stop service systemctl --user stop gondulf # Restart service systemctl --user restart gondulf # Check status systemctl --user status gondulf # View logs journalctl --user -u gondulf -f # Disable service systemctl --user disable gondulf ``` ### Docker (System Service) ```bash # 1. Copy unit file sudo cp deployment/systemd/gondulf-docker.service /etc/systemd/system/gondulf.service # 2. Edit paths in the file sudo nano /etc/systemd/system/gondulf.service # Change WorkingDirectory to your installation path # 3. Reload and enable sudo systemctl daemon-reload sudo systemctl enable --now gondulf.service # 4. Verify status sudo systemctl status gondulf ``` **systemd System Service Commands**: ```bash # Start service sudo systemctl start gondulf # Stop service sudo systemctl stop gondulf # Restart service sudo systemctl restart gondulf # Check status sudo systemctl status gondulf # View logs sudo journalctl -u gondulf -f # Disable service sudo systemctl disable gondulf ``` ### Compose-Based systemd Service For deploying with docker-compose or podman-compose: ```bash # For Podman (rootless): cp deployment/systemd/gondulf-compose.service ~/.config/systemd/user/gondulf.service # Edit to use podman-compose systemctl --user daemon-reload systemctl --user enable --now gondulf.service # For Docker (rootful): sudo cp deployment/systemd/gondulf-compose.service /etc/systemd/system/gondulf.service # Edit to use docker-compose and add docker.service dependency sudo systemctl daemon-reload sudo systemctl enable --now gondulf.service ``` ## Troubleshooting ### Container Won't Start **Check logs**: ```bash # Podman podman logs gondulf # or podman-compose logs gondulf # Docker docker logs gondulf # or docker-compose logs gondulf ``` **Common issues**: 1. **Missing SECRET_KEY**: ``` ERROR: GONDULF_SECRET_KEY is required ``` Solution: Set `GONDULF_SECRET_KEY` in `.env` (minimum 32 characters) 2. **Missing BASE_URL**: ``` ERROR: GONDULF_BASE_URL is required ``` Solution: Set `GONDULF_BASE_URL` in `.env` 3. **Port already in use**: ``` Error: bind: address already in use ``` Solution: ```bash # Check what's using port 8000 sudo ss -tlnp | grep 8000 # Use different port podman run -p 8001:8000 ... ``` ### Database Issues **Check database file**: ```bash # Podman podman exec gondulf ls -la /data/ # Docker docker exec gondulf ls -la /data/ ``` **Check database integrity**: ```bash # Podman podman exec gondulf sqlite3 /data/gondulf.db "PRAGMA integrity_check;" # Docker docker exec gondulf sqlite3 /data/gondulf.db "PRAGMA integrity_check;" ``` **Expected output**: `ok` ### Permission Errors (Rootless Podman) If you see permission errors with volumes: ```bash # 1. Check subuid/subgid configuration grep $USER /etc/subuid grep $USER /etc/subgid # 2. Add if missing sudo usermod --add-subuids 100000-165535 $USER sudo usermod --add-subgids 100000-165535 $USER # 3. Restart user services systemctl --user daemon-reload # 4. Use :Z label for SELinux systems podman run -v ./data:/data:Z ... ``` ### SELinux Issues On SELinux-enabled systems (RHEL, Fedora, CentOS): ```bash # Check for SELinux denials sudo ausearch -m AVC -ts recent # Solution 1: Add :Z label to volumes (recommended) podman run -v gondulf_data:/data:Z ... # Solution 2: Temporarily permissive (testing only) sudo setenforce 0 # Solution 3: Create SELinux policy (advanced) # Use audit2allow to generate policy from denials ``` ### Email Not Sending **Check SMTP configuration**: ```bash # Test SMTP connection from container podman exec gondulf sh -c "timeout 5 bash -c '