vps-deployer 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vps_deployer-1.2.0/LICENSE +122 -0
- vps_deployer-1.2.0/PKG-INFO +277 -0
- vps_deployer-1.2.0/README.md +258 -0
- vps_deployer-1.2.0/pyproject.toml +34 -0
- vps_deployer-1.2.0/setup.cfg +4 -0
- vps_deployer-1.2.0/tests/test_core.py +785 -0
- vps_deployer-1.2.0/tests/test_expectations.py +122 -0
- vps_deployer-1.2.0/tests/test_remote.py +59 -0
- vps_deployer-1.2.0/tests/test_scaffold_doctor.py +138 -0
- vps_deployer-1.2.0/tests/test_ssh_gate.py +179 -0
- vps_deployer-1.2.0/tests/test_usability.py +60 -0
- vps_deployer-1.2.0/vps_deployer/__init__.py +3 -0
- vps_deployer-1.2.0/vps_deployer/cli.py +288 -0
- vps_deployer-1.2.0/vps_deployer/config.py +109 -0
- vps_deployer-1.2.0/vps_deployer/deployment.py +699 -0
- vps_deployer-1.2.0/vps_deployer/doctor.py +135 -0
- vps_deployer-1.2.0/vps_deployer/expectations.py +91 -0
- vps_deployer-1.2.0/vps_deployer/models.py +380 -0
- vps_deployer-1.2.0/vps_deployer/nginx.py +53 -0
- vps_deployer-1.2.0/vps_deployer/remote.py +141 -0
- vps_deployer-1.2.0/vps_deployer/scaffold.py +162 -0
- vps_deployer-1.2.0/vps_deployer/systemd.py +62 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/PKG-INFO +277 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/SOURCES.txt +26 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/dependency_links.txt +1 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/entry_points.txt +2 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/requires.txt +1 -0
- vps_deployer-1.2.0/vps_deployer.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Business Source License 1.1
|
|
2
|
+
|
|
3
|
+
Parameters
|
|
4
|
+
|
|
5
|
+
Licensor: Jan Chimiak
|
|
6
|
+
|
|
7
|
+
Licensed Work: vps-deployer, including every version from the project's
|
|
8
|
+
inception on 2026-08-10 onward. The Licensed Work is
|
|
9
|
+
Copyright (c) 2026 Jan Chimiak.
|
|
10
|
+
|
|
11
|
+
Additional Use Grant: You may make production use of the Licensed Work, provided
|
|
12
|
+
your use does not include offering the Licensed Work to
|
|
13
|
+
third parties on a hosted or embedded basis in order to
|
|
14
|
+
compete with Jan Chimiak's paid version(s) of the Licensed
|
|
15
|
+
Work. For purposes of this license:
|
|
16
|
+
|
|
17
|
+
A "competitive offering" is a Product that is offered to
|
|
18
|
+
third parties on a paid basis, including through paid
|
|
19
|
+
support arrangements, that significantly overlaps with the
|
|
20
|
+
capabilities of Jan Chimiak's paid version(s) of the
|
|
21
|
+
Licensed Work. If your Product is not a competitive
|
|
22
|
+
offering when you first make it generally available, it
|
|
23
|
+
will not become a competitive offering later due to Jan
|
|
24
|
+
Chimiak releasing a new version of the Licensed Work with
|
|
25
|
+
additional capabilities. Products that are not provided on
|
|
26
|
+
a paid basis are not competitive.
|
|
27
|
+
|
|
28
|
+
"Product" means software that is offered to end users to
|
|
29
|
+
manage in their own environments or offered as a service on
|
|
30
|
+
a hosted basis.
|
|
31
|
+
|
|
32
|
+
"Embedded" means including source code or executable code
|
|
33
|
+
from the Licensed Work in a competitive offering. Embedded
|
|
34
|
+
also means packaging the competitive offering so that the
|
|
35
|
+
Licensed Work must be accessed or downloaded for the
|
|
36
|
+
competitive offering to operate.
|
|
37
|
+
|
|
38
|
+
Hosting or using the Licensed Work for internal purposes
|
|
39
|
+
within an organization is not considered a competitive
|
|
40
|
+
offering. Your organization includes all affiliates under
|
|
41
|
+
common control.
|
|
42
|
+
|
|
43
|
+
Change Date: Four years from the date each version of the Licensed Work
|
|
44
|
+
is first publicly distributed.
|
|
45
|
+
|
|
46
|
+
Change License: Mozilla Public License 2.0
|
|
47
|
+
|
|
48
|
+
For information about alternative licensing arrangements for the Licensed Work,
|
|
49
|
+
please contact the Licensor.
|
|
50
|
+
|
|
51
|
+
Notice
|
|
52
|
+
|
|
53
|
+
The Business Source License (this document, or the “License”) is not an Open
|
|
54
|
+
Source license. However, the Licensed Work will eventually be made available
|
|
55
|
+
under an Open Source License, as stated in this License.
|
|
56
|
+
|
|
57
|
+
Business Source License 1.1
|
|
58
|
+
|
|
59
|
+
Terms
|
|
60
|
+
|
|
61
|
+
The Licensor hereby grants you the right to copy, modify, create derivative
|
|
62
|
+
works, redistribute, and make non-production use of the Licensed Work. The
|
|
63
|
+
Licensor may make an Additional Use Grant, above, permitting limited production
|
|
64
|
+
use.
|
|
65
|
+
|
|
66
|
+
Effective on the Change Date, or the fourth anniversary of the first publicly
|
|
67
|
+
available distribution of a specific version of the Licensed Work under this
|
|
68
|
+
License, whichever comes first, the Licensor hereby grants you rights under the
|
|
69
|
+
terms of the Change License, and the rights granted in the paragraph above
|
|
70
|
+
terminate.
|
|
71
|
+
|
|
72
|
+
If your use of the Licensed Work does not comply with the requirements currently
|
|
73
|
+
in effect as described in this License, you must purchase a commercial license
|
|
74
|
+
from the Licensor, its affiliated entities, or authorized resellers, or you must
|
|
75
|
+
refrain from using the Licensed Work.
|
|
76
|
+
|
|
77
|
+
All copies of the original and modified Licensed Work, and derivative works of
|
|
78
|
+
the Licensed Work, are subject to this License. This License applies separately
|
|
79
|
+
for each version of the Licensed Work and the Change Date may vary for each
|
|
80
|
+
version of the Licensed Work released by Licensor.
|
|
81
|
+
|
|
82
|
+
You must conspicuously display this License on each original or modified copy of
|
|
83
|
+
the Licensed Work. If you receive the Licensed Work in original or modified form
|
|
84
|
+
from a third party, the terms and conditions set forth in this License apply to
|
|
85
|
+
your use of that work.
|
|
86
|
+
|
|
87
|
+
Any use of the Licensed Work in violation of this License will automatically
|
|
88
|
+
terminate your rights under this License for the current and all other versions
|
|
89
|
+
of the Licensed Work.
|
|
90
|
+
|
|
91
|
+
This License does not grant you any right in any trademark or logo of Licensor
|
|
92
|
+
or its affiliates (provided that you may use a trademark or logo of Licensor as
|
|
93
|
+
expressly required by this License).
|
|
94
|
+
|
|
95
|
+
TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
|
|
96
|
+
“AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS
|
|
97
|
+
OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF MERCHANTABILITY,
|
|
98
|
+
FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND TITLE.
|
|
99
|
+
|
|
100
|
+
MariaDB hereby grants you permission to use this License’s text to license your
|
|
101
|
+
works, and to refer to it using the trademark “Business Source License”, as long
|
|
102
|
+
as you comply with the Covenants of Licensor below.
|
|
103
|
+
|
|
104
|
+
Covenants of Licensor
|
|
105
|
+
|
|
106
|
+
In consideration of the right to use this License’s text and the “Business
|
|
107
|
+
Source License” name and trademark, Licensor covenants to MariaDB, and to all
|
|
108
|
+
other recipients of the licensed work to be provided by Licensor:
|
|
109
|
+
|
|
110
|
+
1. To specify as the Change License the GPL Version 2.0 or any later version, or
|
|
111
|
+
a license that is compatible with GPL Version 2.0 or a later version, where
|
|
112
|
+
“compatible” means that software provided under the Change License can be
|
|
113
|
+
included in a program with software provided under GPL Version 2.0 or a later
|
|
114
|
+
version. Licensor may specify additional Change Licenses without limitation.
|
|
115
|
+
|
|
116
|
+
2. To either: (a) specify an additional grant of rights to use that does not
|
|
117
|
+
impose any additional restriction on the right granted in this License, as
|
|
118
|
+
the Additional Use Grant; or (b) insert the text “None”.
|
|
119
|
+
|
|
120
|
+
3. To specify a Change Date.
|
|
121
|
+
|
|
122
|
+
4. Not to modify this License in any other way.
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: vps-deployer
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: Manage isolated systemd deployments on existing VPS hosts over SSH
|
|
5
|
+
License-Expression: BUSL-1.1
|
|
6
|
+
Project-URL: Repository, https://github.com/anfit/vps-deployer
|
|
7
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
10
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Topic :: System :: Systems Administration
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: PyYAML>=6.0
|
|
18
|
+
Dynamic: license-file
|
|
19
|
+
|
|
20
|
+
# vps-deployer
|
|
21
|
+
|
|
22
|
+
Deploy ordinary Linux services to a host with the operational properties of a
|
|
23
|
+
small PaaS, without Docker or a resident agent.
|
|
24
|
+
|
|
25
|
+
`vps-deployer` packages a release locally, transfers it over OpenSSH, installs it
|
|
26
|
+
immutably, manages a hardened systemd service or timer, checks health, optionally
|
|
27
|
+
reconciles an nginx route, and rolls back failed activation. It targets operators
|
|
28
|
+
running roughly one to a dozen independent services on a normal Linux host.
|
|
29
|
+
|
|
30
|
+
It also supports command deployments: immutable releases exposed through a
|
|
31
|
+
validated `/usr/local/bin` launcher without creating a daemon.
|
|
32
|
+
|
|
33
|
+
It is deliberately:
|
|
34
|
+
|
|
35
|
+
- not configuration management;
|
|
36
|
+
- not a container runtime;
|
|
37
|
+
- not a build system;
|
|
38
|
+
- not a multi-host rollout orchestrator.
|
|
39
|
+
|
|
40
|
+
Applications arrive as prepared artifacts with an executable `.deployer/run.sh`.
|
|
41
|
+
Host packages, firewalls, TLS issuance, databases, DNS, kernel settings, and
|
|
42
|
+
unrelated users remain owned by host administration. The core product boundary
|
|
43
|
+
is a single-host deployment transaction.
|
|
44
|
+
|
|
45
|
+

|
|
46
|
+
|
|
47
|
+
```mermaid
|
|
48
|
+
flowchart LR
|
|
49
|
+
subgraph workstation["Operator workstation"]
|
|
50
|
+
app["Application repository<br/>code + .deployer contract"]
|
|
51
|
+
infra["Infrastructure repository<br/>hosts + deployment manifests"]
|
|
52
|
+
secrets["Operator environment<br/>secret values"]
|
|
53
|
+
deployer["vps-deployer"]
|
|
54
|
+
app --> deployer
|
|
55
|
+
infra --> deployer
|
|
56
|
+
secrets -->|"resolved locally; never printed"| deployer
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
deployer -->|"OpenSSH deployment transaction"| host["Existing Linux host"]
|
|
60
|
+
host --> releases["Immutable releases<br/>current + rollback target"]
|
|
61
|
+
host --> systemd["Hardened systemd<br/>service or timer"]
|
|
62
|
+
host --> nginx["Optional nginx route"]
|
|
63
|
+
host --> state["Persistent application data"]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## When it fits
|
|
67
|
+
|
|
68
|
+
Use it for self-contained HTTP services, workers, bots, webhooks, and scheduled
|
|
69
|
+
jobs that naturally run under systemd. Go/Rust binaries, packaged Java runtimes,
|
|
70
|
+
prepared Node/Python artifacts, and small native daemons fit especially well.
|
|
71
|
+
|
|
72
|
+
Prefer another tool for container-image-first software, multi-service products,
|
|
73
|
+
host provisioning, or coordinated fleet rollouts. Compared with Docker Compose,
|
|
74
|
+
the application runtime stays Linux-native while the deployment lifecycle is
|
|
75
|
+
managed. Compared with Ansible, vps-deployer owns application releases rather
|
|
76
|
+
than general machine state.
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
Python 3.10 or newer and an OpenSSH client are required on the operator machine.
|
|
81
|
+
The simplest install is an isolated tool environment:
|
|
82
|
+
|
|
83
|
+
```console
|
|
84
|
+
pipx install git+https://github.com/anfit/vps-deployer
|
|
85
|
+
vps-deployer --version
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
To work from a checkout instead (for development):
|
|
89
|
+
|
|
90
|
+
```console
|
|
91
|
+
git clone https://github.com/anfit/vps-deployer.git
|
|
92
|
+
cd vps-deployer
|
|
93
|
+
python -m venv .venv
|
|
94
|
+
.venv/Scripts/python -m pip install . # Linux/macOS: .venv/bin/python
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
The target host requirements are checked by `host inspect` and deployment
|
|
98
|
+
expectations; see the operations guide before onboarding a host.
|
|
99
|
+
|
|
100
|
+
## Quick start
|
|
101
|
+
|
|
102
|
+
The bundled example validates offline, with no host required. It needs one
|
|
103
|
+
variable pointing at your checkout:
|
|
104
|
+
|
|
105
|
+
```console
|
|
106
|
+
export VPS_DEPLOYER_DIR=$PWD # PowerShell: $env:VPS_DEPLOYER_DIR = $PWD.Path
|
|
107
|
+
vps-deployer --repo ./examples validate
|
|
108
|
+
vps-deployer --repo ./examples secrets check # reports unset secret variables by name
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`plan`, `apply`, and `status` connect to the host in `examples/hosts/prod.yaml`
|
|
112
|
+
(an SSH alias you must replace). To start your own infrastructure repository, run `vps-deployer init DIR`, or use
|
|
113
|
+
[service-infra-template](https://github.com/anfit/service-infra-template) for a
|
|
114
|
+
fuller starting point with an operator guide. To prepare an application:
|
|
115
|
+
|
|
116
|
+
```console
|
|
117
|
+
vps-deployer init app ./my-service # writes .deployer/run.sh, ignore.txt, expect.yaml, .gitattributes
|
|
118
|
+
vps-deployer doctor ./my-service # CRLF/shebang, credential-like files, contract syntax
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Detailed documentation:
|
|
122
|
+
|
|
123
|
+
- [`docs/build-properties.md`](docs/build-properties.md) — application-visible build and deployment provenance contract.
|
|
124
|
+
- [`docs/expectations.md`](docs/expectations.md) — read-only application and infrastructure host contracts.
|
|
125
|
+
- [`docs/architecture.md`](docs/architecture.md) — ownership model and remote layout.
|
|
126
|
+
- [`docs/manifest-reference.md`](docs/manifest-reference.md) — complete host and deployment schema.
|
|
127
|
+
- [`docs/operations.md`](docs/operations.md) — onboarding, deployment, status, rollback, removal, and troubleshooting.
|
|
128
|
+
|
|
129
|
+
## Codex skills
|
|
130
|
+
|
|
131
|
+
Codex discovers two repository-scoped operational skills under `.agents/skills`:
|
|
132
|
+
|
|
133
|
+
- `operate-vps-deployments` plans, applies, verifies, diagnoses, rolls back, and removes existing managed deployments.
|
|
134
|
+
- `onboard-vps-service` adapts a new or legacy service and adds its desired state to `service-infra`.
|
|
135
|
+
|
|
136
|
+
Invoke them explicitly as `$operate-vps-deployments` or `$onboard-vps-service`, or describe a matching task while working anywhere in this repository.
|
|
137
|
+
|
|
138
|
+
## Repository layout
|
|
139
|
+
|
|
140
|
+
Keep host identity separate from application deployment identity. Deployment
|
|
141
|
+
files may be grouped recursively by application:
|
|
142
|
+
|
|
143
|
+
```text
|
|
144
|
+
infra/
|
|
145
|
+
hosts/
|
|
146
|
+
prod.yaml
|
|
147
|
+
deployments/
|
|
148
|
+
invoicer/
|
|
149
|
+
prod.yaml
|
|
150
|
+
globals/ # optional; only genuinely shared non-secret values
|
|
151
|
+
artifacts/ # optional local release inputs
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
See [`examples/hosts/prod.yaml`](examples/hosts/prod.yaml) and
|
|
155
|
+
[`examples/deployments/invoicer/prod.yaml`](examples/deployments/invoicer/prod.yaml).
|
|
156
|
+
Ordinary configuration should be committed directly in a deployment. Use
|
|
157
|
+
`from_global` only for values intentionally shared by multiple deployments, and
|
|
158
|
+
use `secrets.from_env` only for secret material.
|
|
159
|
+
|
|
160
|
+
Multiple deployments may target the same host. An optional `http_proxy` block
|
|
161
|
+
manages a deployment's nginx site, TLS certificate references, loopback upstream,
|
|
162
|
+
configuration validation, and reload independently from other routes on that host.
|
|
163
|
+
|
|
164
|
+
The application repository owns `.deployer/` and application provenance. The
|
|
165
|
+
infrastructure repository owns deployment identity, runtime configuration,
|
|
166
|
+
routing, storage, and secret references. Secret values remain in the operator
|
|
167
|
+
environment; persistent application data remains on the host.
|
|
168
|
+
|
|
169
|
+
Local artifact and include paths support explicit `${NAME}` environment roots.
|
|
170
|
+
Unresolved variables and `..` traversal are rejected. For example:
|
|
171
|
+
|
|
172
|
+
```yaml
|
|
173
|
+
release:
|
|
174
|
+
source: ${PROJECTS_DIR}/my-service
|
|
175
|
+
include:
|
|
176
|
+
- source: ${SERVICE_INFRA_DIR}/configs/my-service/prod.yaml
|
|
177
|
+
target: config/production.yaml
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Directory artifacts may contain a `.deployer/ignore.txt` file with gitignore-like
|
|
181
|
+
glob patterns. Matching developer-local files are excluded from both the release
|
|
182
|
+
hash and uploaded archive; use it to prevent local credentials and helper files
|
|
183
|
+
from entering managed releases. `.vps-deployer-ignore` remains a compatibility
|
|
184
|
+
fallback for applications that have not migrated.
|
|
185
|
+
|
|
186
|
+
Applications expose their managed runtime through `.deployer/run.sh`. This is the
|
|
187
|
+
default `runtime.command`, so ordinary infrastructure manifests need not repeat
|
|
188
|
+
an application-owned path. Named entrypoints such as `.deployer/bootstrap.sh` may
|
|
189
|
+
be selected explicitly for a distinct service lifecycle.
|
|
190
|
+
|
|
191
|
+
Every release contains an application-facing `build.properties` file. Existing
|
|
192
|
+
application/build properties are preserved and vps-deployer appends reserved
|
|
193
|
+
`deployment.*` provenance without modifying the checkout. `status` checks the
|
|
194
|
+
active deployment provenance against the desired release and commit; stale or
|
|
195
|
+
missing metadata makes the deployment unhealthy. After a successful activation,
|
|
196
|
+
the active release and its immediate predecessor are retained for rollback; older
|
|
197
|
+
release directories are pruned. Failed deployments do not trigger cleanup.
|
|
198
|
+
|
|
199
|
+
## Commands
|
|
200
|
+
|
|
201
|
+
```console
|
|
202
|
+
vps-deployer --version
|
|
203
|
+
vps-deployer init [DIR] # scaffold an infra repository
|
|
204
|
+
vps-deployer init app [DIR] # scaffold an application's .deployer/ contract
|
|
205
|
+
vps-deployer doctor [APP_DIR] # offline preflight of an application
|
|
206
|
+
vps-deployer validate
|
|
207
|
+
vps-deployer list [--format markdown] # inventory from the manifests (offline)
|
|
208
|
+
vps-deployer secrets check [DEPLOYMENT]
|
|
209
|
+
vps-deployer check DEPLOYMENT
|
|
210
|
+
vps-deployer host inspect HOST
|
|
211
|
+
vps-deployer host onboard HOST
|
|
212
|
+
vps-deployer plan DEPLOYMENT|--all [--exit-code]
|
|
213
|
+
vps-deployer apply DEPLOYMENT
|
|
214
|
+
vps-deployer status DEPLOYMENT|--all
|
|
215
|
+
vps-deployer logs DEPLOYMENT
|
|
216
|
+
vps-deployer rollback DEPLOYMENT
|
|
217
|
+
vps-deployer remove DEPLOYMENT
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The infrastructure repository is chosen in this order: `--repo`, the current
|
|
221
|
+
directory when it contains `hosts/` and `deployments/`, `$VPS_DEPLOYER_REPO`,
|
|
222
|
+
then `$SERVICE_INFRA_DIR`. Examples:
|
|
223
|
+
|
|
224
|
+
```console
|
|
225
|
+
vps-deployer --repo ./examples validate
|
|
226
|
+
vps-deployer --repo ./examples plan invoicer-prod
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`remove` stops and disables a deployment and removes its systemd unit and
|
|
230
|
+
environment file. It deliberately retains releases, writable storage, and service
|
|
231
|
+
users. Privileged deployments require `--allow-privileged` for both apply and
|
|
232
|
+
removal.
|
|
233
|
+
|
|
234
|
+
Hosts may set `ssh.privileged_host`, `ssh.privileged_user`, and
|
|
235
|
+
`ssh.privileged_identity_file` to route privileged operations through a separate
|
|
236
|
+
SSH identity. Use `tools/vps-deployer-ssh-gate` as that key's server-side forced
|
|
237
|
+
command so it cannot open an interactive root shell. The forced command must
|
|
238
|
+
declare the host's managed root and each storage path assigned to this key:
|
|
239
|
+
|
|
240
|
+
```text
|
|
241
|
+
command="/usr/local/sbin/vps-deployer-ssh-gate --managed-root /srv/vps-deployer --storage /var/lib/example" ssh-ed25519 AAAA...
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
The client sends named capability requests; the gate constructs fixed executable
|
|
245
|
+
arguments and rejects storage outside this policy. Update the forced-command
|
|
246
|
+
installation before using a client containing this protocol change.
|
|
247
|
+
|
|
248
|
+
Systemd units use a separate validated gate capability rather than opaque file
|
|
249
|
+
writes. The gate accepts only the hardened non-root unit structure rooted in the
|
|
250
|
+
deployment's active release and allowlisted storage. A restricted-key host cannot
|
|
251
|
+
apply `service.privileged: true`; root services require a separately trusted sudo
|
|
252
|
+
or host administration path.
|
|
253
|
+
|
|
254
|
+
Secrets are accepted only through explicitly declared process-environment
|
|
255
|
+
references and are never printed by planning or validation. `secrets check`
|
|
256
|
+
lists unset variables by name (exit status 1 when any are missing); `apply`
|
|
257
|
+
refuses to start with unset secrets, and `plan` warns that they are redacted.
|
|
258
|
+
|
|
259
|
+
## Versioning and compatibility
|
|
260
|
+
|
|
261
|
+
Version 1.0 establishes the stable public contracts for deployment manifests,
|
|
262
|
+
the `.deployer/` application interface, generated `build.properties`, CLI
|
|
263
|
+
commands, and the restricted SSH gate protocol. Compatible additions are made
|
|
264
|
+
within the 1.x series; incompatible contract changes require a new major
|
|
265
|
+
version and explicit migration documentation.
|
|
266
|
+
|
|
267
|
+
The client and server-side SSH gate are one security-sensitive protocol pair.
|
|
268
|
+
When release notes identify a gate protocol change, install the matching gate
|
|
269
|
+
before using the new client against a restricted-key host.
|
|
270
|
+
|
|
271
|
+
## License
|
|
272
|
+
|
|
273
|
+
The complete `vps-deployer` codebase, including versions from project inception,
|
|
274
|
+
is licensed by Jan Chimiak under the [Business Source License 1.1](LICENSE).
|
|
275
|
+
Production use is permitted except for paid hosted or embedded competitive
|
|
276
|
+
offerings. Each version converts to MPL 2.0 four years after its first public
|
|
277
|
+
distribution.
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# vps-deployer
|
|
2
|
+
|
|
3
|
+
Deploy ordinary Linux services to a host with the operational properties of a
|
|
4
|
+
small PaaS, without Docker or a resident agent.
|
|
5
|
+
|
|
6
|
+
`vps-deployer` packages a release locally, transfers it over OpenSSH, installs it
|
|
7
|
+
immutably, manages a hardened systemd service or timer, checks health, optionally
|
|
8
|
+
reconciles an nginx route, and rolls back failed activation. It targets operators
|
|
9
|
+
running roughly one to a dozen independent services on a normal Linux host.
|
|
10
|
+
|
|
11
|
+
It also supports command deployments: immutable releases exposed through a
|
|
12
|
+
validated `/usr/local/bin` launcher without creating a daemon.
|
|
13
|
+
|
|
14
|
+
It is deliberately:
|
|
15
|
+
|
|
16
|
+
- not configuration management;
|
|
17
|
+
- not a container runtime;
|
|
18
|
+
- not a build system;
|
|
19
|
+
- not a multi-host rollout orchestrator.
|
|
20
|
+
|
|
21
|
+
Applications arrive as prepared artifacts with an executable `.deployer/run.sh`.
|
|
22
|
+
Host packages, firewalls, TLS issuance, databases, DNS, kernel settings, and
|
|
23
|
+
unrelated users remain owned by host administration. The core product boundary
|
|
24
|
+
is a single-host deployment transaction.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
```mermaid
|
|
29
|
+
flowchart LR
|
|
30
|
+
subgraph workstation["Operator workstation"]
|
|
31
|
+
app["Application repository<br/>code + .deployer contract"]
|
|
32
|
+
infra["Infrastructure repository<br/>hosts + deployment manifests"]
|
|
33
|
+
secrets["Operator environment<br/>secret values"]
|
|
34
|
+
deployer["vps-deployer"]
|
|
35
|
+
app --> deployer
|
|
36
|
+
infra --> deployer
|
|
37
|
+
secrets -->|"resolved locally; never printed"| deployer
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
deployer -->|"OpenSSH deployment transaction"| host["Existing Linux host"]
|
|
41
|
+
host --> releases["Immutable releases<br/>current + rollback target"]
|
|
42
|
+
host --> systemd["Hardened systemd<br/>service or timer"]
|
|
43
|
+
host --> nginx["Optional nginx route"]
|
|
44
|
+
host --> state["Persistent application data"]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## When it fits
|
|
48
|
+
|
|
49
|
+
Use it for self-contained HTTP services, workers, bots, webhooks, and scheduled
|
|
50
|
+
jobs that naturally run under systemd. Go/Rust binaries, packaged Java runtimes,
|
|
51
|
+
prepared Node/Python artifacts, and small native daemons fit especially well.
|
|
52
|
+
|
|
53
|
+
Prefer another tool for container-image-first software, multi-service products,
|
|
54
|
+
host provisioning, or coordinated fleet rollouts. Compared with Docker Compose,
|
|
55
|
+
the application runtime stays Linux-native while the deployment lifecycle is
|
|
56
|
+
managed. Compared with Ansible, vps-deployer owns application releases rather
|
|
57
|
+
than general machine state.
|
|
58
|
+
|
|
59
|
+
## Installation
|
|
60
|
+
|
|
61
|
+
Python 3.10 or newer and an OpenSSH client are required on the operator machine.
|
|
62
|
+
The simplest install is an isolated tool environment:
|
|
63
|
+
|
|
64
|
+
```console
|
|
65
|
+
pipx install git+https://github.com/anfit/vps-deployer
|
|
66
|
+
vps-deployer --version
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
To work from a checkout instead (for development):
|
|
70
|
+
|
|
71
|
+
```console
|
|
72
|
+
git clone https://github.com/anfit/vps-deployer.git
|
|
73
|
+
cd vps-deployer
|
|
74
|
+
python -m venv .venv
|
|
75
|
+
.venv/Scripts/python -m pip install . # Linux/macOS: .venv/bin/python
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The target host requirements are checked by `host inspect` and deployment
|
|
79
|
+
expectations; see the operations guide before onboarding a host.
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
The bundled example validates offline, with no host required. It needs one
|
|
84
|
+
variable pointing at your checkout:
|
|
85
|
+
|
|
86
|
+
```console
|
|
87
|
+
export VPS_DEPLOYER_DIR=$PWD # PowerShell: $env:VPS_DEPLOYER_DIR = $PWD.Path
|
|
88
|
+
vps-deployer --repo ./examples validate
|
|
89
|
+
vps-deployer --repo ./examples secrets check # reports unset secret variables by name
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
`plan`, `apply`, and `status` connect to the host in `examples/hosts/prod.yaml`
|
|
93
|
+
(an SSH alias you must replace). To start your own infrastructure repository, run `vps-deployer init DIR`, or use
|
|
94
|
+
[service-infra-template](https://github.com/anfit/service-infra-template) for a
|
|
95
|
+
fuller starting point with an operator guide. To prepare an application:
|
|
96
|
+
|
|
97
|
+
```console
|
|
98
|
+
vps-deployer init app ./my-service # writes .deployer/run.sh, ignore.txt, expect.yaml, .gitattributes
|
|
99
|
+
vps-deployer doctor ./my-service # CRLF/shebang, credential-like files, contract syntax
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Detailed documentation:
|
|
103
|
+
|
|
104
|
+
- [`docs/build-properties.md`](docs/build-properties.md) — application-visible build and deployment provenance contract.
|
|
105
|
+
- [`docs/expectations.md`](docs/expectations.md) — read-only application and infrastructure host contracts.
|
|
106
|
+
- [`docs/architecture.md`](docs/architecture.md) — ownership model and remote layout.
|
|
107
|
+
- [`docs/manifest-reference.md`](docs/manifest-reference.md) — complete host and deployment schema.
|
|
108
|
+
- [`docs/operations.md`](docs/operations.md) — onboarding, deployment, status, rollback, removal, and troubleshooting.
|
|
109
|
+
|
|
110
|
+
## Codex skills
|
|
111
|
+
|
|
112
|
+
Codex discovers two repository-scoped operational skills under `.agents/skills`:
|
|
113
|
+
|
|
114
|
+
- `operate-vps-deployments` plans, applies, verifies, diagnoses, rolls back, and removes existing managed deployments.
|
|
115
|
+
- `onboard-vps-service` adapts a new or legacy service and adds its desired state to `service-infra`.
|
|
116
|
+
|
|
117
|
+
Invoke them explicitly as `$operate-vps-deployments` or `$onboard-vps-service`, or describe a matching task while working anywhere in this repository.
|
|
118
|
+
|
|
119
|
+
## Repository layout
|
|
120
|
+
|
|
121
|
+
Keep host identity separate from application deployment identity. Deployment
|
|
122
|
+
files may be grouped recursively by application:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
infra/
|
|
126
|
+
hosts/
|
|
127
|
+
prod.yaml
|
|
128
|
+
deployments/
|
|
129
|
+
invoicer/
|
|
130
|
+
prod.yaml
|
|
131
|
+
globals/ # optional; only genuinely shared non-secret values
|
|
132
|
+
artifacts/ # optional local release inputs
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
See [`examples/hosts/prod.yaml`](examples/hosts/prod.yaml) and
|
|
136
|
+
[`examples/deployments/invoicer/prod.yaml`](examples/deployments/invoicer/prod.yaml).
|
|
137
|
+
Ordinary configuration should be committed directly in a deployment. Use
|
|
138
|
+
`from_global` only for values intentionally shared by multiple deployments, and
|
|
139
|
+
use `secrets.from_env` only for secret material.
|
|
140
|
+
|
|
141
|
+
Multiple deployments may target the same host. An optional `http_proxy` block
|
|
142
|
+
manages a deployment's nginx site, TLS certificate references, loopback upstream,
|
|
143
|
+
configuration validation, and reload independently from other routes on that host.
|
|
144
|
+
|
|
145
|
+
The application repository owns `.deployer/` and application provenance. The
|
|
146
|
+
infrastructure repository owns deployment identity, runtime configuration,
|
|
147
|
+
routing, storage, and secret references. Secret values remain in the operator
|
|
148
|
+
environment; persistent application data remains on the host.
|
|
149
|
+
|
|
150
|
+
Local artifact and include paths support explicit `${NAME}` environment roots.
|
|
151
|
+
Unresolved variables and `..` traversal are rejected. For example:
|
|
152
|
+
|
|
153
|
+
```yaml
|
|
154
|
+
release:
|
|
155
|
+
source: ${PROJECTS_DIR}/my-service
|
|
156
|
+
include:
|
|
157
|
+
- source: ${SERVICE_INFRA_DIR}/configs/my-service/prod.yaml
|
|
158
|
+
target: config/production.yaml
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Directory artifacts may contain a `.deployer/ignore.txt` file with gitignore-like
|
|
162
|
+
glob patterns. Matching developer-local files are excluded from both the release
|
|
163
|
+
hash and uploaded archive; use it to prevent local credentials and helper files
|
|
164
|
+
from entering managed releases. `.vps-deployer-ignore` remains a compatibility
|
|
165
|
+
fallback for applications that have not migrated.
|
|
166
|
+
|
|
167
|
+
Applications expose their managed runtime through `.deployer/run.sh`. This is the
|
|
168
|
+
default `runtime.command`, so ordinary infrastructure manifests need not repeat
|
|
169
|
+
an application-owned path. Named entrypoints such as `.deployer/bootstrap.sh` may
|
|
170
|
+
be selected explicitly for a distinct service lifecycle.
|
|
171
|
+
|
|
172
|
+
Every release contains an application-facing `build.properties` file. Existing
|
|
173
|
+
application/build properties are preserved and vps-deployer appends reserved
|
|
174
|
+
`deployment.*` provenance without modifying the checkout. `status` checks the
|
|
175
|
+
active deployment provenance against the desired release and commit; stale or
|
|
176
|
+
missing metadata makes the deployment unhealthy. After a successful activation,
|
|
177
|
+
the active release and its immediate predecessor are retained for rollback; older
|
|
178
|
+
release directories are pruned. Failed deployments do not trigger cleanup.
|
|
179
|
+
|
|
180
|
+
## Commands
|
|
181
|
+
|
|
182
|
+
```console
|
|
183
|
+
vps-deployer --version
|
|
184
|
+
vps-deployer init [DIR] # scaffold an infra repository
|
|
185
|
+
vps-deployer init app [DIR] # scaffold an application's .deployer/ contract
|
|
186
|
+
vps-deployer doctor [APP_DIR] # offline preflight of an application
|
|
187
|
+
vps-deployer validate
|
|
188
|
+
vps-deployer list [--format markdown] # inventory from the manifests (offline)
|
|
189
|
+
vps-deployer secrets check [DEPLOYMENT]
|
|
190
|
+
vps-deployer check DEPLOYMENT
|
|
191
|
+
vps-deployer host inspect HOST
|
|
192
|
+
vps-deployer host onboard HOST
|
|
193
|
+
vps-deployer plan DEPLOYMENT|--all [--exit-code]
|
|
194
|
+
vps-deployer apply DEPLOYMENT
|
|
195
|
+
vps-deployer status DEPLOYMENT|--all
|
|
196
|
+
vps-deployer logs DEPLOYMENT
|
|
197
|
+
vps-deployer rollback DEPLOYMENT
|
|
198
|
+
vps-deployer remove DEPLOYMENT
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The infrastructure repository is chosen in this order: `--repo`, the current
|
|
202
|
+
directory when it contains `hosts/` and `deployments/`, `$VPS_DEPLOYER_REPO`,
|
|
203
|
+
then `$SERVICE_INFRA_DIR`. Examples:
|
|
204
|
+
|
|
205
|
+
```console
|
|
206
|
+
vps-deployer --repo ./examples validate
|
|
207
|
+
vps-deployer --repo ./examples plan invoicer-prod
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`remove` stops and disables a deployment and removes its systemd unit and
|
|
211
|
+
environment file. It deliberately retains releases, writable storage, and service
|
|
212
|
+
users. Privileged deployments require `--allow-privileged` for both apply and
|
|
213
|
+
removal.
|
|
214
|
+
|
|
215
|
+
Hosts may set `ssh.privileged_host`, `ssh.privileged_user`, and
|
|
216
|
+
`ssh.privileged_identity_file` to route privileged operations through a separate
|
|
217
|
+
SSH identity. Use `tools/vps-deployer-ssh-gate` as that key's server-side forced
|
|
218
|
+
command so it cannot open an interactive root shell. The forced command must
|
|
219
|
+
declare the host's managed root and each storage path assigned to this key:
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
command="/usr/local/sbin/vps-deployer-ssh-gate --managed-root /srv/vps-deployer --storage /var/lib/example" ssh-ed25519 AAAA...
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The client sends named capability requests; the gate constructs fixed executable
|
|
226
|
+
arguments and rejects storage outside this policy. Update the forced-command
|
|
227
|
+
installation before using a client containing this protocol change.
|
|
228
|
+
|
|
229
|
+
Systemd units use a separate validated gate capability rather than opaque file
|
|
230
|
+
writes. The gate accepts only the hardened non-root unit structure rooted in the
|
|
231
|
+
deployment's active release and allowlisted storage. A restricted-key host cannot
|
|
232
|
+
apply `service.privileged: true`; root services require a separately trusted sudo
|
|
233
|
+
or host administration path.
|
|
234
|
+
|
|
235
|
+
Secrets are accepted only through explicitly declared process-environment
|
|
236
|
+
references and are never printed by planning or validation. `secrets check`
|
|
237
|
+
lists unset variables by name (exit status 1 when any are missing); `apply`
|
|
238
|
+
refuses to start with unset secrets, and `plan` warns that they are redacted.
|
|
239
|
+
|
|
240
|
+
## Versioning and compatibility
|
|
241
|
+
|
|
242
|
+
Version 1.0 establishes the stable public contracts for deployment manifests,
|
|
243
|
+
the `.deployer/` application interface, generated `build.properties`, CLI
|
|
244
|
+
commands, and the restricted SSH gate protocol. Compatible additions are made
|
|
245
|
+
within the 1.x series; incompatible contract changes require a new major
|
|
246
|
+
version and explicit migration documentation.
|
|
247
|
+
|
|
248
|
+
The client and server-side SSH gate are one security-sensitive protocol pair.
|
|
249
|
+
When release notes identify a gate protocol change, install the matching gate
|
|
250
|
+
before using the new client against a restricted-key host.
|
|
251
|
+
|
|
252
|
+
## License
|
|
253
|
+
|
|
254
|
+
The complete `vps-deployer` codebase, including versions from project inception,
|
|
255
|
+
is licensed by Jan Chimiak under the [Business Source License 1.1](LICENSE).
|
|
256
|
+
Production use is permitted except for paid hosted or embedded competitive
|
|
257
|
+
offerings. Each version converts to MPL 2.0 four years after its first public
|
|
258
|
+
distribution.
|