🚀 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
uhdeployanywhere in your terminal. - YAML + Dockerfile-Style Syntax: Configure complex builds and deployments using intuitive directives like
RUN,COPY,CMD,WORKDIR, andENV. - Interactive File Discovery: If
deploy.ymlis not found in the current folder,uhdeployprompts 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).
- SSH / SFTP: Full remote terminal execution (
- Flexible Docker Workflows:
- Local Build & Tar Transfer (Default): Builds the Docker image locally, exports it to a
.tararchive, 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 builddirectly on the VPS. - Auto Environment Sync: Automatically detects and syncs environment files (e.g.
.env.production) referenced in yourdocker-compose.yml.
- Local Build & Tar Transfer (Default): Builds the Docker image locally, exports it to a
- 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.envor.env.deployfiles. - 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):
uhdeployoverwrites 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:
-
Install build tools:
pip install --upgrade build twine
-
Build distribution packages (tar.gz and .whl):
python -m build
-
Check distribution packages:
twine check dist/*
-
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.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| uhdeploy-0.1.4.tar.gz | 27.9 kB | Without provenance | Details for uhdeploy-0.1.4.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| uhdeploy-0.1.4-py3-none-any.whl | Python 3 | none | any | Details for uhdeploy-0.1.4-py3-none-any.whl |
Total release size: 51.4 kB
Release files / uhdeploy-0.1.4.tar.gz
| Download URL | uhdeploy-0.1.4.tar.gz |
|---|---|
| Size | 27.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fd1eb859457882347decc60f2d81d1c0fa98c59050846d475bc14e9d12c4f2a4
|
|
BLAKE2b-256 checksum How to use checksums |
ac9649de21137bbf1c4a6149ee1eb631cadabbd6d40651edbc6d7823f8492112
|
| 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.4-py3-none-any.whl
| Download URL | uhdeploy-0.1.4-py3-none-any.whl |
|---|---|
| Size | 23.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
522f4531499b69dc05fe9abe7c170f290e85fd9203241679c0812478945e541e
|
|
BLAKE2b-256 checksum How to use checksums |
dd9de0329b5c7c882cb0f5137df7904a2f02c49c2804ab8cf6bdcdcfae032f96
|
| 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.4
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.