Documentation
gust — user guide
Everything you need to install, configure, and run gust in production. From a blank VPS to a running deployment in under five minutes.
Introduction
gust is an AI-native, self-hosted deployment engine. It runs on a single Linux server and lets you deploy Docker containers with zero-downtime, automatic HTTPS, managed databases, monitoring, and an AI assistant — all controlled from a simple CLI or a REST API.
It is built for two kinds of users:
- Developers who want Heroku-like ergonomics on their own infrastructure, without paying for a PaaS or learning Kubernetes.
- AI agents (Claude, GPT, custom automations) that need a safe, persistent API to manage infrastructure on behalf of humans.
Requirements
gust is designed to run on a single Linux server. The minimum recommended specs:
| Component | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 1 GB | 2 GB+ |
| Disk | 10 GB | 40 GB SSD |
| OS | Ubuntu 22.04+ | Ubuntu 24.04 LTS |
| Network | Ports 80, 443 open | Static IP + DNS |
For the CLI, you need Node.js 18+ (npm install path) or a POSIX shell (standalone binary path). Works on macOS, Linux, and WSL.
Install the server
Point your domain's A record at your server, then run:
curl -fsSL https://gust.run/scripts/server.sh | sudo bash
The installer performs these steps:
- Installs Docker and Docker Compose if missing
- Creates the
gust-networkDocker network - Generates a bootstrap
TOKENandJWT_SECRET - Writes
/opt/gust/.envand/opt/gust/docker-compose.yml - Starts Traefik (ports 80/443) and the gust API (port 2020)
When the installer finishes, it prints your API key:
✓ gust is running URL: https://gust.example.com KEY: gust_a1b2c3d4e5f6789...
Save this key
grep TOKEN /opt/gust/.env.Install the CLI
Two options — pick whichever fits your workflow.
Option A — npm (Node.js 18+)
npm install -g gust-deploy
Also works with pnpm add -g gust-deploy, yarn global add gust-deploy, and bun add -g gust-deploy.
Option B — Standalone binary
curl -fsSL https://gust.run/scripts/cli.sh | bash
This downloads the latest release for your OS to ~/.gust/bin/gust and adds it to your PATH.
Verify
gust version # gust v2025.3.24
Log in
With an API key:
gust login gust.example.com --key gust_a1b2c3d4e5f6...
Or with email (OTP login, for team members):
gust login gust.example.com --email you@company.com # Check your inbox for a 6-digit code.
Credentials are cached in ~/.gust/config.json. Check the connection:
gust status # ✓ connected to https://gust.example.com as owner
Your first deploy
Let's ship nginx so you can verify everything is wired up end-to-end:
gust deploy nginx:latest --name hello --domain hello.example.com
gust will:
- Pull
nginx:lateston the server - Create a container on
gust-network - Wait for the health check to pass
- Route traffic via Traefik
- Provision a Let's Encrypt certificate
When it finishes, visit https://hello.example.com in your browser. Check app status any time with gust list.
Deploying apps
The gust deploy command is the workhorse. It accepts a Docker image reference (with or without a tag) and a set of flags.
# Simplest gust deploy nginx:latest # Custom name + domain gust deploy ghcr.io/acme/api:v1.2 --name api --domain api.example.com # Environment variables gust deploy myapp --tag v2.0 --env DB_HOST=db.local --env API_KEY=secret # Preview environment (auto-cleaned after TTL) gust deploy myapp --preview feature-x --ttl 3d # Multi-service Compose stack gust deploy --compose docker-compose.yml --service web --name mystack
Private registries
Log in once, then deploy as usual:
gust registry login ghcr.io --username you --password $GHCR_TOKEN gust deploy ghcr.io/acme/private:latest
Zero-downtime deploys
Every deploy creates a new container alongside the old one. Traffic only cuts over once the new container passes its health check. If the health check fails, gust automatically rolls back and reports the error.
Databases
gust can provision and manage Postgres, MySQL, Redis, and MongoDB instances. Each database runs in its own container with a persistent volume.
gust db create postgres --name mydb gust db create redis --name cache gust db info mydb # connection string + status gust db link mydb myapp # injects DATABASE_URL gust db logs mydb gust db remove mydb --force # also deletes volume
After linking, restart the app so it picks up the new DATABASE_URL:
gust restart myapp
Domains & routing
gust supports multiple domains per app and path-based routing for putting several services behind one hostname.
# Domain management gust domain add myapp api.example.com gust domain set-primary myapp api.example.com gust domain check api.example.com # verify DNS gust domain remove myapp api.example.com # Path-based routing gust route add frontend example.com/ gust route add api example.com/api --strip gust route list api
DNS first
gust domain check to verify propagation.Environment variables
gust env set myapp DATABASE_URL=postgres://... SECRET=abc gust env list myapp gust env remove myapp SECRET
Env changes don't take effect until you restart the app: gust restart myapp.
Logs & metrics
gust logs myapp # stream logs gust logs myapp --tail 500 gust metrics myapp # live CPU / memory / network gust history myapp # deployment history
Health watcher
Enable background health monitoring to detect crashes and trigger alerts:
gust watch enable myapp --interval 15s --unhealthy-threshold 3 gust watch status myapp gust watch disable myapp
Notifications
Route events to Slack, Telegram, or any webhook URL:
# Slack gust notify add slack --url https://hooks.slack.com/services/... \ --events deploy,health # Telegram gust notify add telegram --token <bot-token> --chat <chat-id> --events all # Generic webhook gust notify add webhook --url https://example.com/hook --events deploy gust notify list gust notify test <id> gust notify mute <id> --duration 2h
Events: deploy, health, crash, cert, all.
Security
gust ships with everything you need to lock down production:
# IP whitelist gust whitelist add admin-panel 10.0.0.0/24 # Rate limiting gust ratelimit set myapp --rps 100 --burst 200 # Container policies gust policy set myapp --no-root --memory-limit 512m --cpu-limit 1.0 --read-only # Security headers (HSTS, frame deny, etc.) gust middleware add myapp headers --hsts --frame-deny --content-type-nosniff # Basic auth gust middleware add myapp basicauth --users "admin:$apr1$..." # Maintenance mode gust maintenance on myapp
Certificates & TLS
By default gust provisions certificates through Let's Encrypt. You can upload custom certificates (wildcard or corporate) and tune TLS settings per app.
gust cert status # list all certs + expiry gust cert upload example.com --cert fullchain.pem --key privkey.pem gust cert remove example.com # revert to Let's Encrypt gust tls force myapp # force HTTPS gust tls min-version myapp tls1.3
WebSockets
gust ws enable myapp --timeout 300s gust ws status myapp gust ws disable myapp
Enabling WebSockets turns on sticky sessions so clients stay on the same replica for the duration of the connection.
Lifecycle
gust rollback myapp gust restart myapp # apply config changes gust stop myapp gust start myapp gust scale myapp 3 gust remove myapp gust exec myapp -- node migrate.js
Access management
gust supports two auth methods: email + OTP for humans and API keys for CI/CD and AI agents.
# Email users gust access add alice@company.com --role admin gust access list gust access remove alice@company.com # API keys gust access create-key --name ci-deploy --email ci@company.com --role member gust access list-keys gust access revoke-key ci-deploy
| Role | Permissions |
|---|---|
| owner | Full control. Set at bootstrap. One per server. |
| admin | Full control over apps, users, keys, server settings. |
| member | Deploy + read. Cannot manage users or server settings. |
Webhooks
Wire image pushes straight into production with HMAC-signed webhooks:
gust webhook url myapp # URL: https://gust.example.com/webhooks/myapp # Secret: a9f3c2e1...
Configure the URL + secret in Docker Hub or GitHub Container Registry. Every push triggers a zero-downtime redeploy. Requests are verified with HMAC-SHA256 to prevent spoofing.
Preview environments
Spin up a throwaway deploy off a feature branch, with automatic TTL cleanup:
gust deploy myapp --preview feature-x --ttl 3d gust list # previews appear with a label gust logs myapp-preview-feature-x gust remove myapp-preview-feature-x
Operations
gust sync # reconcile DB with running containers gust sync --dry-run gust server info # CPU, memory, Docker stats gust server prune # clean dangling images gust config export --output backup.json gust config import backup.json gust audit-log --app myapp
AI assistant
Every gust server ships with a built-in AI assistant. Run gust ai to drop into an interactive session and manage your server in plain English.
$ gust ai Gust AI — type a message to manage your server (ctrl+c to exit) > deploy nginx:latest as my-site and add domain example.com [deploy_app] [add_domain] Done! Your app "my-site" is live at https://example.com > create a postgres db called userdb and link it to api [create_database] [link_database] Done. DATABASE_URL set. Restart api: gust restart api
The assistant refuses destructive actions (removing apps/databases) and prints the exact CLI command for you to run instead.
Configure a provider in /opt/gust/.env:
AI_PROVIDER=anthropic # or "openai" ANTHROPIC_API_KEY=sk-ant-... # OPENAI_API_KEY=sk-...
External AI agents
Any AI agent that can make HTTP calls can drive gust. Create a dedicated API key, hand it to the agent, and it has the same powers as a human operator.
gust access create-key --name my-agent --email agent@company.com --role admin # gust_a1b2c3d4e5f6...
Example usage in Python:
import requests
GUST = "https://gust.example.com"
KEY = "gust_a1b2c3d4e5f6..."
H = {"Authorization": f"Bearer {KEY}"}
# Deploy
requests.post(f"{GUST}/deploy", headers=H, json={
"image": "myorg/api:latest",
"name": "api",
})
# Create a database
requests.post(f"{GUST}/databases", headers=H, json={
"name": "mydb",
"type": "postgres",
})CI/CD integration
Create a dedicated API key, store it as a secret in your CI system, then call gust deploy on merge:
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm install -g gust-deploy
- run: gust login ${{ secrets.GUST_HOST }} --key ${{ secrets.GUST_KEY }}
- run: gust deploy ghcr.io/${{ github.repository }}:${{ github.sha }} --name apiREST API
Every CLI command is backed by a REST endpoint. Authenticate with a bearer token header:
Authorization: Bearer <api-key-or-jwt>
Core endpoints
| Method | Path | Description |
|---|---|---|
GET | /apps | List apps |
POST | /deploy | Deploy a new app |
GET | /apps/:name | Get app details |
POST | /apps/:name/rollback | Rollback |
POST | /apps/:name/scale | Scale replicas |
PATCH | /apps/:name/env | Set env vars |
GET | /apps/:name/logs | Stream logs (SSE) |
DELETE | /apps/:name | Remove app |
POST | /databases | Create database |
POST | /databases/:name/link | Link to app |
GET | /server/info | CPU/mem/Docker stats |
POST | /ai | AI assistant endpoint |
See the full API reference for every route (certs, notifications, volumes, policies, webhooks).
Configuration
Server environment (/opt/gust/.env)
| Variable | Required | Description |
|---|---|---|
TOKEN | Yes | Bootstrap API key for owner login |
JWT_SECRET | Yes | Secret for signing JWT tokens |
DOMAIN | Yes | Server domain (enables auto HTTPS) |
EMAIL | Yes | Let's Encrypt email |
RESEND_API_KEY | No | Resend API key for email login |
SMTP_HOST | No | SMTP host (Resend fallback) |
AI_PROVIDER | No | anthropic (default) or openai |
ANTHROPIC_API_KEY | No | Anthropic key for gust ai |
OPENAI_API_KEY | No | OpenAI key (if AI_PROVIDER=openai) |
CLI environment
| Variable | Description |
|---|---|
GUST_SERVER | Override server URL |
GUST_TOKEN | Override auth token / API key |
NO_COLOR | Disable colored output |
Architecture
gust is intentionally small. Three moving parts plus Docker do all the work:
- Traefik handles ingress, HTTPS, and routing via Docker labels
- gust server manages deploys, health checks, state, and the API
- gust CLI talks to the server over HTTP
- Docker runs all containers on the same machine
Internet → Traefik (443) → App containers (gust-network)
→ gust API (2020)
→ Database containersState lives in SQLite at /var/lib/gust/state.db. No external dependencies — back it up with gust config export.
Upgrading
gust upgrade # upgrade the CLI gust upgrade --server # upgrade the server gust upgrade --all # both
Or, if the CLI was installed via npm:
npm update -g gust-deploy
Troubleshooting
command not found: gust
After an npm install, ensure $(npm config get prefix)/bin is on your PATH. For the standalone install, restart your shell so ~/.gust/bin is picked up.
error: connection refused
Check the server is reachable (curl -I https://your-server). SSH in and run docker logs gust for server-side errors.
Deploy hangs on "Waiting for health check"
Your app isn't responding on its listening port. Either set --port to match the container, add a HEALTHCHECK to your Dockerfile, or check gust logs <app>.
Let's Encrypt fails to issue a certificate
Ensure the domain's A record points at the server, ports 80/443 are open, and DNS has propagated. Use gust domain check <domain> to verify.
AI assistant returns "no provider configured"
Set ANTHROPIC_API_KEY or OPENAI_API_KEY in /opt/gust/.env and restart: docker restart gust.
Lost the bootstrap API key?
SSH to the server and run grep TOKEN /opt/gust/.env.
Need more help?
Open an issue on GitHub or reach out to the community.