Skip to main content

🚀 uhdeploy - Universal Deployer CLI

A modern, declarative, and color-rich deployment CLI tool for web applications, Docker containers, and static sites. Deploy seamlessly from your local machine to any Linux VPS or hosting server using SSH/SFTP or FTP.


📦 Installation

Install uhdeploy directly via pip:

pip install uhdeploy

Or install from source for development:

git clone https://github.com/AbdullahAhmad-0/Universal-Deployer.git
cd Universal-Deployer
pip install -e .

✨ Features

  • Global CLI Command: Run uhdeploy anywhere in your terminal.
  • YAML + Dockerfile-Style Syntax: Configure complex builds and deployments using intuitive directives like RUN, COPY, CMD, WORKDIR, and ENV.
  • Interactive File Discovery: If deploy.yml is not found in the current folder, uhdeploy prompts you to enter your project directory or file path automatically.
  • Dual Protocol Support:
    • SSH / SFTP: Full remote terminal execution (paramiko), automated directory sync, and Docker container orchestration.
    • FTP: Built-in FTP transfer (ftplib) for static/shared hosting environments (with graceful warnings for unsupported remote commands).
  • Flexible Docker Workflows:
    • Local Build & Tar Transfer (Default): Builds the Docker image locally, exports it to a .tar archive, transfers it via SFTP, and loads it into the remote Docker daemon. This saves RAM and CPU on low-spec VPS servers!
    • Remote Build: Syncs files and triggers docker compose build directly on the VPS.
    • Auto Environment Sync: Automatically detects and syncs environment files (e.g. .env.production) referenced in your docker-compose.yml.
  • Traditional (No Docker) Deployments: Perfect for static frontends (React, Vue, Next.js static exports, Vite) or traditional Node/Python/PHP apps with local builds and Nginx reloading.
  • Dynamic Variable Substitution: Seamlessly interpolate ${VPS_IP}, ${SSH_KEY_PATH}, or custom environment variables from .env or .env.deploy files.
  • Rich Terminal UI: Full ANSI color formatting with colorama, interactive numbered selection menus, step badges ([Docker], [SSH], [FTP]), and upload progress bars.

⚡ Quick Start

1. Initialize Configuration

Generate a starter deploy.yml file in your project directory:

uhdeploy --init

Or view a commented example in your terminal:

uhdeploy --example

2. Configure Environment Variables (.env)

Create a .env file in your project root with your server credentials:

VPS_IP=123.45.67.89
VPS_USER=root
SSH_KEY_PATH=~/.ssh/id_rsa
SSH_PORT=22
# Optional:
SSH_PASSPHRASE=
VPS_PASSWORD=

3. Run Deployer

Launch the deployer:

# Interactive service selection menu
uhdeploy

# Or deploy a specific service directly
uhdeploy frontend

# Or deploy all services in order
uhdeploy all

(You can also run it via python -m uhdeploy or python deploy.py)


🛠️ Configuration Guide (deploy.yml)

The configuration file defines global server credentials and individual service deployment pipelines.

# =============================================================================
# 🌐 GLOBAL SERVER SETTINGS (Inherited by all services)
# =============================================================================
server:
  type: ssh                    # "ssh" (SFTP + Shell) or "ftp" (File transfer only)
  host: ${VPS_IP}              # VPS Hostname or IP (from .env)
  user: ${VPS_USER:-root}      # SSH/FTP Username (defaults to root)
  port: 22                     # Port: 22 for SSH, 21 for FTP
  key_path: ${SSH_KEY_PATH}    # Path to private key file
  passphrase: ${SSH_PASSPHRASE}# (Optional) Private key passphrase
  password: ${VPS_PASSWORD}    # (Optional) SSH or FTP password
  remote_dir: /opt/my-app      # Remote base directory on VPS

# Optional custom env file to load (default: .env or .env.deploy)
env_file: .env

# =============================================================================
# 📦 SERVICES DEFINITION
# =============================================================================
services:

  # ---------------------------------------------------------------------------
  # 1. Frontend Service (WITHOUT DOCKER)
  # ---------------------------------------------------------------------------
  frontend:
    description: "Build Next.js / React frontend and sync static files"
    docker: false              # Disable docker for this service
    work_dir: ./frontend       # Working directory relative to project root

    # Local build steps (Dockerfile-style string commands):
    build:
      - RUN npm install
      - RUN npm run build
      - COPY package.json dist/package.json

    # Upload files or directories to the VPS:
    upload:
      - dist/ -> ${remote_dir}/frontend/dist
      - public/ -> ${remote_dir}/frontend/public

    # (Optional) Wipe remote destination directory before uploading:
    # clean_remote: true

    # Commands executed on VPS after uploading (SSH only):
    run_in_vps:
      - RUN systemctl reload nginx
      - RUN echo "Frontend updated and Nginx reloaded!"

  # ---------------------------------------------------------------------------
  # 2. Backend Service (WITH DOCKER)
  # ---------------------------------------------------------------------------
  backend:
    description: "FastAPI / Node API running in Docker Compose"
    work_dir: ./backend

    docker:
      enabled: true
      compose_file: docker-compose.yml # Compose file (defaults to docker-compose.yml)
      # (Optional: .env.production referenced in compose is auto-synced!)
      service_name: backend            # Name of service inside docker-compose.yml
      build_mode: local                # "local" (build tarball & upload) or "remote"
      image: my-backend:latest
      context: ./backend
      dockerfile: ./backend/Dockerfile # (Optional)

    # Remote steps executed on VPS BEFORE starting Docker:
    before_deploy:
      - RUN mkdir -p ${remote_dir}/backend/data

    # Remote steps executed on VPS AFTER starting Docker:
    # (Note: 'docker compose up -d <service>' is already executed automatically!)
    run_in_vps:
      - RUN docker compose ps
      - RUN docker compose logs --tail=25 backend

  # ---------------------------------------------------------------------------
  # 3. API / Worker (Direct Working Directory Upload with Ignore Rules)
  # ---------------------------------------------------------------------------
  api:
    description: "Sync current working directory directly to VPS with ignore rules"
    docker: false
    work_dir: ./api

    # Upload entire working directory (.) to remote VPS destination:
    upload:
      - . -> ${remote_dir}/api

    # Files and folders to ignore when uploading:
    # (You can also put these into a .deployignore file!)
    ignore:
      - node_modules
      - .venv
      - venv
      - __pycache__
      - .git
      - .env.local
      - "*.log"
      - "*.pyc"

    # (Optional) Wipe destination directory before uploading:
    # clean_remote: true

    run_in_vps:
      - RUN pm2 restart api || pm2 start index.js --name api

🧩 Step Directives Reference

Within build, before_deploy, and run_in_vps, you can write steps as clean strings or structured objects:

Directive Example Description
RUN <cmd> - RUN npm run build Runs a shell command (locally in build, or on the VPS in before_deploy / run_in_vps).
CMD <cmd> - CMD python manage.py migrate Alias for RUN.
COPY <src> <dst> - COPY ./dist -> ${remote_dir}/dist Copies local files/folders locally or uploads them to the remote server.
WORKDIR <dir> - WORKDIR ./subfolder Changes current working directory for subsequent steps.
ENV <k>=<v> - ENV NODE_ENV=production Sets an environment variable for subsequent steps.

📁 Directory Uploads, Ignore Rules & Clean Remote

1. Uploading Current Working Directory (.)

If you want to upload the entire codebase of your working directory (e.g., Python, Node.js, PHP, or Go apps) rather than just a dist/ subfolder, specify .:

upload:
  - . -> ${remote_dir}/api

2. Ignoring Files and Folders (ignore:)

When uploading directories, you can exclude heavy, generated, or sensitive files using the ignore: (or exclude:) list in your deploy.yml:

ignore:
  - node_modules
  - .venv
  - venv
  - __pycache__
  - .git
  - .env.local
  - "*.log"
  - "*.pyc"

3. Using .deployignore

You can also place a .deployignore file in your service directory or project root using standard .gitignore syntax. uhdeploy will detect and apply these rules automatically.

4. Overwrite Behavior vs. clean_remote

  • Default (Safe Overwrite): uhdeploy overwrites matching files on the server with new ones. Any existing files already on the server that are not in your upload (e.g., user uploads, runtime database files, logs) remain untouched and safe.
  • Wiping Destination Folder (clean_remote: true): If you want to empty the target folder on the VPS before uploading fresh files, simply set:
    clean_remote: true
    

🔒 SSH vs. 📡 FTP Differences

Feature SSH / SFTP FTP (ftplib)
File & Directory Sync ✅ Yes ✅ Yes
Progress Indicator ✅ Yes ✅ Yes
Remote Shell Execution (RUN) ✅ Yes ❌ Not supported by FTP
Docker Container Management ✅ Yes ❌ Not supported by FTP
Authentication Key file, Passphrase, or Password Username & Password

🖥️ Command-Line Interface (CLI)

Usage:
  uhdeploy [OPTIONS] [SERVICE_NAME]
  (or python -m uhdeploy [OPTIONS] [SERVICE_NAME])

Options:
  -h, --help               Show full colorful help screen and exit.
  -e, --example            Display a syntax-highlighted example deploy.yml.
  --init                   Generate a starter deploy.yml in the current directory.
  -f, --file PATH          Specify path to deploy configuration file.
  -p, --project DIR        Specify project directory containing deploy configuration.
  -s, --service NAME       Name of service to deploy (or 'all').
  -l, --list               List all services available in the configuration file.
  --dry-run                Validate configuration and simulate steps without connecting.

CLI Examples

# Preview build and deployment steps without making server changes:
uhdeploy backend --dry-run

# List configured services in a custom deploy file:
uhdeploy -f ./custom-deploy.yml --list

# Deploy a specific service from an external project directory:
uhdeploy -p D:/Projects/my-app frontend

🚢 Publishing to PyPI

To build and publish uhdeploy to PyPI:

  1. Install build tools:

    pip install --upgrade build twine
    
  2. Build distribution packages (tar.gz and .whl):

    python -m build
    
  3. Check distribution packages:

    twine check dist/*
    
  4. Upload to PyPI:

    twine upload dist/*
    

Once published, anyone can install your package with:

pip install uhdeploy

📄 License

MIT License. Created by Abdullah Ahmad. Feel free to customize and extend for your deployment pipelines!

Metadata

Release files for uhdeploy 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uhdeploy 0.1.2
File Size Uploaded Provenance Actions
uhdeploy-0.1.2.tar.gz 26.9 kB Without provenance Details for uhdeploy-0.1.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for uhdeploy 0.1.2
File Interpreter ABI Platform Actions
uhdeploy-0.1.2-py3-none-any.whl Python 3 none any Details for uhdeploy-0.1.2-py3-none-any.whl

Total release size: 49.5 kB

Release files / uhdeploy-0.1.2.tar.gz

Download URL uhdeploy-0.1.2.tar.gz
Size 26.9 kB
Tags Source
SHA-256 checksum
How to use checksums
3f837b8c13221e5e75e1364c8561d7ad8863923b8a7c965e75c9a3ad266fdd31
BLAKE2b-256 checksum
How to use checksums
da5fac506ab0ac0c08d9ee5f3f131eaad4fc72d7e155123c5c525e8d7343be78
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Release files / uhdeploy-0.1.2-py3-none-any.whl

Download URL uhdeploy-0.1.2-py3-none-any.whl
Size 22.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
287b18793eda6db6d8b87d44d161673c951d6219ca1e4a32a6e5e458e0f12800
BLAKE2b-256 checksum
How to use checksums
f55ac4173c7add6309642a49e6ac1e7cba4c7c05a56887dbed0c024f67f9e410
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.7

Security for uhdeploy 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page