Skip to main content

vps-deployer

Deploy ordinary Linux services to a host with the operational properties of a small PaaS, without Docker or a resident agent.

vps-deployer packages a release locally, transfers it over OpenSSH, installs it immutably, manages a hardened systemd service or timer, checks health, optionally reconciles an nginx route, and rolls back failed activation. It targets operators running roughly one to a dozen independent services on a normal Linux host.

It also supports command deployments: immutable releases exposed through a validated /usr/local/bin launcher without creating a daemon.

It is deliberately:

  • not configuration management;
  • not a container runtime;
  • not a build system;
  • not a multi-host rollout orchestrator.

Applications arrive as prepared artifacts with an executable .deployer/run.sh. Host packages, firewalls, TLS issuance, databases, DNS, kernel settings, and unrelated users remain owned by host administration. The core product boundary is a single-host deployment transaction.

Animated vps-deployer walkthrough: plan, apply, and verify

flowchart LR
    subgraph workstation["Operator workstation"]
        app["Application repository<br/>code + .deployer contract"]
        infra["Infrastructure repository<br/>hosts + deployment manifests"]
        secrets["Operator environment<br/>secret values"]
        deployer["vps-deployer"]
        app --> deployer
        infra --> deployer
        secrets -->|"resolved locally; never printed"| deployer
    end

    deployer -->|"OpenSSH deployment transaction"| host["Existing Linux host"]
    host --> releases["Immutable releases<br/>current + rollback target"]
    host --> systemd["Hardened systemd<br/>service or timer"]
    host --> nginx["Optional nginx route"]
    host --> state["Persistent application data"]

When it fits

Use it for self-contained HTTP services, workers, bots, webhooks, and scheduled jobs that naturally run under systemd. Go/Rust binaries, packaged Java runtimes, prepared Node/Python artifacts, and small native daemons fit especially well.

Prefer another tool for container-image-first software, multi-service products, host provisioning, or coordinated fleet rollouts. Compared with Docker Compose, the application runtime stays Linux-native while the deployment lifecycle is managed. Compared with Ansible, vps-deployer owns application releases rather than general machine state.

Installation

Python 3.10 or newer and an OpenSSH client are required on the operator machine. The simplest install is an isolated tool environment:

pipx install vps-deployer
vps-deployer --version
pipx upgrade vps-deployer      # later

To work from a checkout instead (for development):

git clone https://github.com/anfit/vps-deployer.git
cd vps-deployer
python -m venv .venv
.venv/Scripts/python -m pip install .   # Linux/macOS: .venv/bin/python

The target host requirements are checked by host inspect and deployment expectations; see the operations guide before onboarding a host.

Quick start

The bundled example validates offline, with no host required. It lives in the source repository and is not part of the pip package, so clone the repository first and point one variable at the clone:

export VPS_DEPLOYER_DIR=$PWD            # PowerShell: $env:VPS_DEPLOYER_DIR = $PWD.Path
vps-deployer --repo ./examples validate
vps-deployer --repo ./examples secrets check     # reports unset secret variables by name

plan, apply, and status connect to the host in examples/hosts/prod.yaml (an SSH alias you must replace). To start your own infrastructure repository, run vps-deployer init DIR, or use service-infra-template for a fuller starting point with an operator guide. To prepare an application:

vps-deployer init app ./my-service     # writes .deployer/run.sh, ignore.txt, expect.yaml, .gitattributes
vps-deployer doctor ./my-service       # CRLF/shebang, credential-like files, contract syntax

Detailed documentation:

Codex skills

Codex discovers two repository-scoped operational skills under .agents/skills:

  • operate-vps-deployments plans, applies, verifies, diagnoses, rolls back, and removes existing managed deployments.
  • onboard-vps-service adapts a new or legacy service and adds its desired state to service-infra.

Invoke them explicitly as $operate-vps-deployments or $onboard-vps-service, or describe a matching task while working anywhere in this repository.

Repository layout

Keep host identity separate from application deployment identity. Deployment files may be grouped recursively by application:

infra/
  hosts/
    prod.yaml
  deployments/
    invoicer/
      prod.yaml
  globals/                 # optional; only genuinely shared non-secret values
  artifacts/               # optional local release inputs

See examples/hosts/prod.yaml and examples/deployments/invoicer/prod.yaml. Ordinary configuration should be committed directly in a deployment. Use from_global only for values intentionally shared by multiple deployments, and use secrets.from_env only for secret material.

Multiple deployments may target the same host. An optional http_proxy block manages a deployment's nginx site, TLS certificate references, loopback upstream, configuration validation, and reload independently from other routes on that host.

The application repository owns .deployer/ and application provenance. The infrastructure repository owns deployment identity, runtime configuration, routing, storage, and secret references. Secret values remain in the operator environment; persistent application data remains on the host.

Local artifact and include paths support explicit ${NAME} environment roots. Unresolved variables and .. traversal are rejected. For example:

release:
  source: ${PROJECTS_DIR}/my-service
  include:
    - source: ${SERVICE_INFRA_DIR}/configs/my-service/prod.yaml
      target: config/production.yaml

Directory artifacts may contain a .deployer/ignore.txt file with gitignore-like glob patterns. Matching developer-local files are excluded from both the release hash and uploaded archive; use it to prevent local credentials and helper files from entering managed releases. .vps-deployer-ignore remains a compatibility fallback for applications that have not migrated.

Applications expose their managed runtime through .deployer/run.sh. This is the default runtime.command, so ordinary infrastructure manifests need not repeat an application-owned path. Named entrypoints such as .deployer/bootstrap.sh may be selected explicitly for a distinct service lifecycle.

Every release contains an application-facing build.properties file. Existing application/build properties are preserved and vps-deployer appends reserved deployment.* provenance without modifying the checkout. status checks the active deployment provenance against the desired release and commit; stale or missing metadata makes the deployment unhealthy. After a successful activation, the active release and its immediate predecessor are retained for rollback; older release directories are pruned. Failed deployments do not trigger cleanup.

Commands

vps-deployer --version
vps-deployer init [DIR]                 # scaffold an infra repository
vps-deployer init app [DIR]             # scaffold an application's .deployer/ contract
vps-deployer doctor [APP_DIR]           # offline preflight of an application
vps-deployer validate
vps-deployer list [--format markdown]   # inventory from the manifests (offline)
vps-deployer secrets check [DEPLOYMENT]
vps-deployer check DEPLOYMENT
vps-deployer host inspect HOST
vps-deployer host onboard HOST
vps-deployer plan DEPLOYMENT|--all [--exit-code]
vps-deployer apply DEPLOYMENT
vps-deployer status DEPLOYMENT|--all
vps-deployer logs DEPLOYMENT
vps-deployer rollback DEPLOYMENT
vps-deployer remove DEPLOYMENT

The infrastructure repository is chosen in this order: --repo, the current directory when it contains hosts/ and deployments/, $VPS_DEPLOYER_REPO, then $SERVICE_INFRA_DIR. Examples:

vps-deployer --repo ./examples validate
vps-deployer --repo ./examples plan invoicer-prod

remove stops and disables a deployment and removes its systemd unit and environment file. It deliberately retains releases, writable storage, and service users. Privileged deployments require --allow-privileged for both apply and removal.

Hosts may set ssh.privileged_host, ssh.privileged_user, and ssh.privileged_identity_file to route privileged operations through a separate SSH identity. Use the packaged vps-deployer-ssh-gate (vps_deployer/data/; vps-deployer host bootstrap HOST prints or installs it) as that key's server-side forced command so it cannot open an interactive root shell. The forced command must declare the host's managed root and each storage path assigned to this key:

command="/usr/local/sbin/vps-deployer-ssh-gate --managed-root /srv/vps-deployer --storage /var/lib/example" ssh-ed25519 AAAA...

The client sends named capability requests; the gate constructs fixed executable arguments and rejects storage outside this policy. Update the forced-command installation before using a client containing this protocol change.

Systemd units use a separate validated gate capability rather than opaque file writes. The gate accepts only the hardened non-root unit structure rooted in the deployment's active release and allowlisted storage. A restricted-key host cannot apply service.privileged: true; root services require a separately trusted sudo or host administration path.

Secrets are accepted only through explicitly declared references (secrets.<KEY>.from_env: NAME) and are never printed by planning or validation. NAME is looked up in order in the process environment, then in a user-only secrets file, then (optionally) the OS credential store, and the first hit wins. The environment always comes first, so existing setups behave exactly as before. secrets check lists unset variables by name (exit status 1 when any are missing); apply refuses to start with unset secrets, and plan warns that they are redacted. secrets list, secrets rotate and secrets unset manage stored values over time. See operations.

Versioning and compatibility

Version 1.0 establishes the stable public contracts for deployment manifests, the .deployer/ application interface, generated build.properties, CLI commands, and the restricted SSH gate protocol. Compatible additions are made within the 1.x series; incompatible contract changes require a new major version and explicit migration documentation.

The client and server-side SSH gate are one security-sensitive protocol pair. When release notes identify a gate protocol change, install the matching gate before using the new client against a restricted-key host.

License

The complete vps-deployer codebase, including versions from project inception, is licensed by Jan Chimiak under the Business Source License 1.1. Production use is permitted except for paid hosted or embedded competitive offerings. Each version converts to MPL 2.0 four years after its first public distribution.

Metadata

Release files for vps-deployer 1.4.0

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

Source distribution (sdist)

Source distribution for vps-deployer 1.4.0
File Size Uploaded Provenance Actions
vps_deployer-1.4.0.tar.gz 65.2 kB With provenance Details for vps_deployer-1.4.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for vps-deployer 1.4.0
File Interpreter ABI Platform Actions
vps_deployer-1.4.0-py3-none-any.whl Python 3 none any Details for vps_deployer-1.4.0-py3-none-any.whl

Total release size: 115.8 kB

Release files / vps_deployer-1.4.0.tar.gz

Download URL vps_deployer-1.4.0.tar.gz
Size 65.2 kB
Tags Source
SHA-256 checksum
How to use checksums
7bcad6de780cdf5da90bd140c6b38b4f63dba3c9e4ff38bd2036194a38e03cc0
BLAKE2b-256 checksum
How to use checksums
12e4b2a4116c65029d24c85f67282c131c5daee317ebf778d659f63bf32efcbe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Release files / vps_deployer-1.4.0-py3-none-any.whl

Download URL vps_deployer-1.4.0-py3-none-any.whl
Size 50.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
be869b06eb881b4fc90f773a4151e31bd6ceb661b1c5c19c439398a39da87a39
BLAKE2b-256 checksum
How to use checksums
5e910e35d236caef05c5a4a51d611a6112c13a2c9099d0d3513776f283ba37a4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

Security for vps-deployer 1.4.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vps_deployer-1.4.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 4, 2026.

Transparency log

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