# Custom Documentation Deployment Server Complete guide for setting up and configuring custom deployment servers for mdBook documentation. ## Overview VAPORA supports multiple custom deployment methods: - **SSH/SFTP** — Direct file synchronization to remote servers - **HTTP** — API-based deployment with REST endpoints - **Docker** — Container registry deployment - **AWS S3** — Cloud object storage with CloudFront CDN - **Google Cloud Storage** — GCS with cache control ## 🔐 Prerequisites ### Repository Secrets Setup Add these secrets to GitHub repository (**Settings** → **Secrets and variables** → **Actions**): #### Core Secrets (all methods) ``` DOCS_DEPLOY_METHOD # ssh, sftp, http, docker, s3, gcs ``` #### SSH/SFTP Method ``` DOCS_DEPLOY_HOST # docs.your-domain.com DOCS_DEPLOY_USER # docs (remote user) DOCS_DEPLOY_PATH # /var/www/vapora-docs DOCS_DEPLOY_KEY # SSH private key (base64 encoded) ``` #### HTTP Method ``` DOCS_DEPLOY_ENDPOINT # https://deploy.your-domain.com/api/deploy DOCS_DEPLOY_TOKEN # Authentication bearer token ``` #### AWS S3 Method ``` AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_DOCS_BUCKET # vapora-docs-prod AWS_REGION # us-east-1 ``` #### Google Cloud Storage Method ``` GCS_CREDENTIALS_FILE # Service account JSON (base64 encoded) GCS_DOCS_BUCKET # vapora-docs-prod ``` #### Docker Registry Method ``` DOCKER_REGISTRY # registry.your-domain.com DOCKER_USERNAME DOCKER_PASSWORD ``` --- ## 📝 Deployment Script The deployment script is located at: `.scripts/deploy-docs.sh` ### Script Features - ✅ Supports 6 deployment methods - ✅ Pre-flight validation (connectivity, required files) - ✅ Automatic backups (SSH/SFTP) - ✅ Post-deployment verification - ✅ Detailed logging - ✅ Rollback capability (SSH) ### Configuration Files ``` .scripts/ ├── deploy-docs.sh (Main deployment script) ├── .deploy-config.production (Production config) └── .deploy-config.staging (Staging config) ``` ### Running Locally ```bash # Build locally first cd docs && mdbook build # Deploy to production bash .scripts/deploy-docs.sh production # Deploy to staging bash .scripts/deploy-docs.sh staging # View logs tail -f /tmp/docs-deploy-*.log ``` --- ## 🔧 SSH/SFTP Deployment Setup ### 1. Create Deployment User on Remote Server ```bash # SSH into your server ssh user@docs.your-domain.com # Create docs user sudo useradd -m -d /var/www/vapora-docs -s /bin/bash docs # Set up directory sudo mkdir -p /var/www/vapora-docs/backups sudo chown -R docs:docs /var/www/vapora-docs sudo chmod 755 /var/www/vapora-docs ``` ### 2. Configure SSH Key ```bash # On your deployment server sudo -u docs mkdir -p /var/www/vapora-docs/.ssh sudo -u docs chmod 700 /var/www/vapora-docs/.ssh # Create authorized_keys sudo -u docs touch /var/www/vapora-docs/.ssh/authorized_keys sudo -u docs chmod 600 /var/www/vapora-docs/.ssh/authorized_keys ``` ### 3. Add Public Key to Server ```bash # Locally, generate key (if needed) ssh-keygen -t ed25519 -f ~/.ssh/vapora-docs -N "" # Add to server's authorized_keys cat ~/.ssh/vapora-docs.pub | ssh user@docs.your-domain.com \ "sudo -u docs tee -a /var/www/vapora-docs/.ssh/authorized_keys" # Test connection ssh -i ~/.ssh/vapora-docs docs@docs.your-domain.com "ls -la" ``` ### 4. Add to GitHub Secrets ```bash # Encode private key (base64) cat ~/.ssh/vapora-docs | base64 -w0 | pbcopy # Paste into GitHub Secrets: # Settings → Secrets → New repository secret # Name: DOCS_DEPLOY_KEY # Value: [paste base64-encoded key] ``` ### 5. Add SSH Configuration Secrets ``` DOCS_DEPLOY_METHOD = ssh DOCS_DEPLOY_HOST = docs.your-domain.com DOCS_DEPLOY_USER = docs DOCS_DEPLOY_PATH = /var/www/vapora-docs DOCS_DEPLOY_KEY = [base64-encoded private key] ``` ### 6. Set Up Web Server ```bash # On remote server, configure nginx sudo tee /etc/nginx/sites-available/vapora-docs > /dev/null << 'EOF' server { listen 80; server_name docs.your-domain.com; root /var/www/vapora-docs/docs; location / { index index.html; try_files $uri $uri/ /index.html; } location ~ \.(js|css|fonts|images)$ { expires 1h; add_header Cache-Control "public, immutable"; } } EOF # Enable site sudo ln -s /etc/nginx/sites-available/vapora-docs \ /etc/nginx/sites-enabled/vapora-docs # Test and reload sudo nginx -t && sudo systemctl reload nginx ``` --- ## 🌐 HTTP API Deployment Setup ### 1. Create Deployment Endpoint Implement an HTTP endpoint that accepts deployments: ```python # Example: Flask deployment API from flask import Flask, request, jsonify import tarfile import os from pathlib import Path app = Flask(__name__) DOCS_PATH = "/var/www/vapora-docs" BACKUP_PATH = f"{DOCS_PATH}/backups" @app.route('/api/deploy', methods=['POST']) def deploy(): # Verify token token = request.headers.get('Authorization', '').replace('Bearer ', '') if not verify_token(token): return {'error': 'Unauthorized'}, 401 # Check for archive if 'archive' not in request.files: return {'error': 'No archive provided'}, 400 archive = request.files['archive'] # Create backup os.makedirs(BACKUP_PATH, exist_ok=True) backup_name = f"backup_{int(time.time())}" os.rename(f"{DOCS_PATH}/current", f"{BACKUP_PATH}/{backup_name}") # Extract archive os.makedirs(f"{DOCS_PATH}/current", exist_ok=True) with tarfile.open(fileobj=archive) as tar: tar.extractall(f"{DOCS_PATH}/current") # Update symlink os.symlink(f"{DOCS_PATH}/current", f"{DOCS_PATH}/docs") return {'status': 'deployed', 'backup': backup_name}, 200 @app.route('/health', methods=['GET']) def health(): return {'status': 'healthy'}, 200 def verify_token(token): # Implement your token verification return token == os.getenv('DEPLOY_TOKEN') if __name__ == '__main__': app.run(host='127.0.0.1', port=5000) ``` ### 2. Configure Nginx Reverse Proxy ```nginx upstream deploy_api { server 127.0.0.1:5000; } server { listen 443 ssl http2; server_name deploy.your-domain.com; ssl_certificate /etc/letsencrypt/live/deploy.your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/deploy.your-domain.com/privkey.pem; # API endpoint location /api/deploy { proxy_pass http://deploy_api; client_max_body_size 100M; } # Health check location /health { proxy_pass http://deploy_api; } } ``` ### 3. Add GitHub Secrets ``` DOCS_DEPLOY_METHOD = http DOCS_DEPLOY_ENDPOINT = https://deploy.your-domain.com/api/deploy DOCS_DEPLOY_TOKEN = your-secure-token ``` --- ## ☁️ AWS S3 Deployment Setup ### 1. Create S3 Bucket and IAM User ```bash # Create bucket aws s3 mb s3://vapora-docs-prod --region us-east-1 # Create IAM user aws iam create-user --user-name vapora-docs-deployer # Create access key aws iam create-access-key --user-name vapora-docs-deployer # Create policy cat > /tmp/s3-policy.json << 'EOF' { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::vapora-docs-prod", "arn:aws:s3:::vapora-docs-prod/*" ] } ] } EOF # Attach policy aws iam put-user-policy \ --user-name vapora-docs-deployer \ --policy-name S3Access \ --policy-document file:///tmp/s3-policy.json ``` ### 2. Configure CloudFront (Optional) ```bash # Create distribution aws cloudfront create-distribution \ --origin-domain-name vapora-docs-prod.s3.amazonaws.com \ --default-root-object index.html ``` ### 3. Add GitHub Secrets ``` DOCS_DEPLOY_METHOD = s3 AWS_ACCESS_KEY_ID = AKIAIOSFODNN7EXAMPLE AWS_SECRET_ACCESS_KEY = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY AWS_DOCS_BUCKET = vapora-docs-prod AWS_REGION = us-east-1 ``` --- ## 🐳 Docker Registry Deployment Setup ### 1. Create Docker Registry ```bash # Using Docker Registry (self-hosted) docker run -d \ -p 5000:5000 \ --restart always \ --name registry \ -e REGISTRY_STORAGE_DELETE_ENABLED=true \ registry:2 # Or use managed: AWS ECR, Docker Hub, etc. ``` ### 2. Configure Registry Authentication ```bash # Create credentials echo "username:$(openssl passwd -crypt password)" > /auth/htpasswd # Docker login docker login registry.your-domain.com \ -u username -p password ``` ### 3. Add GitHub Secrets ``` DOCS_DEPLOY_METHOD = docker DOCKER_REGISTRY = registry.your-domain.com DOCKER_USERNAME = username DOCKER_PASSWORD = password ``` --- ## 🔔 Webhooks & Notifications ### Slack Notification Add webhook URL to secrets: ``` NOTIFICATION_WEBHOOK = https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX ``` Workflow sends JSON payload: ```json { "status": "success", "environment": "production", "commit": "abc123...", "branch": "main", "timestamp": "2026-01-12T14:30:00Z", "run_url": "https://github.com/vapora-platform/vapora/actions/runs/123" } ``` ### Custom Webhook Handler ```python @app.route('/webhook/deployment', methods=['POST']) def deployment_webhook(): data = request.json if data['status'] == 'success': send_slack_message(f"✅ Docs deployed: {data['commit']}") else: send_slack_message(f"❌ Deployment failed: {data['commit']}") return {'ok': True} ``` --- ## 🔄 Deployment Workflow ### Automatic Deployment Flow ``` Push to main (docs/ changes) ↓ mdBook Build & Deploy Workflow ├─ Build (2-3s) ├─ Quality Check └─ Upload Artifact ↓ mdBook Publish Workflow (triggered) ├─ Download Artifact ├─ Deploy to Custom Server │ ├─ Pre-flight Checks │ ├─ Deployment Method │ │ ├─ SSH: rsync files + backup │ │ ├─ HTTP: upload tarball │ │ ├─ S3: sync to bucket │ │ └─ Docker: push image │ └─ Post-deployment Verify ├─ Create Deployment Record └─ Send Notifications ↓ Documentation Live ``` ### Manual Deployment ```bash # Local build cd docs && mdbook build # Deploy using script bash .scripts/deploy-docs.sh production # Or specific environment bash .scripts/deploy-docs.sh staging ``` --- ## 🆘 Troubleshooting ### SSH Deployment Fails **Error**: `Permission denied (publickey)` **Fix**: ```bash # Verify key is in authorized_keys cat ~/.ssh/vapora-docs.pub | ssh user@server \ "sudo -u docs cat >> /var/www/vapora-docs/.ssh/authorized_keys" # Test connection ssh -i ~/.ssh/vapora-docs -v docs@server.com ``` ### HTTP Deployment Fails **Error**: `HTTP 401 Unauthorized` **Fix**: - Verify token in GitHub Secrets matches server - Check HTTPS certificate validity - Verify endpoint is reachable ```bash curl -H "Authorization: Bearer $TOKEN" https://deploy.server.com/health ``` ### S3 Deployment Fails **Error**: `NoSuchBucket` **Fix**: - Verify bucket name in secrets - Check IAM policy allows the action - Verify AWS credentials ```bash aws s3 ls s3://vapora-docs-prod/ ``` ### Docker Deployment Fails **Error**: `unauthorized: authentication required` **Fix**: - Verify credentials in secrets - Test Docker login locally ```bash docker login registry.your-domain.com ``` --- ## 📊 Deployment Configuration Reference ### Production Template ```bash # .deploy-config.production DEPLOY_METHOD="ssh" DEPLOY_HOST="docs.vapora.io" DEPLOY_USER="docs" DEPLOY_PATH="/var/www/vapora-docs" BACKUP_RETENTION_DAYS=30 NOTIFY_ON_SUCCESS="true" NOTIFY_ON_FAILURE="true" ``` ### Staging Template ```bash # .deploy-config.staging DEPLOY_METHOD="ssh" DEPLOY_HOST="staging-docs.vapora.io" DEPLOY_USER="docs-staging" DEPLOY_PATH="/var/www/vapora-docs-staging" BACKUP_RETENTION_DAYS=7 NOTIFY_ON_SUCCESS="false" NOTIFY_ON_FAILURE="true" ``` --- ## ✅ Verification Checklist - [ ] SSH/SFTP user created and configured - [ ] SSH keys generated and added to server - [ ] Web server (nginx/apache) configured - [ ] GitHub secrets added for deployment method - [ ] Test push to main with docs/ changes - [ ] Monitor Actions tab for workflow - [ ] Verify deployment completed - [ ] Check documentation site - [ ] Test rollback procedure (if applicable) - [ ] Set up monitoring/alerts --- ## 📚 Additional Resources - [AWS S3 Documentation](https://docs.aws.amazon.com/s3/) - [Google Cloud Storage](https://cloud.google.com/storage/docs) - [Docker Registry](https://docs.docker.com/registry/) - [GitHub Actions Documentation](https://docs.github.com/en/actions) --- **Last Updated**: 2026-01-12 **Status**: ✅ Production Ready For deployment script details, see: `.scripts/deploy-docs.sh`