Why I wrote it
I deploy small apps to plain VPS machines, and every project had its own set of Bash scripts for build, upload, restart, and rollback. On Taskmind and Campus Circle, four scripts handled build, upload, server deploy, and server setup, and I copied them from project to project. minideploy replaces them with one CLI and one deploy.yml per project.
How a deploy works
minideploy builds the app on the local machine.
Simplified replay with two systemd instances, as on Campus Circle. Log lines and release names are illustrative, not the exact CLI output.
Healthy deploy
- minideploy builds the app on the local machine.
- It uploads the build over SSH into a new, timestamped release directory.
- It points the current symlink at the new release.
- Instance 1 restarts on the new release. Instance 2 keeps serving traffic.
- minideploy waits, then calls the health endpoint of instance 1, with retries. It answers 200.
- Instance 2 restarts. Instance 1 serves traffic now.
- Instance 2 passes its health check.
- All instances are healthy, so minideploy deletes releases beyond keep_releases: 3.
Failed health check
- minideploy builds the app on the local machine.
- It uploads the build over SSH into a new, timestamped release directory.
- It points the current symlink at the new release.
- Instance 1 restarts on the new release. Instance 2 keeps serving traffic.
- Instance 1 fails its health check on every retry. Instance 2 still serves the old release.
- minideploy points the current symlink back at the previous release.
- It restarts the instances on the previous release.
- They pass their health checks, and the deploy ends on the previous release.
- Build the app on the local machine.
- Upload the build over SSH into a new, timestamped release directory.
- Point the
currentsymlink at the new release. - Restart the instances one at a time. After each restart, wait, then call its health endpoint with retries.
- If a health check fails, point the symlink back at the previous release, restart the instances, and check them again.
- When all instances are healthy, delete releases older than the number to keep.
Because the instances restart in turn, at least one instance serves traffic during the deploy. No agent runs on the server. Everything goes over SSH.
Config
app:
name: "myapp"
build:
type: "custom"
command: "go build -o dist/myapp ."
output_dir: "dist"
server:
host: "your-server.com"
user: "deploy"
deploy_dir: "/opt/myapp"
service:
type: "systemd"
instances:
- id: "1"
port: 8080
health_endpoint: "/health"
systemd_options:
exec_start: "/opt/myapp/current/myapp"
deploy:
strategy: "rolling"
keep_releases: 3Pydantic models validate the file, and a JSON Schema gives editors autocompletion. Hooks can run commands before and after deploys and rollbacks. Other commands set up the server, list releases, show status, and roll back on request.
The systemd backend is the one I used in production. Campus Circle deployed two .NET instances with it. The Docker Compose and PM2 backends exist but saw less testing.
Status
I no longer maintain it, but it did its job: Campus Circle replaced its four deploy scripts with one config file.