VPS Management
How to manage your Hostinger VPSes after initial setup.
List Your VPSes
just vps-list
This queries the Hostinger API for all your VPSes, then SSHes into each one to detect what's running. Output includes:
| Column | Source |
|---|---|
| HOSTNAME | Hostinger API |
| IP | Hostinger API |
| STATE | Hostinger API (running, stopped, etc.) |
| PLAN | Hostinger API |
| ALIAS | Your ~/.ssh/config (or - if not registered) |
| ROLE | SSH probe (detects nginx, webhook, 1password, docker) |
If a VPS shows unreachable under ROLE, it's not registered in your SSH config yet.
Register a VPS
Before you can run modules or get role detection, a VPS needs to be in your SSH config with the 1Password agent:
# 1. Find the hostname from vps-list
just vps-list
# 2. Register it with an alias
just vps-add openclaw-vps-2 srv1460704.hstgr.cloud
# 3. Test the connection
ssh openclaw-vps-2 echo ok
vps-add does two things:
- Scans the host key and adds it to
~/.ssh/known_hosts - Adds an entry to
~/.ssh/configusing the 1Password SSH agent:
Host openclaw-vps-2
HostName srv1460704.hstgr.cloud
User root
IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
If the alias already exists in your SSH config, the command is a no-op.
Run Modules on a VPS
Once registered, use just vps to run setup modules:
# Run all modules (1Password, SSH hardening, firewall, webserver)
just vps openclaw-vps-2 --all
# Pick specific modules
just vps openclaw-vps-2 --ssh # SSH hardening only
just vps openclaw-vps-2 --1password # 1Password CLI only
just vps openclaw-vps-2 --firewall web # Firewall with web profile
just vps openclaw-vps-2 --webserver # Nginx + webhook + certbot
just vps openclaw-vps-2 --ssh --firewall web # Combine modules
Firewall Profiles
| Profile | Ports |
|---|---|
base | 22 (SSH only) |
web | 22, 80, 443 |
custom | Only the ports you specify |
Provision a New VPS
To create a brand new VPS from scratch:
just vps-new --alias openclaw-vps-3
This runs the full flow: creates the VPS via the Hostinger API, waits for it to boot, runs all modules, adds it to SSH config, and stores details in 1Password. See VPS Setup for details.
Commands Reference
| Command | Purpose |
|---|---|
just vps-list | List all VPSes with state and detected role |
just vps-add ALIAS HOST | Register a VPS in SSH config |
just vps-new --alias NAME | Create a new VPS + run all modules |
just vps HOST --all | Run all modules on a registered VPS |
just vps HOST --ssh | SSH hardening |
just vps HOST --1password | 1Password CLI setup |
just vps HOST --firewall PROFILE | Firewall rules |
just vps HOST --webserver | Nginx + webhook + certbot |
Current VPSes
| Alias | Hostname | IP | Role |
|---|---|---|---|
openclaw-vps-1 | srv1296613.hstgr.cloud | 76.13.100.227 | Hollanov (Telegram) + Ilya (OpenClaw gateway) (WhatsApp) + Hollanov site (nginx, webhook) |
Using the AI Assistants
Two AI assistants run on openclaw-vps-1:
| Name | Container | Channel | Model | Description |
|---|---|---|---|---|
| Hollanov | hollanov | Telegram @illyarozenovbot | Claude Sonnet 4.5 | Custom agent runtime — 34 tools, permissions, memory, sessions |
| Ilya | openclaw-gateway | WhatsApp (+15089811893) | Claude Opus 4.6 | Hostinger OpenClaw gateway — default personality and settings |
Quick Reference
Most day-to-day operations are initiated through just recipes:
# Hollanov (Telegram) logs
just bot-logs # or: just bot-logs-tail (live)
# Ilya (WhatsApp) logs
just gateway-logs
# Deploy (git pull + rebuild Hollanov)
just bot-deploy
# Restart Hollanov (no rebuild — fast, picks up env changes)
just bot-restart
# Start/stop Ilya gateway
just gateway-start
just gateway-stop
# Open a shell in the bot container
just bot-shell
Direct SSH equivalents (for cases where just isn't available):
ssh openclaw-vps-1 docker logs hollanov --tail 20
ssh openclaw-vps-1 docker logs openclaw-gateway --tail 20
ssh openclaw-vps-1 'cd /var/www/openclaw && bash deploy/start.sh'
ssh openclaw-vps-1 docker ps
In web/ephemeral environments (e.g. Claude Code web), use just vps-exec:
just vps-exec "docker logs hollanov --tail 20"
just vps-exec "docker ps"
Deploying Updates
Code changes auto-deploy via GitHub webhook. When you push to main:
- Webhook triggers
hollanov/webhook/deploy.sh - Script runs
git pull origin main - If
src/,package*,Dockerfile,docker-compose, ordeploy/changed, the bot container is rebuilt
Note: The webhook config file
webhook.confis generated at deploy time fromwebhook.conf.tplviaenvsubst(so the webhook secret stays out of git). The generatedwebhook.confis gitignored.
Manual deploy (all initiated through just):
just bot-deploy # SSHes into VPS, pulls, rebuilds container
Configuration
Bot configuration is defined in .env.schema (the single source of truth). Secrets are resolved inside the container at startup by varlock; non-secret config has defaults in the schema.
| Variable | Type | Source |
|---|---|---|
TELEGRAM_BOT_TOKEN | Secret | 1Password (resolved by varlock) |
ANTHROPIC_API_KEY | Secret | 1Password (resolved by varlock) |
BRAVE_API_KEY | Secret | 1Password (resolved by varlock) |
TELEGRAM_ALLOWED_USERS | Secret | 1Password (resolved by varlock) |
WHATSAPP_ALLOWED_NUMBERS | Secret | 1Password (resolved by varlock) |
HOLLANOV_CHANNELS | Config | Default: telegram |
HOLLANOV_MODEL | Config | Default: claude-sonnet-4-6 |
HOLLANOV_MAX_TOKENS | Config | Default: 250000 |
HOLLANOV_MAX_TURNS | Config | Default: 50 |
HOLLANOV_LOG_LEVEL | Config | Default: info |
HOLLANOV_HOST | Config | Default: openclaw-vps-1 |
ELEVENLABS_API_KEY | Secret (optional) | 1Password (resolved by varlock) — enables TTS voice responses |
ELEVENLABS_VOICE_ID | Secret (optional) | 1Password (resolved by varlock) — Ilya Rozenov voice ID |
To change the model or limits, edit .env.schema, commit, push, and the webhook auto-deploys.
Container Architecture
The bot container is self-contained for secret resolution. Only one secret
exists on the VPS host: OP_SERVICE_ACCOUNT_TOKEN. The container runs as
non-root user hollanov (UID 1000).
How it works
deploy/start.shvalidates the token, then runsdocker compose up -d --build- Docker passes only
OP_SERVICE_ACCOUNT_TOKENand non-secret config to the container - Inside the container,
npx varlock runreads.env.schema, resolves everyop()reference via the 1Password SDK, and execs the bot - Resolved secrets exist only in the running process memory — never on disk
What's in the container
| Layer | What |
|---|---|
| Base | node:22-slim (Debian bookworm) |
| System deps | curl, git, ssh, chromium (WhatsApp Web), 1Password CLI |
| Node deps | All app dependencies (including varlock + 1Password plugin) |
| App code | Compiled TypeScript in dist/ |
| Config | .env.schema (secret references + defaults, no plaintext) |
| Entrypoint | npx varlock run -- node dist/cli/bot.js |
Volumes
| Host path | Container path | Purpose |
|---|---|---|
./data/sessions | /home/hollanov/.openclaw/sessions | Chat sessions |
./data/logs | /home/hollanov/.openclaw/logs | Application logs |
./data/memory | /home/hollanov/.openclaw/memory | Persistent memory |
./data/cost | /home/hollanov/.openclaw/cost | API cost tracking |
./data/heartbeat | /home/hollanov/.openclaw/heartbeat | Health check data |
./wiki | /var/www/openclaw/wiki | Wiki vault |
./data/whatsapp-session | /app/data/whatsapp-session | WhatsApp auth |
Gateway vs Bot
| Bot (Hollanov) | Gateway (Ilya) | |
|---|---|---|
| Image | Built from Dockerfile | ghcr.io/hostinger/hvps-openclaw:latest |
| Secret resolution | Inside container (op run --env-file) | Host-side (op run --env-file) |
| Env template | .env.schema | deploy/.env.gateway.tpl |
| Start script | deploy/start.sh | deploy/start-gateway.sh |
Container Management Commands
All container operations go through the justfile:
Lifecycle
| Command | What it does |
|---|---|
just bot-deploy | SSH into VPS, git pull, rebuild + restart container |
just bot-restart | Restart without rebuilding (picks up env changes) |
just bot-redeploy | Rebuild without git pull (hotfix testing) |
just gateway-start | Start the WhatsApp gateway container |
just gateway-stop | Stop the gateway container |
Monitoring
| Command | What it does |
|---|---|
just bot-logs | Last 50 log lines from bot container |
just bot-logs-tail | Live tail of bot logs |
just gateway-logs | Last 20 log lines from gateway |
just bot-shell | Interactive shell inside bot container |
just heartbeat | Health status for all heartbeat sources |
just heartbeat-check | Exit non-zero if any source unhealthy |
just agent-cost-vps | Show API cost report from the VPS |
WhatsApp
| Command | What it does |
|---|---|
just whatsapp-link | Interactive QR code linking |
Secrets
| Command | What it does |
|---|---|
just secrets-check | Validate all secrets resolve (local, via varlock) |
just env-load | Validate .env.schema without running |
just env-scan | Find unresolved secret references |
Updating Secrets
All app secrets live in the Shared-Infrastructure vault in 1Password. To rotate:
# 1. Update the secret in 1Password (from your Mac)
op item edit "Anthropic API Key" --vault "Shared-Infrastructure" credential="sk-ant-new-key-here"
# 2. Restart the container to pick up the change (varlock re-resolves on startup)
just bot-restart
No files on the VPS need editing. Varlock re-resolves all op:// references on every container start.
Restarting the Container
Using just recipes (preferred):
just bot-restart # Fast restart — no rebuild
just bot-deploy # Full rebuild — git pull + docker build
just bot-logs # Check recent logs
Or via direct SSH:
ssh openclaw-vps-1 systemctl restart hollanov
ssh openclaw-vps-1 docker ps
ssh openclaw-vps-1 docker logs hollanov --tail 20
Destroying a VPS
To stop billing and decommission a VPS:
just vps-destroy openclaw-vps-2
This has three safeguards before proceeding:
- Shows full VPS details (hostname, IP, plan, state, subscription cost)
- Requires you to type the alias name to confirm
- Requires you to type
DESTROYto proceed
On confirmation it:
- Stops the VPS
- Disables auto-renewal on the subscription (VPS runs until the current billing period ends)
- Removes the entry from
~/.ssh/config
Note: The Hostinger API does not support immediate VPS deletion. To fully remove a stopped VPS, go to hpanel.hostinger.com/vps.
Troubleshooting
VPS shows "unreachable" in vps-list
The VPS isn't in your SSH config or the host key is missing:
just vps-add my-alias srv-hostname.hstgr.cloud
SSH prompts for password
The 1Password SSH agent isn't offering the key. Check:
-
1Password desktop app is running and unlocked
-
The key is registered in the agent config:
cat ~/.config/1Password/ssh/agent.toml -
The
IdentityAgentis set in~/.ssh/config(either on the host entry or viaHost *)
SSH fails with "Too many authentication failures"
The 1Password SSH agent offers all loaded keys in order. If Private vault keys are listed before VPS keys in ~/.config/1Password/ssh/agent.toml, the server hits its MaxAuthTries limit before the VPS key is tried.
Fix: ensure VPS keys are listed before the Private vault in agent.toml:
# VPS keys first
[[ssh-keys]]
item = "openclaw-vps-1"
vault = "Shared-Infrastructure"
# Then everything else
[[ssh-keys]]
vault = "Private"
Verify the key order with:
SSH_AUTH_SOCK=~/Library/Group\ Containers/2BUA8C4S2C.com.1password/t/agent.sock ssh-add -l
Module fails with "permission denied"
Modules run as root. Make sure the SSH config entry has User root.
Telegram bot returns 404 on all API calls
The bot token is invalid or revoked. Create a new bot via @BotFather, update the token in 1Password, and restart:
op item edit "Telegram Bot Token" --vault "Shared-Infrastructure" credential="NEW_TOKEN"
ssh openclaw-vps-1 systemctl restart hollanov
Bot container keeps restarting
Check the logs:
just bot-logs # or: just vps-exec "docker logs hollanov --tail 50"
Common causes: invalid API key, network issues, TypeScript build failure.
Bot doesn't respond to messages
- Check the bot is running:
just vps-exec "docker ps" - Check your Telegram user ID is in
TELEGRAM_ALLOWED_USERSin the 1Password Shared-Infrastructure vault - Check logs for permission denials or errors:
just bot-logs