keyrotate

Server install

Last reviewed:

The main install paths (npx, Homebrew, npm -g) assume you're on a desktop. This page covers the other cases: running keyrotate on a Linux server / VPS / bastion / CI runner / Docker, on Windows, and how to authenticate to your vault without biometrics.

Linux server (headless VPS / bastion / CI)

The smallest install — drop a single static binary on the box, sign in once.

# 1. Install the binary (single file, no Node/Bun runtime required)
curl -fsSL https://raw.githubusercontent.com/Prompto-Studio/keyrotate/main/scripts/install.sh | bash
# puts keyrotate (and `kr` alias) at ~/bin/

# 2. Install destination CLIs you need (apt example)
sudo apt update && sudo apt install -y unzip
# GitHub CLI
type -p curl >/dev/null || sudo apt install -y curl
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list
sudo apt update && sudo apt install -y gh

# Bitwarden CLI (recommended vault for headless boxes — see below)
npm install -g @bitwarden/cli   # requires Node 18+

Headless vault auth: pick Bitwarden, not 1Password

1Password's op CLI requires biometric unlock by default — fine on a Mac, painful on a headless server. The cleanest headless story is Bitwarden, whose bw CLI supports a fully non-interactive API-key login that you can drop into systemd or a Docker entrypoint.

# 1. Generate an API key in Bitwarden web vault → Settings → My Account → API Key
# 2. Export the credentials
export BW_CLIENTID="user.xxxxxx"
export BW_CLIENTSECRET="xxxxx"
export BW_PASSWORD="your-master-password"

# 3. Authenticate and unlock — no prompts
bw login --apikey
export BW_SESSION="$(bw unlock --passwordenv BW_PASSWORD --raw)"

# 4. keyrotate can now write to Bitwarden without further input
keyrotate doctor      # confirms `bw` reachable + unlocked
keyrotate rotate openai

Store BW_CLIENTID / BW_CLIENTSECRET / BW_PASSWORD in /etc/keyrotate.env with mode 0600, or in your CI's secret store. The BW_SESSION token expires; either re-unlock per cron run or pin a long-lived session in /run/keyrotate/session with a systemd unit that refreshes it.

Running rotations from a cron job

# /etc/cron.d/keyrotate-weekly  — Sunday 03:00, rotate the Resend key
0 3 * * 0  steve  /home/steve/bin/keyrotate rotate resend-alerts >> /var/log/keyrotate.log 2>&1

For interactive providers (anything where the rotate flow prompts for a value), the cron version isn't going to ask you for a key — you'd need v00.00.17's auto-rotation feature (create() capability) which generates the new key over the provider's API. That ships in the next release.

Docker

A minimal image suitable for one-shot rotations or a long-running scheduled container:

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
      curl ca-certificates gnupg nodejs npm \
    && rm -rf /var/lib/apt/lists/*
RUN curl -fsSL https://raw.githubusercontent.com/Prompto-Studio/keyrotate/main/scripts/install.sh | bash
RUN npm install -g @bitwarden/cli
ENV PATH="/root/bin:${PATH}"
WORKDIR /app
COPY keyrotate.toml ./
ENTRYPOINT ["keyrotate"]
CMD ["doctor"]

Run with vault creds injected from the host's secret store:

docker run --rm \
  -e BW_CLIENTID -e BW_CLIENTSECRET -e BW_PASSWORD \
  -v $(pwd)/keyrotate.toml:/app/keyrotate.toml:ro \
  yourorg/keyrotate \
  rotate resend-alerts

Windows

keyrotate ships a Windows x64 binary. Install via npm or the curl-equivalent PowerShell flow:

# 1. Install Node 18+ from https://nodejs.org/ or via winget
winget install OpenJS.NodeJS.LTS

# 2. Install keyrotate
npm install -g keyrotate

# 3. Install destination CLIs you need
winget install 1Password.CLI       # `op` — interactive vault
winget install GitHub.CLI          # `gh`
winget install Supabase.CLI
winget install Netlify.Netlify
winget install Fly-io.flyctl

# 4. Sign each one in
op signin
gh auth login
supabase login
netlify login
flyctl auth login

# 5. Verify keyrotate sees them all
keyrotate doctor

.env files on Windows

The envfile destination uses Node's path.resolve(), which is fully Windows-aware. .env files work identically on Windows — same format, same semantics. Point at either a relative path (.env.local) or an absolute one (C:\\Users\\you\\project\\.env).

If you'd rather set machine-wide environment variables instead of a .env file (rare in modern dev workflows but supported by Windows):

# PowerShell — set a user-scoped persistent env var
[Environment]::SetEnvironmentVariable("OPENAI_API_KEY", "sk-…", "User")

This isn't currently a destination plugin — most projects expect a .env file. If you have a strong use case for system-env destinations, open a Discussion and we'll consider it.

Cron equivalent on Windows: Task Scheduler

schtasks /create /tn "keyrotate-daily" `
  /tr "C:\Users\you\AppData\Roaming\npm\keyrotate.cmd doctor --check-rotations" `
  /sc daily /st 09:00

Importing existing keys (any OS)

The most common server-install scenario isn't "I'm rotating" — it's "I already have keys in .env and I want to bring them under management without making new ones." For that:

# 1. Scan everywhere keyrotate can read for existing keys
keyrotate discover

# 2. For each provider you found, add a rotation in keyrotate.toml
#    (either via `keyrotate setup` interactively, or by editing the file directly)

# 3. Import the existing key — it's verified against the provider, then
#    written to all destinations (1Password / Bitwarden / GitHub / .env / etc).
#    Nothing is rotated; nothing is revoked.
keyrotate import openai
keyrotate import resend-alerts
keyrotate import stripe

From that point on, all subsequent rotations go through keyrotate. The audit log records the import as the first event in each key's history.

Troubleshooting