Secrets Reference

Two secret files are required. Neither is ever committed to git.

secrets/inputs.sh — Infrastructure config

Sourced by the Makefile at parse time (and re-exported for Terraform). Contains deployment-level config, not runtime secrets.

1
2
cp secrets/inputs.example.sh secrets/inputs.sh
vim secrets/inputs.sh
VariableRequiredDescription
HCLOUD_TOKENYesHetzner Cloud API token — console.hetzner.cloud → Security → API Tokens
SSH_KEY_FINGERPRINTYesFingerprint of your SSH key uploaded to Hetzner — console → Security → SSH Keys
SERVER_IPTailscale onlySet to openclaw-prod (MagicDNS) after locking down SSH. Leave unset to auto-detect from Terraform output.
TAILSCALE_AUTH_KEYTailscale onlyReusable pre-authorized key from login.tailscale.com/admin/settings/keys. Must be set before make bootstrap.
SSH_KEYNoPath to SSH private key. Defaults to ~/.ssh/id_rsa.

The Makefile auto-sources inputs.sh for Terraform vars. For SSH-based targets (make ssh, make deploy, etc.), SERVER_IP must resolve — either auto-detected from Terraform output or explicitly set.

secrets/.env — Container runtime secrets

Deployed to the VPS by Ansible and loaded by Docker Compose. Contains API keys and service tokens.

1
2
cp secrets/.env.example secrets/.env
vim secrets/.env
VariableRequiredDescription
OPENCLAW_GATEWAY_TOKENYesToken for accessing the OpenClaw gateway API. Generate with openssl rand -hex 32.
ANTHROPIC_API_KEYYes*Anthropic API key. Leave empty if using subscription auth (make setup-auth).
TELEGRAM_BOT_TOKENYesTelegram bot token from @BotFather. Use a dummy value if not using Telegram.
TELEGRAM_CHAT_IDNoYour Telegram user/chat ID.
OPENROUTER_API_KEYNoOpenRouter API key for alternative model providers.
BRAVE_API_KEYNoBrave Search API key for web search skill.
GH_TOKENNoGitHub personal access token for GitHub skill.

* Required unless using make setup-auth for Claude subscription auth.

How secrets reach the VPS

Ansible (called by make deploy / make bootstrap) uses one of two paths:

  1. secrets/.env exists locally → copied directly to ~/openclaw/.env on the VPS. Simplest for local-only workflows.
  2. secrets/.env.enc + secrets/age-key.txt exist → both are pushed to the VPS, SOPS decrypts .env.enc to .env there. Used when the plaintext .env should never leave your machine.

If neither exists, Ansible warns and containers start without updated secrets.

SOPS Encryption (optional for local, required for CI)

SOPS encrypts secrets/.env at rest using an age key. The encrypted file (secrets/.env.enc) is safe to commit.

1
2
3
4
5
6
# One-time setup
make secrets-generate-key    # generates secrets/age-key.txt + prints public key
# Paste the public key into .sops.yaml (shown after generation)

make secrets-encrypt         # secrets/.env → secrets/.env.enc
git add secrets/.env.enc .sops.yaml

Day-to-day editing:

1
2
3
4
5
make secrets-edit            # opens $EDITOR with decrypted content, re-encrypts on save
# or manually:
make secrets-decrypt         # → secrets/.env
vim secrets/.env
make secrets-encrypt         # → secrets/.env.enc

SOPS encryption is required if you use the GitOps auto-deploy workflow — the CI workflow only handles .env.enc, never a plaintext .env.

CI Secrets

The GitOps auto-deploy workflow needs additional GitHub repository secrets beyond what Terraform CI uses. See GitOps auto-deploy for the full list.