upySingle-binary Web deployment tool

A single binary, straight from Release to production.

Single Go binary, zero runtime dependencies, minimal memory footprint.
Swiftly deploy GitHub Releases or local bundles to servers for SPAs and Docker containers.

Linux / macOS · x86_64 / arm64 · Zero dependencies

Build Artifact → upy Runner → Production
Blue Go Gopher on a skateboard, aligned within engineering drafting guides
Release BundleupyProduction
Fast Deploy Disk Clean Convention Backup Project Analysis Bark / Webhook

Installation

Install with a single command.

Automatically detects system architecture and configures Shell Tab completions.
Single standalone binary, no background daemon required.

View installation docs ↗
Install script
curl -fsSL https://raw.githubusercontent.com/AaronConlon/upy/main/install.sh | bash

Installed to /usr/local/bin by default. Override via INSTALL_DIR.

Core Features

Deploy, clean, backup, and alert.

Covering application delivery, disk space maintenance, baseline data backups, and multi-recipient mobile alerts in a unified binary.

01 / Deploy

Atomic switching from Release to production.

Interactively pick a GitHub Release asset in the terminal, or deploy latest with a single command. Supports SHA-256 integrity verification, resumable downloads, and offline local bundles.

View deployment guide ↗
Deploy commands
# Interactively pick a Release asset
upy release

# Deploy latest official release
upy release latest

# Deploy local offline bundle
upy bundle ./bundle.zip
SPA Static Sites

Atomic symlinks with automatic permission fixes.

Extracted into isolated release folders and switched atomically via the current symlink. Automatically standardizes permissions to 0755/0644 to prevent 403 Forbidden errors in Nginx and Caddy.

Native Systemd Services

Go binaries with atomic units and zero privilege creep.

Runs lightweight Go/Rust binaries under systemd supervision. Guarantees strict service user pre-checks, argument fidelity, port identity probing, and automatic rollback on failure.

Docker / Compose Applications

Graceful handover with automated rollbacks.

Prepares new images and configurations before stopping previous containers to prevent port conflicts. Includes built-in health checks that automatically rollback on startup failure.

Deployment Walkthrough: From CI Packaging to Server Execution

Explore real-world pipelines for each deployment mode — including CI build steps, server directory structure, terminal logs, and resilience mechanisms.

01 CI/CD Packaging
.github/workflows/deploy.yml
# 1. Build frontend artifacts
- name: Build frontend
  run: npm ci && npm run build

# 2. Inject manifest & package bundle.zip
- name: Package bundle
  run: |
    cat << 'EOF' > dist/upy.yaml
    name: my-spa-app
    version: ${{ github.ref_name }}
    type: static
    static:
      directory: dist
    EOF
    cd dist && zip -r ../bundle.zip . upy.yaml
02 Server Configuration & Layout
/var/www/my-spa-app/deploy.yaml
# Server project declaration
name: my-spa-app
release:
  provider: github
  repository: owner/my-spa-app
  asset: bundle.zip

# Retention policies (defaults to 3 bundle archives, range 0~100)
bundles:
  keep: 3
releases:
  keep: 1

# Nginx config: root /var/www/my-spa-app/current;
host: /var/www/my-spa-app
$ upy release latest
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  📦 Project: my-spa-app
  📁 Root:    /var/www/my-spa-app
  🧩 Type:    Static SPA (static)
  🏷 Active:  (none)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  -> Fetching latest release from owner/my-spa-app...
  ✓ Selected version v1.0.0
  -> ⬇️  Downloading bundle.zip (1.4MB)...
  ✓ Saved to bundles/v1.0.0-bundle.zip
  -> Extracting bundle...
  ✓ Bundle unpacked into releases/1.0.0
  -> Validating static entrypoint...
  ✓ Verified dist/index.html
  -> Normalizing filesystem permissions...
  ✓ Corrected dirs to 0755, files to 0644
  -> Atomically switching symlink...
  ✓ current -> releases/1.0.0/dist
  ✓ Deployment complete: my-spa-app 1.0.0
  ⏱ Elapsed: 0.8s
Zero 403 Forbidden Errors: Recursively resets directory permissions to 0755 and file permissions to 0644.
Atomic Symlink Swap: Points current -> releases/1.0.0/dist in microseconds using POSIX atomic rename.
Dual-Version Rollback: Keeps active and previous release intact; rollback immediately swaps symlink back.
02 / Clean

Disk space analysis and safe pruning.

Auto-prunes old bundle archives during deployment based on bundles.keep (default 3, range 0~100); supports interactive cleanup or one-click slim down with upy clean. Dual-layer active protection ensures the active current version is never deleted, backed by cross-process locks against concurrent operations.

View cleanup docs ↗
Clean commands
# Interactive clean menu with usage breakdown
upy clean

# Purge downloaded bundle archives only
upy clean --bundles

# One-click slim down (caches and old releases)
upy clean --all -y
03 / Backup

Convention backups with lock protection.

Follows convention over configuration, automatically detecting project backup.sh scripts and injecting standardized runtime variables. Guarantees zero dirty snapshots via cross-process exclusive locks, bounded disk footprint through intelligent retention rotation, and instant mobile alerts.

View backup documentation ↗
01 / Convention Script Auto-Discovery backup.sh
#!/usr/bin/env bash
# Auto-injected: UPY_BACKUP_DIR, UPY_PROJECT_NAME, UPY_TIMESTAMP
mkdir -p "${UPY_BACKUP_DIR}"
BACKUP_FILE="${UPY_BACKUP_DIR}/dump-${UPY_PROJECT_NAME}-${UPY_TIMESTAMP}.sql.gz"

# Execute dump inside container or on native host
docker compose exec -T db mysqldump -u root -p"${DB_PASSWORD}" "${DB_NAME}" | gzip > "${BACKUP_FILE}"
02 / Context & Rotation Optional Config deploy.yaml
# Optional backup settings
backup:
  script: backup.sh            # Auto-probes backup.sh if omitted
  dir: backups                 # Output directory (<root>/backups)
  retention: 7                 # Keep latest 7 archives, auto-prune older
  timeout: 15m                 # Safety timeout (supports 30s, 5m, 1h)
Terminal — upy backup
upy backup
$ upy backup
-> Preparing convention backup for project my-api-service...
✓ Discovered convention script: backup.sh (chmod 0755)
✓ Acquired project exclusive lock (.deploy.lock)
-> Executing backup with context (UPY_BACKUP_DIR=backups)...
------------------------------------------------------------
Exporting database to backups/dump-my-api-service-20261019-120000.sql.gz...
Database export completed successfully!
------------------------------------------------------------
✓ Project my-api-service backup finished successfully!
  📦 Archive:  dump-my-api-service-20261019-120000.sql.gz (42.5 MB)
  🛡️  Rotation: Kept latest 7 archives (cleaned 1 older file)
  ⏱  Elapsed:   2.4s
  🔔 Pushed notification to Bark channel
upy backup Interactive project backup
upy backup my-app --list View archive history & file sizes
upy backup my-app --prune 5 Retain latest 5 archives & clean older
upy backup --all -y Batch backup all registered projects
Cross-Process Mutual Exclusion: Acquires exclusive .deploy.lock (action: 备份中), strictly preventing dirty snapshots from concurrent releases or cleanups.
Automatic Retention Rotation: Automatically scans archive modification times and cleans snapshots past the threshold, keeping server disk usage bounded.
Observability & Mobile Alerts: Pushes instant alerts with elapsed execution times, archive file sizes, and failure reasons to Bark mobile channels.
04 / Analysis

Host project catalog with 100% benchmark bars.

Dynamic host-wide project cataloging with proportional horizontal benchmark bars using the largest project as the 100% baseline. Spotlights recent releases at the top with version, stack type, relative duration, and service health. Strictly avoids symlink double-counting, with --detail breakdowns and --json export.

View analysis docs ↗
01 / 100% Benchmark Bars Spot Storage Hogs Max Benchmark Bar
# Largest project serves as 100% baseline reference
1  my-compose-app  docker  1.45 GB  [████████████████████] 100.0%
2  my-api-service  system  482 MB   [███████░░░░░░░░░░░░░]  32.5%
3  portal-frontend static   86 MB   [█░░░░░░░░░░░░░░░░░░░]   5.8%
02 / Latest Release Spotlight Real-Time Pulse Spotlight Focus
# Top card spotlighting the most recent release
📦 Project: my-api-service (v1.2.0)
🧩 Stack:   Systemd Native Service (system) · upy-my-api-service.service
⏱  Time:    22 minutes ago (2026-10-19 19:40:02)
🟢 Status:  Active & Running (active)
Terminal — upy analysis
$ upy analysis
upy analysis — Host Project & Disk Analysis

━ Recent Deployment Activity ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  📦 Project: my-api-service (v1.2.0)
  🧩 Stack:   Systemd Native Service (system) · upy-my-api-service.service
  ⏱  Time:    22 minutes ago (2026-10-19 19:40:02)
  🟢 Status:  Active & Running (active)

━ Project Analysis (Status · Space Benchmark · Stack · Version) ━━━━━━━━━
#   Project             Status         Stack    Version     Disk Space  Benchmark (Max)
1   my-compose-app      🟢 active      docker   v2.1.0      1.45 GB     [████████████████████] 100.0%
2   my-api-service      🟢 active      system   v1.2.0      482.3 MB    [███████░░░░░░░░░░░░░]  32.5%
3   portal-frontend     🟢 active      static   v0.9.4       86.1 MB    [█░░░░░░░░░░░░░░░░░░░]   5.8%
4   demo-spa            ⚪ undeployed  static   -            14.2 MB    [▏░░░░░░░░░░░░░░░░░░░]   0.9%

━ Storage Summary ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  📊 Projects:    4 registered
  💾 Total Space: 2.03 GB (Average 519.6 MB per project)
  👑 Top Hog:     my-compose-app (1.45 GB, 71.4% of total host upy space)
  💡 Tip: Run upy analysis --detail to view subdirectory breakdowns
upy analysis Host-wide project space bars & latest release
upy analysis --detail Subdirectory breakdowns (releases, bundles, backups)
upy analysis <name> Detailed breakdown for a specific project
upy analysis --json Structured JSON output for monitoring & CI
Symlink Double-Count Prevention: Never recurses into current symlinks; active releases are counted only once.
Subdirectory Breakdown (--detail): Separates active releases, historical versions, bundle cache, and backups.
Structured JSON Export (--json): Emits standard JSON for automated health checks, CI/CD audits, and monitoring.
05 / Notify

Multi-recipient Bark mobile push & project-specific Webhooks.

Real-time delivery alerts directly to iOS devices and team messaging bots. Powered natively by Bark mobile push and project-isolated Webhooks. Zero third-party middleware, project-specific routing in deploy.yaml, global fallback in ~/.upy/config.yaml, HMAC-SHA256 request verification, and smart deduplication.

View notification docs ↗
Open Source What is Bark?
bark.day.app ↗

Bark is a lightweight, open-source push notification service for iOS. It allows developers to send custom push notifications to Apple devices via simple HTTP GET/POST requests. upy integrates Bark natively to give you instant, zero-middleware deployment and backup observability.

01 / Unified Structure Common Defaults & Multi-User Scope deploy.yaml / ~/.upy/config.yaml
# Unified format: Common properties + user list with precedence
notify:
  bark:
    # 1. Common attributes (inherited by all recipients)
    serverUrl: https://api.day.app     # Default official API or private domain
    group: "payment-api"               # Push group header
    sound: "chime"                     # Default ringtone (bell, chime, glass, etc.)
    level: "active"                    # Default priority level

    # 2. Per-user attributes (precedence over common attributes)
    users:
      - name: "Frontend Lead"
        token: "DEVICE_KEY_ALICE"
        sound: "glass"                 # Overrides common sound
      - name: "DevOps On-Call"
        token: "DEVICE_KEY_BOB"
        serverUrl: https://bark.ops.internal # Overrides common serverUrl
        level: "timeSensitive"
      - name: "QA Tester"
        token: "DEVICE_KEY_CAROL"      # Fully inherits common attributes
02 / Single-User Direct Mode Zero Boilerplate deploy.yaml
# Minimal single-user mode: Declare token directly under bark
notify:
  bark:
    serverUrl: https://api.day.app
    group: "my-service"
    sound: "bell"
    token: "DEVICE_KEY_SINGLE_USER"
03 / Project-Specific Webhook Zero Global Pollution · HMAC Verified deploy.yaml
# Project-scoped webhook (single endpoint, multiple URLs, or HMAC signature)
notify:
  webhook:
    - url: "https://ops.example.com/api/deploy-webhook"
      secret: "SEC_KEY_FOR_HMAC_SHA256"  # Injects X-Upy-Signature (HMAC-SHA256)
      headers:
        X-Environment: "production"
    - url: "https://qyapi.weixin.qq.com/.../send" # Slack, Lark, DingTalk bots
04 / Payload Standards Rich Context Generated Automatically Title & Body Structure
# Title and Body are assembled automatically from deployment state:
Title: upy 部署成功 · my-api-service
Body:
  项目: my-api-service
  版本: v1.2.0
  类型: Systemd 系统服务
  目录: /opt/my-api-service
  耗时: 3.2s
  # Failure alerts automatically append "原因: <diagnostic error>"
Terminal — Mobile Push
upy release latest
$ upy release latest
-> Deploying my-api-service v1.2.0...
✓ Unit configuration validated
✓ Switched unit to releases/1.2.0
✓ Systemd service started (upy-my-api-service.service)
✓ Healthcheck passed (HTTP 200 OK)
✓ current -> releases/1.2.0
⏱ Elapsed: 3.2s
🔔 Dispatched 3 push notifications & 1 project webhook:
  ✓ Alice (https://api.day.app) [HTTP 200]
  ✓ Bob (https://api.day.app) [HTTP 200]
  ✓ DevOps On-Call (https://bark.mycorp.internal) [HTTP 200]
  ✓ Webhook: https://ops.example.com/api/deploy-webhook [HTTP 200]
🔔 Bark · Just now
Deploy Success
Title: upy 部署成功 · my-api-service
项目: my-api-service
版本: v1.2.0
类型: Systemd 系统服务
目录: /opt/my-api-service
耗时: 3.2s
💾 Bark · 2m ago
Backup Success
Title: upy 备份成功 · my-api-service
项目: my-api-service
归档: dump-my-api-service-20261019-120000.sql.gz (42.5 MB)
目录: /opt/my-api-service
耗时: 2.4s
Project-Isolated Webhooks: Webhooks reside exclusively in project deploy.yaml files without polluting global configurations. Features HMAC-SHA256 signature verification (X-Upy-Signature) and idempotency IDs.
Per-User Attribute Precedence: When splitting recipients via - name, user-level fields override common defaults while unset properties inherit automatically.
Dual Configuration Scopes: Project-specific routing in deploy.yaml and host-wide on-call fallback in ~/.upy/config.yaml.
Smart Multi-Scale Duration: Execution time automatically shifts units — seconds below 60s (3.2s), minutes above 60s (1m15s), and hours above 60m (1h2m).
Smart Merge & Deduplication: Overlapping recipients between project and global configs are merged and dispatched once.
Bundle Retention & Shell Completion Sync: Configurable bundles.keep (0~100, default 3) in deploy.yaml; upy update automatically synchronizes completion rules for zsh, bash, and fish so new commands are instantly available.

A single binary,
built for delivery.

Open source (MIT License).
Standalone binary, zero external dependencies, low memory footprint.

Read Full Documentation ↗