Skip to main content
Action Llama supports two deployment methods for running agents on a VPS: SSH push deploy (al push) and manual deployment. This page explains what happens under the hood. For a step-by-step walkthrough, see Deploying to a VPS.

Two Deployment Methods

Manages your project on a remote server via SSH. No container registry, no vendor lock-in.

Manual deployment

Install Action Llama directly on the server and run al start --expose --headless. Simpler but requires SSH’ing into the server to manage.

What al env prov Does

The provisioning wizard (al env prov <name>) creates a new VPS and configures it as an environment. It supports three modes:
  1. Connect to existing server — collects SSH details, verifies Docker, sets up firewall
  2. Provision new Vultr VPS — full automated wizard
  3. Provision new Hetzner VPS — full automated wizard

Provisioning flow (Vultr/Hetzner)

  1. API key — prompts for the provider API key (saved to credentials if missing)
  2. Plan selection — searchable list of available server plans
  3. Region selection — filtered to regions where the plan is available
  4. OS selection — x64 Linux images (auto-selects Ubuntu 24.04 if 1GB+ RAM)
  5. SSH key — use existing vps_ssh credential, pick from provider keys, or generate new
  6. Firewall — creates firewall group (SSH port 22, web ports 80/443 or 3000)
  7. Instance creation — launches the VPS with a cloud-init script that installs Docker and Node.js
  8. Readiness check — polls until the VPS is active, SSH-ready, and Docker/Node.js are installed (10-minute timeout)
  9. Cloudflare HTTPS (optional) — see below
  10. Environment file — saves all connection details to ~/.action-llama/environments/<name>.toml

Cloudflare HTTPS (optional)

If you choose HTTPS during provisioning:
  1. Prompts for Cloudflare API token (requires Zone:DNS:Edit and Zone:SSL and Certificates:Edit permissions)
  2. Lists your Cloudflare zones and collects a subdomain
  3. Upserts a DNS A record (proxied) pointing to the VPS IP
  4. Generates a Cloudflare Origin CA certificate (or reuses an existing one)
  5. Installs nginx as a reverse proxy with TLS termination
  6. Configures nginx to proxy to the gateway on localhost
  7. Sets Cloudflare SSL mode to Full (Strict)
  8. Verifies the health check endpoint through the full chain

What al push Does

A full push (al push -E <name>) runs three phases:

Phase A: Bootstrap and sync

  1. Bootstrap check — verifies Node.js and Docker are installed on the remote server
  2. SSH hardening — disables password auth, restricts root login, installs fail2ban
  3. File sync — rsyncs project files to the remote basePath
  4. Credential sync — pushes credentials over SSH to ~/.action-llama/credentials/ on the server

Phase B: Configure

  1. Conditional npm install — computes SHA-256 hash of package.json + package-lock.json, compares with remote hash, only runs npm install if changed
  2. nginx setup — if Cloudflare HTTPS was configured, installs the origin cert and nginx config
  3. Write .env.toml — writes the environment binding with gateway config
  4. Symlink credentials — creates credential symlinks as needed
  5. Install systemd unit — writes and enables the systemd service

Phase C: Restart and verify

  1. Restart servicesystemctl restart action-llama
  2. Health check — polls the /health endpoint with exponential backoff, tails the systemd journal for diagnostics if it fails

Hot-reload: single agent push

al push <agent> -E <name> pushes only that agent’s files and credentials. The running scheduler detects the change via file watcher and hot-reloads the agent — no full restart needed.

Systemd Service

The systemd unit file runs the scheduler in headless mode:
[Unit]
Description=Action Llama Scheduler
After=network.target

[Service]
Type=simple
User=root
WorkingDirectory=/opt/action-llama
ExecStart=/usr/local/bin/al start -w --expose --headless
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
The service restarts automatically on failure with a 10-second delay.

Environment File [server] Fields

# ~/.action-llama/environments/production.toml
[server]
host = "5.6.7.8"
user = "root"
port = 22
keyPath = "~/.ssh/id_rsa"
basePath = "/opt/action-llama"
expose = true
FieldTypeDefaultDescription
hoststring(required)Server IP or hostname
userstring"root"SSH username
portnumber22SSH port
keyPathstring(ssh-agent)Path to SSH private key
basePathstring"/opt/action-llama"Remote project directory
exposebooleantrueBind gateway to 0.0.0.0 (all interfaces)

Teardown

al env deprov <name> tears down an environment:
  1. Stops all running containers on the server
  2. Cleans up remote credentials
  3. Deletes DNS records (if Cloudflare was configured)
  4. Optionally deletes the VPS instance (if it was provisioned via al env prov)

Security

  • SSH key-only auth — password authentication is disabled during al push bootstrap
  • fail2ban — installed automatically to protect against brute-force SSH attacks
  • No passphrase on SSH keys — VPS deployment SSH keys should not have passphrases because agents need unattended access. The vps_ssh credential is separate from your personal SSH key.
  • Credential isolation — each agent container receives only the credentials listed in its SKILL.md frontmatter
  • expose = false for reverse proxy — when using Caddy or nginx in front of the gateway, set expose = false to prevent the gateway port from being directly reachable

Troubleshooting

SSH connection failures

# Test SSH manually
ssh -o ConnectTimeout=10 root@your-vps-ip echo ok

# Check key permissions
chmod 600 ~/.ssh/id_rsa

# Verify environment config
al env show production
al env check production

Gateway not accessible externally

  • Ensure expose = true in [server] config (or --expose flag for manual deployment)
  • Check firewall: ports 80/443 (if using reverse proxy) or 8080 (direct)
  • Verify TLS certificate is valid if using Caddy/nginx

Docker issues on the server

# Check Docker daemon
ssh root@your-vps-ip systemctl status docker

# Test Docker access
ssh root@your-vps-ip docker ps

Webhook delivery failures after deploy

  • Check reverse proxy configuration
  • Verify TLS certificate is valid
  • Test webhook URL accessibility from external services
  • Check al env logs production for gateway errors

Out of disk space

  • Clean up old Docker images: docker system prune -a
  • Rotate logs: configure systemd journal limits
  • Monitor disk usage: df -h