Some checks failed
Documentation Lint & Validation / Markdown Linting (push) Has been cancelled
Documentation Lint & Validation / Validate mdBook Configuration (push) Has been cancelled
Documentation Lint & Validation / Content & Structure Validation (push) Has been cancelled
mdBook Build & Deploy / Build mdBook (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
Documentation Lint & Validation / Lint & Validation Summary (push) Has been cancelled
mdBook Build & Deploy / Documentation Quality Check (push) Has been cancelled
mdBook Build & Deploy / Deploy to GitHub Pages (push) Has been cancelled
mdBook Build & Deploy / Notification (push) Has been cancelled
13 KiB
13 KiB
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
# 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
# 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
# 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
# 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
# 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
# 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:
# 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
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
# 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)
# 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
# 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
# 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:
{
"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
@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
# 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:
# 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
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
aws s3 ls s3://vapora-docs-prod/
Docker Deployment Fails
Error: unauthorized: authentication required
Fix:
- Verify credentials in secrets
- Test Docker login locally
docker login registry.your-domain.com
📊 Deployment Configuration Reference
Production Template
# .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
# .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
Last Updated: 2026-01-12 Status: ✅ Production Ready
For deployment script details, see: .scripts/deploy-docs.sh