Skip to main content

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:

ColumnSource
HOSTNAMEHostinger API
IPHostinger API
STATEHostinger API (running, stopped, etc.)
PLANHostinger API
ALIASYour ~/.ssh/config (or - if not registered)
ROLESSH 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:

  1. Scans the host key and adds it to ~/.ssh/known_hosts
  2. Adds an entry to ~/.ssh/config using 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

ProfilePorts
base22 (SSH only)
web22, 80, 443
customOnly 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

CommandPurpose
just vps-listList all VPSes with state and detected role
just vps-add ALIAS HOSTRegister a VPS in SSH config
just vps-new --alias NAMECreate a new VPS + run all modules
just vps HOST --allRun all modules on a registered VPS
just vps HOST --sshSSH hardening
just vps HOST --1password1Password CLI setup
just vps HOST --firewall PROFILEFirewall rules
just vps HOST --webserverNginx + webhook + certbot

Current VPSes

AliasHostnameIPRole
openclaw-vps-1srv1296613.hstgr.cloud76.13.100.227Hollanov (Telegram) + Ilya (OpenClaw gateway) (WhatsApp) + Hollanov site (nginx, webhook)

Using the AI Assistants

Two AI assistants run on openclaw-vps-1:

NameContainerChannelModelDescription
HollanovhollanovTelegram @illyarozenovbotClaude Sonnet 4.5Custom agent runtime — 34 tools, permissions, memory, sessions
Ilyaopenclaw-gatewayWhatsApp (+15089811893)Claude Opus 4.6Hostinger 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:

  1. Webhook triggers hollanov/webhook/deploy.sh
  2. Script runs git pull origin main
  3. If src/, package*, Dockerfile, docker-compose, or deploy/ changed, the bot container is rebuilt

Note: The webhook config file webhook.conf is generated at deploy time from webhook.conf.tpl via envsubst (so the webhook secret stays out of git). The generated webhook.conf is 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.

VariableTypeSource
TELEGRAM_BOT_TOKENSecret1Password (resolved by varlock)
ANTHROPIC_API_KEYSecret1Password (resolved by varlock)
BRAVE_API_KEYSecret1Password (resolved by varlock)
TELEGRAM_ALLOWED_USERSSecret1Password (resolved by varlock)
WHATSAPP_ALLOWED_NUMBERSSecret1Password (resolved by varlock)
HOLLANOV_CHANNELSConfigDefault: telegram
HOLLANOV_MODELConfigDefault: claude-sonnet-4-6
HOLLANOV_MAX_TOKENSConfigDefault: 250000
HOLLANOV_MAX_TURNSConfigDefault: 50
HOLLANOV_LOG_LEVELConfigDefault: info
HOLLANOV_HOSTConfigDefault: openclaw-vps-1
ELEVENLABS_API_KEYSecret (optional)1Password (resolved by varlock) — enables TTS voice responses
ELEVENLABS_VOICE_IDSecret (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

  1. deploy/start.sh validates the token, then runs docker compose up -d --build
  2. Docker passes only OP_SERVICE_ACCOUNT_TOKEN and non-secret config to the container
  3. Inside the container, npx varlock run reads .env.schema, resolves every op() reference via the 1Password SDK, and execs the bot
  4. Resolved secrets exist only in the running process memory — never on disk

What's in the container

LayerWhat
Basenode:22-slim (Debian bookworm)
System depscurl, git, ssh, chromium (WhatsApp Web), 1Password CLI
Node depsAll app dependencies (including varlock + 1Password plugin)
App codeCompiled TypeScript in dist/
Config.env.schema (secret references + defaults, no plaintext)
Entrypointnpx varlock run -- node dist/cli/bot.js

Volumes

Host pathContainer pathPurpose
./data/sessions/home/hollanov/.openclaw/sessionsChat sessions
./data/logs/home/hollanov/.openclaw/logsApplication logs
./data/memory/home/hollanov/.openclaw/memoryPersistent memory
./data/cost/home/hollanov/.openclaw/costAPI cost tracking
./data/heartbeat/home/hollanov/.openclaw/heartbeatHealth check data
./wiki/var/www/openclaw/wikiWiki vault
./data/whatsapp-session/app/data/whatsapp-sessionWhatsApp auth

Gateway vs Bot

Bot (Hollanov)Gateway (Ilya)
ImageBuilt from Dockerfileghcr.io/hostinger/hvps-openclaw:latest
Secret resolutionInside container (op run --env-file)Host-side (op run --env-file)
Env template.env.schemadeploy/.env.gateway.tpl
Start scriptdeploy/start.shdeploy/start-gateway.sh

Container Management Commands

All container operations go through the justfile:

Lifecycle

CommandWhat it does
just bot-deploySSH into VPS, git pull, rebuild + restart container
just bot-restartRestart without rebuilding (picks up env changes)
just bot-redeployRebuild without git pull (hotfix testing)
just gateway-startStart the WhatsApp gateway container
just gateway-stopStop the gateway container

Monitoring

CommandWhat it does
just bot-logsLast 50 log lines from bot container
just bot-logs-tailLive tail of bot logs
just gateway-logsLast 20 log lines from gateway
just bot-shellInteractive shell inside bot container
just heartbeatHealth status for all heartbeat sources
just heartbeat-checkExit non-zero if any source unhealthy
just agent-cost-vpsShow API cost report from the VPS

WhatsApp

CommandWhat it does
just whatsapp-linkInteractive QR code linking

Secrets

CommandWhat it does
just secrets-checkValidate all secrets resolve (local, via varlock)
just env-loadValidate .env.schema without running
just env-scanFind 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:

  1. Shows full VPS details (hostname, IP, plan, state, subscription cost)
  2. Requires you to type the alias name to confirm
  3. Requires you to type DESTROY to 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:

  1. 1Password desktop app is running and unlocked

  2. The key is registered in the agent config:

    cat ~/.config/1Password/ssh/agent.toml
  3. The IdentityAgent is set in ~/.ssh/config (either on the host entry or via Host *)

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

  1. Check the bot is running: just vps-exec "docker ps"
  2. Check your Telegram user ID is in TELEGRAM_ALLOWED_USERS in the 1Password Shared-Infrastructure vault
  3. Check logs for permission denials or errors: just bot-logs