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