Docs/Deployment/VPS / SSH

VPS / SSH Deployment

Deploy to any VPS or server via SSH. Full control with automated deployments to DigitalOcean, Linode, Hetzner, Vultr, or any SSH-accessible server.

Setup Time

~15 minutes

Complexity

Advanced

Best For

Full Control

Overview

VPS (Virtual Private Server) deployment gives you complete control over your infrastructure. QODRYX automates the deployment process via SSH, handling code transfer, build steps, process management, and health checks.

Full Control

VPS deployment is ideal when you need full control over the server environment, custom configurations, or cost-effective hosting for steady workloads.

Supported VPS Providers

DigitalOcean

Droplets with SSH access

Linode (Akamai)

Linodes with SSH access

Hetzner

Cloud & dedicated servers

Vultr

Cloud compute instances

OVHcloud

VPS & dedicated servers

Any SSH Server

Any Linux server with SSH

Prerequisites

  • A VPS or server with SSH access
  • SSH key pair for authentication
  • Root or sudo access on the server
  • Required software installed (Node.js, Docker, etc.)

Step 1: Server Setup

Prepare your server for QODRYX deployments:

Server Setup Commands
# Update system
sudo apt update && sudo apt upgrade -y

# Install Node.js (example for Node 20)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs

# Install Docker (optional)
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER

# Install PM2 for process management
sudo npm install -g pm2

# Create deploy user
sudo useradd -m -s /bin/bash deploy
sudo usermod -aG sudo deploy

# Set up SSH key for deploy user
sudo mkdir -p /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
# Add your public key to authorized_keys

Step 2: Configure SSH Access

Add your SSH key to QODRYX:

Terminal
# Generate SSH key pair (if you don't have one)
ssh-keygen -t ed25519 -C "qodryx-deploy"

# Add private key to QODRYX secrets
qodryx secrets set SSH_PRIVATE_KEY "$(cat ~/.ssh/id_ed25519)"

# Add the public key to your server
ssh-copy-id deploy@your-server.com

# Or manually add to server
cat ~/.ssh/id_ed25519.pub
# Copy and add to /home/deploy/.ssh/authorized_keys on server

Security Best Practice

Use a dedicated SSH key for deployments. Never use your personal SSH key or root account for automated deployments.

Step 3: Configure Deployment

Configure VPS deployment in your QODRYX config:

qodryx.config.yaml
deployment:
  provider: ssh
  
  ssh:
    # Server connection
    host: 123.45.67.89
    port: 22
    user: deploy
    
    # SSH key (from secrets)
    private_key: "{{secrets.SSH_PRIVATE_KEY}}"
    
    # Deployment directory
    directory: /var/www/my-app
    
    # Build and deploy steps
    before_deploy:
      - echo "Starting deployment..."
      - sudo systemctl stop my-app || true
      
    deploy:
      - rsync -avz --delete --exclude=node_modules ./ deploy@server:/var/www/my-app/
      - cd /var/www/my-app && npm ci --production
      - cd /var/www/my-app && npm run build
      
    after_deploy:
      - sudo systemctl start my-app
      - sudo systemctl enable my-app
      
    # Health check
    healthcheck:
      url: http://localhost:3000/health
      retries: 5
      interval: 5s
      
    # Rollback on failure
    rollback:
      enabled: true
      keep_releases: 5

Deployment Strategies

Simple Sync

Direct file synchronization (suitable for simple apps):

qodryx.config.yaml
deployment:
  provider: ssh
  
  ssh:
    strategy: sync
    
    # Files to exclude from sync
    exclude:
      - node_modules
      - .git
      - .env.local
      - logs/

Release-Based

Zero-downtime deployments with release management:

qodryx.config.yaml
deployment:
  provider: ssh
  
  ssh:
    strategy: releases
    
    # Directory structure:
    # /var/www/my-app/
    #   releases/
    #     20240101120000/
    #     20240102120000/
    #   current -> releases/20240102120000
    #   shared/
    #     node_modules/
    #     uploads/
    
    releases:
      directory: /var/www/my-app
      keep: 5
      
      # Shared directories (persisted across releases)
      shared_directories:
        - node_modules
        - uploads
        - logs
        
      # Shared files
      shared_files:
        - .env
        
    # Deployment hooks
    hooks:
      before_symlink:
        - cd {{ release_path }} && npm ci --production
        - cd {{ release_path }} && npm run build
        
      after_symlink:
        - sudo systemctl reload my-app

Docker-Based

Deploy Docker containers to your VPS:

qodryx.config.yaml
deployment:
  provider: ssh
  
  ssh:
    strategy: docker
    
    docker:
      # Pull and run container
      image: myregistry/myapp:latest
      container_name: my-app
      
      # Port mapping
      ports:
        - 3000:3000
        
      # Environment file
      env_file: /var/www/my-app/.env
      
      # Volumes
      volumes:
        - /var/www/my-app/data:/app/data
        
      # Docker Compose alternative
      compose_file: docker-compose.prod.yml
      
    # Commands
    before_deploy:
      - docker pull myregistry/myapp:latest
      
    deploy:
      - docker compose -f docker-compose.prod.yml up -d
      
    after_deploy:
      - docker system prune -f

Process Management

Configure process management with PM2 or systemd:

PM2 Configuration

ecosystem.config.js
module.exports = {
  apps: [{
    name: 'my-app',
    script: 'npm',
    args: 'start',
    cwd: '/var/www/my-app',
    instances: 'max',
    exec_mode: 'cluster',
    env: {
      NODE_ENV: 'production',
      PORT: 3000
    },
    // Auto-restart on crash
    autorestart: true,
    max_memory_restart: '1G',
    // Log files
    error_file: '/var/log/my-app/error.log',
    out_file: '/var/log/my-app/out.log',
  }]
};

Systemd Service

/etc/systemd/system/my-app.service
[Unit]
Description=My App
After=network.target

[Service]
Type=simple
User=deploy
WorkingDirectory=/var/www/my-app
ExecStart=/usr/bin/node dist/index.js
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=3000

[Install]
WantedBy=multi-user.target

SSL & Nginx

Configure Nginx as a reverse proxy with SSL:

/etc/nginx/sites-available/my-app
server {
    listen 80;
    server_name myapp.com www.myapp.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name myapp.com www.myapp.com;

    ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Deployment Commands

Terminal
# Deploy to server
qodryx deploy --provider ssh

# Deploy to specific server
qodryx deploy --provider ssh --host production-server

# Rollback to previous release
qodryx rollback --provider ssh

# SSH into server
qodryx ssh production-server

# View server logs
qodryx logs --provider ssh --follow

# Run remote command
qodryx ssh production-server -- "pm2 status"

Key Features

Any Server

Works with any SSH-accessible server

Zero Downtime

Release-based deployments

Rollbacks

Instant rollback to previous versions

Health Checks

Automatic health verification

Troubleshooting

SSH Connection Failed

  • Verify SSH key is correct and has proper permissions (600)
  • Check server firewall allows SSH (port 22)
  • Ensure SSH key is added to authorized_keys on server

Permission Denied

  • Check deploy user has permissions to the deployment directory
  • Verify sudo access for system commands
  • Review systemd/PM2 permissions

Server Maintenance

VPS requires manual maintenance for OS updates, security patches, and backups. Consider managed services if you need hands-off operations.